Kotlin의 클래스 및 객체 인스턴스

1. 시작하기 전에

이 과정의 Codelab에서는 Dice Roller Android 앱을 빌드합니다. 이 앱에서는 사용자가 '주사위를 굴리면' 결과가 임의로 생성됩니다. 결과는 주사위의 면 수를 고려합니다. 예를 들어 6면 주사위로는 1~6의 값만 굴릴 수 있습니다.

최종 앱은 다음과 같이 표시됩니다.

8299aaca25c93863.png

이 앱의 새 프로그래밍 개념에 집중할 수 있도록 브라우저 기반 Kotlin 프로그래밍 도구를 사용하여 핵심 앱 기능을 만듭니다. 프로그램은 결과를 콘솔에 출력합니다. 나중에 Android 스튜디오에서 사용자 인터페이스를 구현합니다.

이 첫 번째 Codelab에서는 실제 주사위처럼 주사위 굴리기를 시뮬레이션하여 랜덤 숫자를 출력하는 Kotlin 프로그램을 만듭니다.

기본 요건

  • https://developer.android.com/training/kotlinplayground에서 코드를 열어 수정하고 실행하는 방법을 이해하고 있습니다.
  • 변수와 함수를 사용하고 결과를 콘솔에 출력하는 Kotlin 프로그램을 만들고 실행할 수 있습니다.
  • ${variable} 표기법으로 문자열 템플릿을 사용하여 텍스트 내의 숫자 형식을 지정할 수 있습니다.

학습할 내용

  • 프로그래매틱 방식으로 랜덤 숫자를 생성하여 주사위 굴리기를 시뮬레이션하는 방법
  • 변수와 메서드로 Dice 클래스를 만들어 코드를 구조화하는 방법
  • 클래스의 객체 인스턴스를 만들고 변수를 수정하며 메서드를 호출하는 방법

빌드할 항목

  • 브라우저 기반 Kotlin 프로그래밍 도구에서 랜덤 주사위 굴리기를 실행할 수 있는 Kotlin 프로그램

필요한 항목

  • 인터넷이 연결된 컴퓨터

2. 랜덤 숫자 굴리기

게임에는 임의의 요소가 있는 경우가 많습니다. 임의의 상품을 받거나 게임 보드에서 임의 개수의 단계를 전진할 수 있습니다. 일상에서는 임의의 숫자와 문자를 사용하여 더 안전한 비밀번호를 생성할 수 있습니다.

실제 주사위를 굴리는 대신 주사위 굴리기를 시뮬레이션하는 프로그램을 작성할 수 있습니다. 주사위를 굴릴 때마다 가능한 값 범위 내의 임의 숫자가 결과로 나올 수 있습니다. 다행히 이러한 프로그램을 위해 자체 랜덤 숫자 생성기를 빌드하지 않아도 됩니다. Kotlin을 비롯한 대다수 프로그래밍 언어는 랜덤 숫자를 생성하는 방식을 기본적으로 갖추고 있습니다. 여기서는 Kotlin 코드를 사용하여 랜덤 숫자를 생성합니다.

시작 코드 설정

  1. 브라우저에서 https://developer.android.com/training/kotlinplayground 웹사이트를 엽니다.
  2. 코드 편집기에서 기존 코드를 모두 삭제하고 아래의 코드로 바꿉니다. 이전 Codelab에서 작업한 main() 함수입니다.
fun main() {

}

랜덤 함수 사용

주사위를 굴리려면 유효한 주사위 굴리기 값을 모두 나타낼 방법이 있어야 합니다. 일반적인 6면 주사위의 경우 허용되는 주사위 굴리기 값은 1, 2, 3, 4, 5, 6입니다.

이전 과정에서 정수의 경우 Int, 텍스트의 경우 String과 같은 데이터 유형이 있음을 살펴본 바 있습니다. IntRange는 또 다른 데이터 유형으로, 시작점부터 끝점까지 정수의 범위를 나타냅니다. IntRange는 주사위를 굴려 생성할 수 있는 값을 나타내는 적절한 데이터 유형입니다.

  1. main() 함수 내에서 변수를 diceRange라는 val로 정의합니다. 1에서 6까지의 IntRange에 할당하여 6면 주사위로 굴릴 수 있는 정수의 범위를 나타냅니다.
val diceRange = 1..6

1..6은 Kotlin 범위인 것을 알 수 있습니다. 시작 숫자, 점 두 개, 끝 숫자가 공백 없이 순서대로 놓여 있기 때문입니다. 정수 범위의 다른 예로는 숫자 2~5의 2..5, 숫자 100~200의 100..200이 있습니다.

println()을 호출하면 시스템이 지정된 텍스트를 출력하는 것과 유사하게 random()이라는 함수를 사용하여 주어진 범위의 랜덤 숫자를 생성하고 반환할 수 있습니다. 앞에서와 같이 결과를 변수에 저장할 수 있습니다.

  1. main() 내에서 변수를 randomNumber라는 val로 정의합니다.
  2. 아래와 같이 randomNumberdiceRange 범위에서 random()을 호출한 결과 값을 갖도록 합니다.
 val randomNumber = diceRange.random()

변수 및 함수 호출 사이에 마침표 또는 점을 사용하여 diceRange에서 random()을 호출합니다. 이를 'diceRange에서 랜덤 숫자를 생성하는 중'이라고 해석할 수 있습니다. 결과는 randomNumber 변수에 저장됩니다.

  1. 랜덤으로 생성된 숫자를 확인하려면 문자열 형식 표기법('문자열 템플릿'이라고도 함) ${randomNumber}를 사용하여 아래와 같이 출력합니다.
println("Random number: ${randomNumber}")

완성된 코드는 다음과 같이 표시됩니다.

fun main() {
    val diceRange = 1..6
    val randomNumber = diceRange.random()
    println("Random number: ${randomNumber}")
}
  1. 코드를 여러 번 실행합니다. 그러면 아래와 같이 매번 다른 랜덤 숫자로 출력됩니다.
Random number: 4

3. Dice 클래스 만들기

주사위를 굴릴 때 주사위는 손 안에 있는 실제 물건입니다. 그러나 방금 작성한 코드가 완벽하게 작동하더라도 실제 주사위라고 생각하기는 어렵습니다. 구현해 내는 것과 더 비슷하도록 프로그램을 구성하면 이를 이해하기가 더 쉬워집니다. 따라서 굴릴 수 있는 주사위를 프로그래매틱 방식으로 만드는 것이 좋습니다.

모든 주사위는 기본적으로 동일하게 작동합니다. 속성(예: 면)이 동일하고 동작(예: 굴릴 수 있음)도 동일합니다. Kotlin에서는 주사위에 면이 있고 랜덤 숫자를 굴릴 수 있다고 표시하는 프로그래매틱 방식의 주사위 청사진을 만들 수 있습니다. 이 청사진을 클래스라고 합니다.

이 클래스에서 객체 인스턴스라는 실제 주사위 객체를 만들 수 있습니다. 예를 들어 12면 주사위나 4면 주사위를 만들 수 있습니다.

Dice 클래스 정의

다음 단계에서는 Dice라는 새 클래스를 정의하여 굴릴 수 있는 주사위를 나타냅니다.

  1. 새로 시작하려면 main() 함수에서 코드를 지워서 아래와 같은 코드를 만듭니다.
fun main() {

}
  1. main() 함수 아래에 빈 줄을 추가하고 코드를 추가하여 Dice 클래스를 만듭니다. 아래와 같이 키워드 class, 클래스 이름, 여는 중괄호, 닫는 중괄호 순으로 작성합니다. 중괄호 사이에 공백을 두어 클래스의 코드를 넣습니다.
class Dice {

}

클래스 정의 내에서 변수를 사용하여 클래스의 속성을 하나 이상 지정할 수 있습니다. 실제 주사위는 면과 색상, 무게가 다양할 수 있습니다. 이 작업에서는 주사위의 면 수에 관한 속성에 중점을 둡니다.

  1. Dice 클래스 내에서 주사위 면 수를 위한 sides라는 var를 추가합니다. sides를 6으로 설정합니다.
class Dice {
    var sides = 6
}

이상입니다. 이제 주사위를 나타내는 매우 간단한 클래스가 생겼습니다.

Dice 클래스의 인스턴스 만들기

Dice 클래스로 주사위 개념에 관한 청사진을 보유하게 됩니다. 프로그램에 실제 주사위를 포함하려면 Dice 객체 인스턴스를 만들어야 합니다. 주사위가 3개 있어야 하면 객체 인스턴스를 3개 만듭니다.

ba2038022410942c.jpeg

  1. Dice의 객체 인스턴스를 만들려면 main() 함수에서 myFirstDice라는 val을 만들어 Dice 클래스의 인스턴스로 초기화합니다. 클래스 이름 뒤에 있는 괄호는 클래스에서 새 객체 인스턴스를 만들고 있음을 나타냅니다.
fun main() {
    val myFirstDice = Dice()
}

이제 청사진으로 만든 myFirstDice 객체가 생겼으니 이 객체의 속성에 액세스할 수 있습니다. Dice의 유일한 속성은 sides입니다. '점 표기법'을 사용하여 속성에 액세스합니다. 따라서 myFirstDicesides 속성에 액세스하려면 'myFirstDicesides'로 발음되는 myFirstDice.sides를 호출합니다.

  1. myFirstDice 선언 아래에서 println() 문을 추가하여 myFirstDice.sides 수를 출력합니다.
println(myFirstDice.sides)

코드는 다음과 같이 표시됩니다.

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
}

class Dice {
    var sides = 6
}
  1. 프로그램을 실행하면 Dice 클래스에 정의된 sides의 수가 출력됩니다.
6

이제 Dice 클래스와 sides가 6인 실제 주사위 myFirstDice가 있습니다.

주사위를 굴려봅시다.

주사위 굴리기

이전에는 함수를 사용하여 케이크 층을 출력하는 작업을 실행했습니다. 주사위 굴리기도 함수로 구현할 수 있는 작업입니다. 모든 주사위를 굴릴 수 있으므로 Dice 클래스 내에서 함수를 추가하면 됩니다. 클래스 내에서 정의된 함수를 메서드라고도 합니다.

  1. Dice 클래스의 sides 변수 아래에서 빈 줄을 삽입하고 주사위를 굴리는 새 함수를 만듭니다. Kotlin 키워드 fun, 메서드 이름, 괄호 (), 여는 중괄호, 닫는 중괄호 {} 순으로 작성합니다. 아래와 같이 중괄호 사이에 빈 줄을 두어 코드를 추가할 공간을 만듭니다. 클래스는 다음과 같이 표시됩니다.
class Dice {
    var sides = 6

    fun roll() {

    }
}

6면 주사위를 굴리면 1에서 6 사이의 랜덤 숫자가 생성됩니다.

  1. roll() 메서드 내에서 val randomNumber를 만듭니다. 1..6 범위에서 랜덤 숫자를 할당합니다. 점 표기법을 사용하여 범위에서 random()을 호출합니다.
val randomNumber = (1..6).random()
  1. 랜덤 숫자를 생성한 후 콘솔에 출력합니다. 완성된 roll() 메서드는 아래의 코드와 같이 표시됩니다.
fun roll() {
     val randomNumber = (1..6).random()
     println(randomNumber)
}
  1. 실제로 myFirstDice를 굴리려면 main()에서 myFirstDiceroll() 메서드를 호출합니다. '점 표기법'을 사용하여 메서드를 호출하세요. 따라서 myFirstDiceroll() 메서드를 호출하려면 'myFirstDiceroll()'로 발음되는 myFirstDice.roll()을 입력합니다.
myFirstDice.roll()

완성된 코드는 다음과 같이 표시됩니다.

fun main() {
    val myFirstDice = Dice()
    println(myFirstDice.sides)
    myFirstDice.roll()
}

class Dice {
    var sides = 6

    fun roll() {
        val randomNumber = (1..6).random()
        println(randomNumber)
    }
}
  1. 코드를 실행합니다. 면 수 아래에 랜덤 주사위 굴리기의 결과가 표시됩니다. 코드를 여러 번 실행하면 면 수가 동일하게 유지되고 주사위 굴리기 값이 변경됩니다.
6
4

축하합니다. sides 변수와 roll() 함수를 사용하여 Dice 클래스를 정의했습니다. main() 함수에서 새로운 Dice 객체 인스턴스를 만든 후 roll() 메서드를 호출하여 랜덤 숫자를 생성했습니다.

4. 주사위 굴리기 값 반환

현재 roll() 함수에서 randomNumber 값을 출력하고 있고 잘 작동합니다. 그러나 함수 결과를 어떤 함수에라도 반환하는 것이 더 유용할 수 있습니다. 예를 들어 roll() 메서드의 결과를 변수에 할당하고 플레이어를 그 수 만큼 이동할 수 있습니다. 이 과정을 확인해 보겠습니다.

  1. main()에서 myFirstDice.roll()이라는 줄을 수정합니다. diceRoll이라는 val을 만듭니다. roll() 메서드에서 반환하는 값과 동일하게 설정합니다.
val diceRoll = myFirstDice.roll()

roll()이 아무것도 반환하지 않기 때문에 어떤 것도 실행되지 않습니다. 이 코드가 의도대로 작동하려면 roll()에서 무언가를 반환해야 합니다.

이전 Codelab에서는 함수의 입력 인수를 위한 데이터 유형을 지정해야 한다는 것을 알아봤습니다. 같은 방식으로 함수가 반환하는 데이터의 데이터 유형을 지정해야 합니다.

  1. roll() 함수를 변경하여 반환할 데이터 유형을 지정합니다. 이 경우 랜덤 숫자는 Int이므로 반환 유형은 Int입니다. 반환 유형을 지정하는 구문은 다음과 같습니다. 함수 이름, 괄호, 콜론, 공백, 함수 반환 유형의 Int 키워드 순으로 작성합니다. 함수 정의는 아래 코드와 같이 표시됩니다.
fun roll(): Int {
  1. 코드를 실행합니다. Problems View에 오류가 표시됩니다. 오류는 다음과 같습니다.
A ‘return' expression required in a function with a block body (‘{...}')

Int를 반환하도록 함수 정의를 변경했지만 시스템에서 코드가 실제로 Int를 반환하지 않는다고 불만을 제기합니다. '블록 본문' 또는 '함수 본문'은 함수의 중괄호 사이에 있는 코드를 나타냅니다. 함수 본문 끝의 return 문을 사용하여 함수에서 값을 반환함으로써 이 오류를 해결할 수 있습니다.

  1. roll()에서 println() 문을 삭제하고 randomNumberreturn 문으로 바꿉니다. roll() 함수는 아래 코드와 같이 표시됩니다.
fun roll(): Int {
     val randomNumber = (1..6).random()
     return randomNumber
}
  1. main()에서 주사위 면을 위한 print 문을 삭제합니다.
  2. 정보를 제공하는 문장에 sidesdiceRoll의 값을 출력하는 문을 추가합니다. 완성된 main() 함수는 아래 코드와 같이 표시됩니다.
fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}
  1. 코드를 실행하면 다음과 같은 결과가 출력됩니다.
Your 6 sided dice rolled 4!

다음은 지금까지 작성한 모든 코드입니다.

fun main() {
    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..6).random()
        return randomNumber
    }
}

5. 주사위의 면 수 변경

모든 주사위가 6면은 아닙니다. 주사위는 모양도 면도 여러 가지입니다. 4면, 8면, 최대 120면 주사위도 있습니다.

  1. Dice 클래스의 roll() 메서드에서 하드 코딩 1..6을 대신 sides를 사용하도록 변경하여 범위, 따라서 굴리는 랜덤 숫자가 항상 면 수에 맞도록 합니다.
val randomNumber = (1..sides).random()
  1. main() 함수에서 주사위 굴리기를 출력한 후 아래에 myFirstDicesides를 20으로 설정하도록 변경합니다.
myFirstDice.sides = 20
  1. 면 수를 변경한 다음 아래에 기존 출력 문을 복사하여 붙여넣습니다.
  2. diceRoll의 출력을 myFirstDiceroll() 메서드 호출 결과의 출력으로 바꿉니다.
println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")

프로그램은 다음과 같이 표시됩니다.

fun main() {

    val myFirstDice = Dice()
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.sides} sided dice rolled ${diceRoll}!")

    myFirstDice.sides = 20
    println("Your ${myFirstDice.sides} sided dice rolled ${myFirstDice.roll()}!")
}

class Dice {
    var sides = 6

    fun roll(): Int {
        val randomNumber = (1..sides).random()
        return randomNumber
    }
}
  1. 프로그램을 실행하면 6면 주사위 메시지와 20면 주사위의 두 번째 메시지가 표시됩니다.
Your 6 sided dice rolled 3!
Your 20 sided dice rolled 15!

6. 주사위 맞춤설정

클래스 개념은 무언가를 나타내는 것으로, 실제 물건을 나타내는 경우가 많습니다. 이 경우 Dice 클래스는 실제 주사위를 나타냅니다. 실제로 주사위는 면 수를 변경할 수 없습니다. 다른 면 수를 원한다면 다른 주사위를 구매해야 합니다. 프로그래매틱 방식으로는 기존 Dice 객체 인스턴스의 sides 속성을 변경하는 대신 원하는 면 수로 새 주사위 객체 인스턴스를 만들어야 한다는 의미입니다.

이 작업에서는 새 인스턴스를 만들 때 면 수를 지정할 수 있도록 Dice 클래스를 수정합니다. Dice 클래스 정의를 변경하여 면 수를 제공할 수 있습니다. 이는 함수가 입력의 인수를 허용하는 방법과 비슷합니다.

  1. Dice 클래스 정의를 수정하여 numSides라는 정수를 허용합니다. 클래스 내 코드는 변경되지 않습니다.
class Dice(val numSides: Int) {
   // Code inside does not change.
}
  1. 이제 numSides를 사용할 수 있으므로 Dice 클래스 내에서 sides 변수를 삭제합니다.
  2. 또한 numSides를 사용하도록 범위를 수정합니다.

Dice 클래스는 다음과 같이 표시됩니다.

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}

이 코드를 실행하면 오류가 많이 표시됩니다. Dice 클래스의 변경사항과 호환되도록 main()을 업데이트해야 하기 때문입니다.

  1. main()에서 면이 6개인 myFirstDice를 만들려면 아래와 같이 이제 면 수를 Dice 클래스의 인수로 제공해야 합니다.
    val myFirstDice = Dice(6)
  1. print 문에서 sidesnumSides로 변경합니다.
  2. 그 아래에서 sides를 20으로 변경하는 코드를 삭제합니다. 이 변수는 더 이상 없기 때문입니다.
  3. 그 아래 println 문도 삭제합니다.

main() 함수는 아래의 코드와 같이 표시됩니다. 실행하면 오류가 발생하지 않습니다.

fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")
}
  1. 첫 번째 주사위 굴리기를 출력한 후 코드를 추가하여 면이 20개인 mySecondDice라는 두 번째 Dice 객체를 만들어 출력합니다.
val mySecondDice = Dice(20)
  1. 반환된 값을 굴리고 출력하는 print 문을 추가합니다.
println("Your ${mySecondDice.numSides} sided dice rolled  ${mySecondDice.roll()}!")
  1. 완성된 main() 함수는 다음과 같습니다.
fun main() {
    val myFirstDice = Dice(6)
    val diceRoll = myFirstDice.roll()
    println("Your ${myFirstDice.numSides} sided dice rolled ${diceRoll}!")

    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        val randomNumber = (1..numSides).random()
        return randomNumber
    }
}
  1. 완성된 프로그램을 실행하면 다음과 같은 결과가 출력됩니다.
Your 6 sided dice rolled 5!
Your 20 sided dice rolled 7!

7. 적절한 코딩 사례 채택

코드는 간결하게 작성하는 것이 좋습니다. randomNumber 변수를 삭제하고 랜덤 숫자를 직접 반환해도 됩니다.

  1. return 문을 변경하여 랜덤 숫자를 직접 반환합니다.
fun roll(): Int {
    return (1..numSides).random()
}

두 번째 print 문에서 랜덤 숫자를 가져오는 호출을 문자열 템플릿에 배치합니다. 첫 번째 print 문에서 동일한 작업을 실행하여 diceRoll 변수를 삭제할 수 있습니다.

  1. 문자열 템플릿에서 myFirstDice.roll()을 호출하고 diceRoll 변수를 삭제합니다. 이제 main() 코드의 처음 두 줄이 다음과 같이 표시됩니다.
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
  1. 코드를 실행하면 출력에 아무 차이가 없습니다.

다음은 리팩터링 후 최종 코드입니다.

fun main() {
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")

    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}

8. 솔루션 코드

fun main() {
    val myFirstDice = Dice(6)
    println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")

    val mySecondDice = Dice(20)
    println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}

class Dice (val numSides: Int) {

    fun roll(): Int {
        return (1..numSides).random()
    }
}

9. 요약

  • IntRange에서 random() 함수를 호출하여 랜덤 숫자를 생성합니다. (1..6).random()
  • 클래스는 객체의 청사진과 같습니다. 변수와 함수로 구현된 속성과 동작을 포함할 수 있습니다.
  • 클래스 인스턴스는 주사위와 같은 실제 객체를 나타내는 경우가 많습니다. 객체에서 작업을 호출하고 속성을 변경할 수 있습니다.
  • 인스턴스를 만들 때 값을 클래스에 제공할 수 있습니다. 예를 들어 class Dice(val numSides: Int) 다음에 Dice(6)로 인스턴스를 만듭니다.
  • 함수에서 무언가를 반환할 수 있습니다. 함수 정의에서 반환할 데이터 유형을 지정하고 함수 본문에서 return 문을 사용하여 무언가를 반환합니다. 예: fun example(): Int { return 5 }

10. 자세히 알아보기

11. 연습하기

다음을 따르세요.

  • Dice 클래스에 또 다른 색상 속성을 제공하고 면과 색상이 다양한 주사위 인스턴스를 여러 개 만듭니다.
  • Coin 클래스를 만들고 뒤집기 기능을 부여한 후 클래스 인스턴스를 만들어 동전을 던져 봅니다. 범위가 있는 random() 함수를 사용하여 동전 던지기를 달성하는 방법은 무엇인가요?