밤빵's 개발일지
[TIL]20241127 자동차 경주 게임 구현 본문
코틀린으로 자동차 경주 게임을 구현한 프리코스 2주차 과제 내용을 바탕으로 개발일지를 작성했다. 프리코스 1주차 과제 하면서 나 코틀린으로는 코테 절대못푼다😢 라는 생각이 들어서 과제만 해보자란 생각으로 꾸역꾸역 과제를 했었다. 코틀린을 배우고싶어서 시작한 거였는데 ai없이 코드를 작성하려고하니까 잘 모르기도하고, 작성법은 낯설어 죽겠고 시간이 너무 많이 소요되서 포기부터 하고 간략하게만 구현했다. 경험해본걸로 만족하고 공부 더 열심히 하는걸로..!
▶과제 요구사항 분석
2주차 과제는 사용자가 입력한 자동차 이름과 시도할 횟수에 따라 자동차 경주 게임을 진행하는 프로그램을 작성하는 것으로 주요 기능으로는 자동차 이름과 횟수를 입력받고, 경주 로직을 통해 자동차의 이동을 결정하며, 최종적으로 우승자를 결정하는 것이였다. 요구사항에 따라 여러 기능이 필요했고, 덕분에 코틀린의 다양한 문법과 기능을 공부할 수 있었다.
▶구현된 기능 및 코드 분석
1. 자동차 이름 입력 기능
fun inputCarNames(): List<String> {
println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)")
val input = readLine() ?: ""
return input.split(",").map { it.trim() }
}
→ 사용자가 자동차 이름을 입력하면 쉼표로 구분된 문자열을 리스트로 변환하였다. 이 과정에서 각 이름은 .trim()을 사용해 공백을 제거하였다. 입력 검증을 통해 잘못된 입력에 대해 유효성을 검사하는 코드가 추가될 필요가 있었다.
2. 자동차 이름 유효성 검사
fun validateCarNames(carNames: List<String>) {
if (carNames.isEmpty()) {
println("자동차 이름을 한 개 이상 입력해야 합니다.")
return
}
if (carNames.any { it.length > 5 }) {
throw IllegalArgumentException("자동차 이름은 5글자 이하만 가능합니다.")
}
}
→ 자동차 이름이 5글자 이하인지 확인하고, 제한을 초과한 경우 예외를 발생시킨다. 예외를 발생시키는건 쉽지만, 메시지를 제공하는 데 있어 조금 더 구체적인 설명이 있었으면 더 좋았을 거다. 예를 들어, 어떤 자동차 이름이 잘못되었는지를 표시하는 추가 로직 같은거..?
3. 시도할 횟수 입력 기능
fun inputAttemptCount(): Int {
println("시도할 횟수는 몇 회인가요?")
return try {
readLine()?.toInt() ?: 0
} catch (e: NumberFormatException) {
println("올바른 숫자를 입력해주세요.")
0
}
}
→ 사용자가 시도할 횟수를 입력받아 정수로 변환하는 기능을 구현했지만, 정수로 변환할 수 없는 입력이 들어왔을 때 발생할 수 있는 예외 처리가 필요했다.
4. 자동차 경주 로직 구현
fun raceCars(carNames: List<String>, attemptCount: Int): List<Int> {
val distances = MutableList(carNames.size) { 0 }
for (i in 1..attemptCount) {
carNames.indices.forEach { index ->
val randomValue = (0..9).random()
if (randomValue >= 4) {
distances[index]++
}
}
printResults(carNames, distances)
}
return distances
}
→ 주어진 횟수 동안 각 자동차가 전진하거나 멈추는 로직으로 여기서 (0..9).random()을 통해 랜덤한 값을 받아, 4 이상일 때만 자동차를 전진시켰다.
5. 결과 출력 및 우승자 결정 기능
fun determineWinners(carNames: List<String>, distances: List<Int>): List<String> {
val maxDistance = distances.maxOrNull() ?: return emptyList()
return carNames.filterIndexed { index, _ -> distances[index] == maxDistance }
}
fun printResults(carNames: List<String>, distances: List<Int>) {
carNames.indices.forEach { index ->
println("${carNames[index]} : ${"-".repeat(distances[index])}")
}
}
→ 최종 우승자를 결정하고 출력하는 기능으로 우승자는 최대 거리를 이동한 자동차를 필터링하여 결정하였고, 각 차수마다 자동차의 이동 결과를 출력했다. printResults 함수에서 "-".repeat()을 사용해 시각적으로 자동차의 이동을 나타낸 점은 직관적이었지만, 자동차의 이름과 함께 결과를 출력했다면 더 이해하기 쉬웠을거 같다.
▶개선할 부분 및 느낀 점
- 입력 검증 강화: 입력 단계에서 잘못된 값을 입력했을 때 예외 처리가 부족한 부분이 있었다. 자동차 이름 입력 시 비어있는 이름을 입력하거나, 시도 횟수 입력 시 숫자가 아닌 값을 입력했을 때 이에 대한 예외 처리가 필요하다. 이러한 점을 보완하기 위해 입력 검증 로직을 더욱 강화해야 한다.
- 테스트 코드 부족: 작성한 기능들을 제대로 검증하기 위한 테스트 코드가 부족했다. 코틀린을 처음 배우는 단계에서 테스트 코드 작성은 어렵게 느껴졌고, 자바를 배우면서도 테스트 코드 작성을 해본경험이 적었기때문에 기능이 제대로 동작하는지 확인하기 위해 경주 로직이나 입력 검증에 대한 단위 테스트를 작성했으면 많은 공부가 됐을거다.
- 코틀린 언어 활용 부족: 코틀린에는 많은 편리한 기능과 문법이 있지만, 이번 과제에서는 이를 충분히 활용하지 못했다. 코틀린의 확장 함수나 데이터 클래스를 사용하면 코드의 가독성을 높이고 유지보수가 쉬워지지만, 이런 장점을 잘 활용 할 만큼 내 실력이 따라주질 못했다.
▶보완한 점
▷코틀린의 기능 활용
확장 함수(Extension Function)
입력된 자동차 이름 리스트에 대해 유효성을 검증하는 함수를 확장 함수로 작성해서 코드가 더 깔끔해졌고, 입력 리스트에 대해 직접적으로 검증을 수행할 수 있었다.
fun List<String>.validateCarNames() {
if (this.isEmpty() || this.any { it.length > 5 }) {
throw IllegalArgumentException("자동차 이름은 5글자 이하만 가능합니다.")
}
}
데이터 클래스 사용
경주에 참여하는 자동차 정보를 data class로 정의해서 코드의 가독성이 좋아졌다. Car 클래스를 통해 각 자동차의 상태를 관리하면 경주 로직 구현 시 더 직관적으로 처리할 수 있었다.
data class Car(val name: String, var distance: Int = 0)
예외 처리 개선
사용자 입력 시 잘못된 값에 대해 구체적인 피드백을 제공하고 사용자 친화적인 예외 처리를 할 수 있도록 개선했다.이렇게 하면 오류가 발생했을 때 구체적인 자동차 이름을 사용자에게 알려줄 수 있어, 문제를 명확하게 파악하고 수정하기 쉬웠다.
fun validateCarNames(carNames: List<String>) {
carNames.forEach {
if (it.length > 5) {
throw IllegalArgumentException("자동차 이름 '$it' 은 5글자 이하만 가능합니다.")
}
}
}
▷테스트코드 작성
코틀린에서 JUnit을 사용하여 단위 테스트를 작성했다. 특히 자동차 경주 로직에서의 입력 유효성 검사나 시도 횟수 입력과 같은 부분은 예외 상황이 발생할 수 있기때문에 테스트 코드를 통해 다양한 케이스를 검증했다.
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
class RacingCarTest {
@Test
fun `자동차 이름 유효성 검사 - 5글자 초과`() {
val carNames = listOf("자동차1", "긴이름자동차")
val exception = assertThrows<IllegalArgumentException> {
validateCarNames(carNames)
}
assertEquals("자동차 이름은 5글자 이하만 가능합니다.", exception.message)
}
@Test
fun `경주 우승자 결정`() {
val carNames = listOf("자동차1", "자동차2")
val distances = listOf(3, 5)
val winners = determineWinners(carNames, distances)
assertEquals(listOf("자동차2"), winners)
}
}
2주차 프리코스 과제를 통해 코틀린의 기본적인 문법과 흐름을 익히는 데 큰 도움이 되었다. 특히 자바와 비교해서 코틀린에서 제공하는 다양한 기능들이 생산성을 크게 향상시킬 수 있다는 점을 느꼈다. 하지만, 코틀린의 문법에 아직 익숙하지 않아서 자바로 구현했다가 변환해보기도 하고, 의미가 없다는 생각에 ai 없이 구현도 도전해보고 처음부터 끝까지 너무 많은 시간을 소요해야 했다. 처음 자바 배울때 생각 나고.... 코드 작성법은 낯설고....3, 4 주차 과제도 개선할 부분 적용해보고 개발일지에 기록 하려면 코틀린을 좀 더 깊이 있게 공부하고, 언어의 장점을 최대한 활용하는 방향으로 가야겠다.
'Kotlin' 카테고리의 다른 글
[TIL]20241129 Inline 함수와 Reified 키워드 (0) | 2024.11.29 |
---|---|
[TIL]20241128 자바 스트림과 코틀린 시퀀스의 차이 (1) | 2024.11.28 |
[TIL]20241118 확장성과 오버엔지니어링 (3) | 2024.11.18 |
[TIL]20241110 커링(Currying) (0) | 2024.11.10 |
[TIL]20241109 sealed interface (0) | 2024.11.09 |