Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20241203 Lotto 기능 구현하기 본문

Kotlin

[TIL]20241203 Lotto 기능 구현하기

최밤빵 2024. 12. 3. 03:16

3주차 과제도 시작하기 전 여러 고민이 있었다. 코틀린의 문법에 익숙하지 않으니까 클래스 구조나 예외 처리 방법에 대해 어떻게 접근해야 할지 고민했고, 자바와는 다른 코틀린의 data classcompanion object 같은 개념들을 이론만 알지 적용하는건 어려웠다. 그리고 예외 상황을 어떻게 깔끔하게 처리할지에 대한 고민까지.... 과제를 해결하기 위해 코틀린 공식 문서를 참고하고, 여러 예제 코드를 살펴보면서 문법에 대한 이해를 넓혀 나갔다. 초기에는 간단한 기능부터 구현해 보며 자신감을 쌓고, 이후 단계적으로 복잡한 로직을 추가해 나가면서 문제를 해결할 수 있었다....!

 

과제 요구사항 분석

이번 과제는 사용자가 로또 구입 금액을 입력하고, 로또를 구매하며, 당첨 번호를 입력받아 당첨 통계를 계산하는 프로그램을 구현한다. 주요 기능으로는 로또 구입 금액 입력, 구입 금액에 따른 로또 발행, 당첨 번호와 보너스 번호 입력 및 검증, 그리고 당첨 결과 계산 후 통계 출력을 한다. 이 과정을 통해 예외 처리, 컬렉션, 무한 반복문 등 다양한 기능들을 활용할 수 있었다.


구현된 기능 및 코드 분석

1. 로또 구입 금액 입력 및 검증 기능

fun inputPayment(): Int {
    println("구입금액을 입력해 주세요.")
    val payment = readLine()?.toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 구입금액은 숫자로 입력해야 합니다.")

    require(payment > 0 && payment % 1000 == 0 && payment <= 100000) {
        "[ERROR] 구입금액은 1000원 단위로 10만원 이하의 금액만 입력해야 합니다."
    }
    return payment
}

→ 사용자로부터 로또 구입 금액을 입력받고, 1000원 단위로 나누어 떨어지는지, 최대 10만 원 이하인지를 검증하고 잘못된 입력이 있을 경우 예외 메시지를 출력하도록 구현했다. 초입자라서 입력 검증에서 여러 조건들을 require를 통해 한 번에 처리하는 것이 좋았다.


2. 로또 발행 기능

fun issueLottos(payment: Int): List<List<Int>> {
    val numberOfLottos = payment / 1000
    val lottos = mutableListOf<List<Int>>()

    repeat(numberOfLottos) {
        lottos.add(Randoms.pickUniqueNumbersInRange(1, 45, 6).sorted())
    }

    println("$numberOfLottos개를 구매했습니다.")
    lottos.forEach { println(it) }
    return lottos
}

→ 구입 금액에 따라 로또의 수량을 결정하고, 각 로또마다 랜덤하게 숫자 6개를 발행하도록 했다. 로또 번호는 중복 없이 1~45 사이에서 선택되고, 발행된 로또 번호를 오름차순으로 정렬했다.


3. 당첨 번호 및 보너스 번호 입력 기능

fun inputWinningNumbers(): List<Int> {
    println("당첨 번호를 입력해 주세요.")
    val input = readLine() ?: throw IllegalArgumentException("[ERROR] 입력값을 확인할 수 없습니다.")
    val numbers = input.split(",").map { it.trim().toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 숫자만 입력해야 합니다.") }

    require(numbers.size == 6 && numbers.distinct().size == 6 && numbers.all { it in 1..45 }) {
        "[ERROR] 당첨 번호는 중복되지 않는 1부터 45 사이의 숫자 6개여야 합니다."
    }
    return numbers
}

fun inputBonusNumber(winningNumbers: List<Int>): Int {
    println("보너스 번호를 입력해 주세요.")
    val bonus = readLine()?.toIntOrNull() ?: throw IllegalArgumentException("[ERROR] 보너스 번호는 숫자로 입력해야 합니다.")

    require(bonus in 1..45 && bonus !in winningNumbers) {
        "[ERROR] 보너스 번호는 당첨 번호와 중복되지 않는 1부터 45 사이의 숫자여야 합니다."
    }
    return bonus
}

→ 당첨 번호는 쉼표로 구분된 6개의 숫자를 입력받고, 각 숫자가 1~45 사이인지, 중복이 없는지를 확인했다. 보너스 번호는 당첨 번호와 중복되지 않는지 검증하도록 구현했다.


4. 당첨 결과 계산 및 출력 기능

fun calculateWinningStatistics(lottos: List<List<Int>>, winningNumbers: List<Int>, bonusNumber: Int): Map<Int, Int> {
    val winningCount = mutableMapOf(3 to 0, 4 to 0, 5 to 0, 6 to 0, 7 to 0)

    lottos.forEach { lotto ->
        val matchedCount = lotto.count { it in winningNumbers }
        if (matchedCount == 5 && lotto.contains(bonusNumber)) {
            winningCount[7] = winningCount[7]!! + 1  // 2등
        } else if (matchedCount >= 3) {
            winningCount[matchedCount] = winningCount[matchedCount]!! + 1
        }
    }
    return winningCount
}

→ 로또 번호와 당첨 번호를 비교하여 당첨 결과를 계산했다. 1등부터 5등까지의 결과를 저장하고,  5개의 번호와 보너스 번호가 일치하면 2등으로 처리하도록 했다. 


개선한 부분 및 보완한 코드

  • 입력 검증 강화: 사용자가 입력하는 금액, 당첨 번호, 보너스 번호의 검증을 더욱 강화했다. 처음 구현한 코드에서는 잘못된 입력이 발생했을 때 다시 입력받는 로직이 없었다. 그 부분을 보완해서 재입력 기능을 추가했다.
    fun inputWithRetry(prompt: String, validator: (String) -> Boolean): String {
        while (true) {
            println(prompt)
            val input = readLine() ?: continue
            if (validator(input)) {
                return input
            } else {
                println("[ERROR] 잘못된 입력입니다. 다시 시도해 주세요.")
            }
        }
    }
    → inputWithRetry 함수를 추가해서, 잘못된 입력이 들어오면 다시 시도하게 하고, 이 함수는 여러 입력 검증 단계에서 재사용이 가능하도록 했다. 

  • 테스트 코드 추가: 주요 기능에 대해 테스트 코드를 작성했다. 로또 번호 생성, 당첨 번호 및 보너스 번호 입력 검증, 당첨 결과 계산에 대한 테스트를 추가하고, 코드의 안정성을 높였다.
@Test
fun `당첨 번호 입력 검증 테스트`() {
val input = "1, 2, 3, 4, 5, 6"
val numbers = input.split(",").map { it.trim().toInt() }
assertEquals(6, numbers.size)
assertTrue(numbers.all { it in 1..45 })
        }

@Test
fun `로또 발행 개수 검증 테스트`() {
val payment = 5000
val numberOfLottos = payment / 1000
assertEquals(5, numberOfLottos)
}

→ 간단한 테스트 코드를 통해서 입력과 출력이 예상대로 동작하는지 검증하고, 예외 상황에서도 코드가 안정적으로 동작함을 확인할 수 있었다.


이번 과제를 하면서 코틀린의 기본적인 문법과 예외 처리에 대해 더 이해할 수 있었다. 특히 로또 발행 과정에서의 랜덤한 숫자 생성, 입력값 검증, 예외 처리 등의 기능을 구현하면서 코틀린의 표준 라이브러리를 사용하며 코드를 작성하는 방법을 익혀갔다. 또한, 개선 과정을 통해 코드의 재사용성과 안정성을 높이면서, 사용자 경험을 개선하는 방향으로 발전시켰다. 3주차 과제를 하면서도 코틀린 작성법이 익숙해지지가 않아서 그나마 익숙한 코드인 프로젝트에 더 집중하게 되는것같다😢