TDD, ํด๋ฆฐ ์ฝ๋ with Kotlin 6๊ธฐ ๋ก๋ ํผ๋๋ฐฑ
๋ฏธ์ ์ ์งํํ๋ฉฐ ๋ฐ์๋ ํผ๋๋ฐฑ์ ํ ๋๋ก ๋ฐฐ์ด ์ ๋ค์ ์ ์ด๋ณด๋ ค ํ๋ค.
ํด๋ฆฐ์ฝ๋ ๋ฟ๋ง ์๋๋ผ ์ฝํ๋ฆฐ์ ์ข ๋ ์ฝํ๋ฆฐ์ค๋ฝ๊ฒ ๊ตฌํํ ์ ์๋ ๋ฐฉ๋ฒ๋ค์ ๋ง์ด ์์๊ฐ ์ ์์ด ๋งค์ฐ ์ข์ ๊ฒฝํ์ด์๋ค!
Step2
https://github.com/next-step/kotlin-lotto/pull/759
๊ฒ์ฆ๋ถ๋ ํ๋ ์ฝ๋ฉ์ ์ฌ์ฉํ์
ํ ์คํธ์ฝ๋์์ ๊ฒ์ฆ์ ์ํด ๋๋ฉ์ธ์ ์์๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํด ๊ฒ์ฆ ๋ก์ง์ ์งฐ๋๋ฐ ํ ์คํธ์์๋ ์์๋ฅผ ์ง์ ํ์ฉํ๊ธฐ ๋ณด๋ค๋ ํ๋ ์ฝ๋ฉ์ ํด๋ณด๋๊ฑด ์ด๋ป๊ฒ ๋๋ ํผ๋๋ฐฑ์ ๋ฐ์๋ค.
๊ฒ์ฆ๋ถ์ ๋๋ฉ์ธ ๋ก์ง์ ์ถ๊ฐํ์ ๋์ ๋ฌธ์ ์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ๋ฌด์๋ฏธํ ๊ฒ์ฆ
- ์ฌ์ค์ ํ๋ก๋์ ์ฝ๋๋ฅผ ๋ณต์ฌ & ๋ถ์ฌ๋ฃ๊ธฐ ํ ๊ฒ๊ณผ ๊ฐ๋ค.
- ๊ตฌํ ์ฝ๋์์ ๊ฐ๊ฒฐํฉ
- ํ๋ก๋์ ์ฝ๋์ ๊ฐ๊ฒฐํฉํ๊ฒ ๋๋ฉด ๊ฒฐ๊ณผ๋ ๋์ผํ์ง๋ง ๋ก์ง์ด ๋ณ๊ฒฝ๋๋ ๋ฆฌํฉํฐ๋ง์ ํ๋ค๋ฉด ํ ์คํธ ์ฝ๋์ ๋๋ฉ์ธ ๋ก์ง ๋ํ ํจ๊ป ๊ณ ์ณ์ผ ํ๋ค.
- ๊ฑฐ์ง ์ฑ๊ณต
- ๋๋ฉ์ธ ๋ก์ง ์์ฒด๊ฐ ๋ฒ๊ทธ์ธ ๊ฒฝ์ฐ์๋ ํ ์คํธ ์ฝ๋๊ฐ ์คํจํ์ง ์๋๋ค.
- ์ฆ, ๊ฒ์ฆ๋ฌธ์ ๋์ผํ ๋ก์ง์ ์ฌ์ฉํ๋ ์๊ฐ ๋ด ๋๋ฉ์ธ ๋ก์ง์ ๊ฒ์ฆ์ ๋ณด์ฅ๋ฐ์ง ๋ชปํ๊ฒ ๋๋ค.
- Test First(TDD)์ ์ด๋ ค์
- ์ํํธ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ ๊ฒ ์์ฒด๊ฐ ๋๋ Test First๋ก ๊ฐ๋ฐํ์ง ์๋๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. Test First ์์ฒด๊ฐ ๊ตฌํ๋ถ๋ฅผ ๋ฏธ๋ฆฌ ์๊ฐํ๊ณ ์งํํ๋ ๊ฒ์ด ์๋๋ผ, ์์ ๊ฒฐ๊ณผ๋ฅผ ๋ฏธ๋ฆฌ ํ ์คํธ ์ฝ๋๋ก ๊ตฌํํ ๊ตฌํ๋ถ๋ฅผ ๋ง๋ค๊ณ ๋ฆฌํฉํ ๋ง ํ๋ ๊ณผ์ ์ผ๋ก ์งํํ๋ค.
- ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด๋ฏธ ๊ตฌํ๋ถ๋ฅผ ํ ์คํธ ์ฝ๋์์ ์ ์์ฑํ๋ ๊ฒ์ ๋ฆฌํฉํ ๋ง ๊ณผ์ ์ ํฌ๊ธฐํ๋ ๊ฒ๊ณผ ๋ค๋ฅผ๋ฐ ์๋ค.
- ๊ฒฐ๊ณผ ์์ธก์ ์ด๋ ค์
- ์์๋๋ ํ ์คํธ์ฝ๋์ ๊ฒฐ๊ณผ๋ฅผ ํ๋์ ํ์ ํ๊ธฐ ์ด๋ ต๋ค.
- ๋ค์๊ณผ ๊ฐ์ด expected ๊ฐ ์ ์ธ๋ ๊ฒฝ์ฐ ๊ฒฐ๊ณผ๊ฐ ์์์ด ๋ ๊น?
string expected = "C:\\\\Output Folder" + string.Join("\\\\", target.Field1, target.Field2, target.Field3, target.Field4);
์ฐธ๊ณ
https://jojoldu.tistory.com/615?category=1036934
Step3
https://github.com/next-step/kotlin-lotto/pull/769
์ผ๊ธ ์ปฌ๋ ์ ๋ด๋ถ ๊ฐ ์ฒ๋ฆฌํ๊ธฐ
- ์ผ๊ธ ์ปฌ๋ ์ ์ ๋ง๋ค ๊ฒฝ์ฐ ๊ฐ๋ ์ฑ์ด ํฅ์๋๋ฉฐ ์ ํจ์ฑ ๊ฒ์ฆ์ ์ธ๋ถ์ ์ผ๋ก ํ ์ ์๊ณ ๋ถ๋ณํ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
- ์ด๋ฌํ ์ด์ ๋ก ์ผ๊ธ ์ปฌ๋ ์ ์ ์ ์ฉํ๋๋ฐ ๋ด๋ถ ๊ฐ์ ๋ํ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๋ ์ธ๋ถ ํด๋์ค์์๋ ๊ฐ์ ๊บผ๋ด์ ์ฒ๋ฆฌํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ฐ์ ๊บผ๋ด์ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ์์ ์ฝ๋๊ฐ ์กฐ๊ธ ๋๋ฌ์์ง๋ ๊ฒ์ ๋๊ผ๋ค.
- ๋ด๋ถ ๊ฐ์ ๋ํ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ์๋ ์ธ๋ถ์์ ๊ฐ์ ๊บผ๋ด๊ธฐ๋ณด๋ค ์ผ๊ธ ์ปฌ๋ ์ ํด๋์ค ๋ด์์ ์ฒ๋ฆฌํ๋ ๊ฒ๊น์ง๊ฐ ์ผ๊ธ ์ปฌ๋ ์ ์ ์ญํ ์ด๋ฉฐ ์ญํ ๋ ๊น๋ํ๊ฒ ๋ถ๋ฆฌํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ค.
class LottoMatcher(private val winningNumbers: List<Int>) {
fun getMatchingResult(lottoTickets: LottoTickets): LottoResult {
val map = lottoTickets.numbers
.map { numbers -> countMatchingNumbers(numbers) }
.groupingBy { LottoRank.getLottoRankByMatchCount(it) }
.eachCount()
return LottoResult(map)
}
private fun countMatchingNumbers(myNumbers: Numbers) = myNumbers.values.count { winningNumbers.contains(it) }
}
class WinningTicket(private val winningNumbers: LottoNumbers) {
fun getMatchingResult(lottoTickets: LottoTickets): LottoResult {
return myTickets
.matchCount(winningNumbers)
.groupingBy { matchCount -> LottoRank(matchCount) }
.eachCount()
.let { LottoResult(it) }
}
}
์๋์ ๊ฐ์ด ์ผ์นํ๋ ๋ก๋ ๊ฐ์๋ฅผ ๊ณ์ฐํ๋ ๋ก์ง์ LottoTickets ์ผ๊ธ ์ปฌ๋ ์ ์ ์์ํ๋ฉด์ ์ฝ๋๊ฐ ๊น๋ํด์ง๊ณ ๊ฐ๋ ์ฑ์ ๋์ผ ์ ์์๋ค.
value class ์ฌ์ฉํ๊ธฐ (๋ฉ๋ชจ๋ฆฌ ์ ์ฝํ๊ธฐ)
์์ฃผ ์ฌ์ฉ๋๋ฉฐ, ๊ณ ์ ๋์ด ๋ง๋ค์ด์ง๋ ๊ฐ์ฒด๋ค์ ๋ํด์๋ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋๊ณ ์ฌํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ์ข๋ค.
@JvmInline
value class LottoNumber(
val number: Int
) {
init {
require(number in (MIN..MAX)) {
"$MIN ๊ณผ $MAX ์ฌ์ด์ ๊ฐ๋ง ์์ฑํ ์ ์์ต๋๋ค. input: [$number]"
}
}
companion object {
private const val MIN = 1
private const val MAX = 45
}
}
์์ LottoNumber ํด๋์ค๋ฅผ ๋์ปดํ์ผํด๋ณด๋ฉด ์์ฑ์์์ int ํ์ ์ ๋ฆฌํดํ๊ณ ์๋ ๊ฒ์ ๋ณผ ์ ์์ ๊ฒ์ด๋ค.
๊ฒ์ฆ์ด ๊ฐ๋ฅํ ์์ ์ ์ธ ๊ฐ์ฒด๋ฅผ ์ํด ์์๊ฐ์ ๋ํํ๋ค๋ณด๋ ์ธ์คํด์ค๋ฅผ ๋ง๋ค์ด ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์งํ๊ฒ ๋๊ณ ์ด๋ฅผ ์ค์ฌ์ฃผ๊ธฐ ์ํด ๋ก์ปฌ ์บ์๋ฅผ ์ฌ์ฉํ์๋๋ฐ value class๋ฅผ ๋ง๋ค๋ฉด ์ธ์คํด์ค ์์ฒด๋ฅผ ๋ง๋ค์ง ์์ ์ ์๋ค.
์ฆ, value class ๋ฅผ ํตํด ์์๊ฐ ํฌ์ฅ์ผ๋ก ์ธํด ์์ฑ๋๋ ์ธ์คํด์ค๋ก ์ธํ ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ์ฝํ ์ ์๋ค.
- ๋จ, value class ๋ ํ๋์ ๊ฐ(ํ๋กํผํฐ)๋ง ๊ฐ์ง ์ ์๋ค.
- 2๊ฐ ์ด์์ ๊ฐ์ ๊ฐ์ ธ์ผ ํ๋ค๋ฉด ์บ์ฑ ๋ฐ ํฉํ ๋ฆฌํจ์๋ฅผ ์ฌ์ฉํ๋ค.
- ๋ถ๋ณ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐจ์งํ๋ ๊ฒ์ด ๋ถ์ํ๋ค → ๋ฉ๋ชจ๋ฆฌ๋ณด๋ค ๋ด๊ฐ ์์ ํ๊ฒ ์ฝ๋ฉํ ์ ์๋ค๋ ๊ฐ์น๊ฐ ๋ ํฌ๋ค. (ํญ์ ํด์๋ ๊ณ ๋ฏผ์ธ๋ฐ ์ด์ ๋ํ ๋ช ์พํ ๋ต์ธ ๊ฒ ๊ฐ๋ค.)
Step4
https://github.com/next-step/kotlin-lotto/pull/789
๋ฆฌํด ํ์ ์ ๋ช ์ํ์
- ๋ฆฌํด ํ์ ์ ๋ช ์ํ๋ ๊ฒ์ ๋ํ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ์๋ค. ์ฝํ๋ฆฐ์ ํ์ ์ถ๋ก ์ด ๊ฐ๋ฅํ ์ธ์ด๋ค. ๋ฐ๋ผ์ ํจ์์ ๋ฆฌํด ํ์ ์ ์๋ตํ๊ธฐ ์ฌ์ด ํ๊ฒฝ์ด๋ผ ์ด๋ฅผ ๊ฐ๊ณผํ๊ธฐ ์ฝ๋ค.
- ์๋ฅผ ๋ค์ด ์ธํฐํ์ด์ค ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํด์ฃผ๊ณ ํ์ ํ์ ์ผ๋ก ์ด๊ธฐํํ ๊ฐ์ฒด๊ฐ ์์ ๊ฒฝ์ฐ ๋ช ์์ ์ผ๋ก ์ง์ ํด์ค ํ์ ์ ์ง์๋ฒ๋ฆฌ๋ฉด ํ์ ํ์ ์ ๊ฐ์ฒด๋ง ์ฌ์ฉ์ด ๊ฐ๋ฅํด์ง๋ ๋ฌธ์ ๋ฅผ ๊ฒช์ ์ ์๋ค.
- ๋ฆฌํด ํ์ ์ API๋ฅผ ์ ๋ชจ๋ฅด๋ ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ๋ณผ ๋ ์์ฃผ ์ค์ํ ์ ๋ณด์ด๋ค. ๋ฐ๋ผ์ ๋ฆฌํด ํ์ ์ ์ธ๋ถ์์ ํ์ธํ ์ ์๊ฒ ๋ช ์์ ์ผ๋ก ์ง์ ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
View ์ ๋๋ฉ์ธ ์์กด
View์์ ์ถ๋ ฅํ ๋๋ฉ์ธ ์ ๋ณด๊ฐ ๊ฐํ๊ฒ ๊ฒฐํฉ๋์ด ์์ด View์์ ๋๋ฉ์ธ ๋ก์ง์ ๊ฐ์ ธ๊ฐ๋ ๊ตฌ์กฐ๋ก ๊ตฌํ์ ํ์๋ค. ์ด์ ๋ํ ๊ณ ๋ฏผ์ด ์์์ง๋ง ํ์ฌ์ ๊ฐ์ด ๊ฐ๋จํ ๋ฏธ์ ๊ณผ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ์ํฉ์์ DTO๊น์ง๋ ๊ตณ์ด ๋ง๋ค์ด์ฃผ์ง ์์๋ค.
View์์ ๋๋ฉ์ธ ๋ก์ง์ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ด ์๋ ๋๋ฉ์ธ์ ๊ฐ์ ๊บผ๋ด์ ์ถ๋ ฅ๋ง ํ๋ ๊ฒฝ์ฐ์ด๊ธฐ ๋๋ฌธ์ ํ์ฉํด๋ ๊ด์ฐฎ์ง ์์๊น ์๊ฐํ๋ค๋ ๋ฆฌ๋ทฐ์ด๋์ ๋ต๋ณ์ ๋ฐ์๋ค.
ํ์ง๋ง ๋๋ฉ์ธ์ด ์ปค์ง๊ณ ๊ท๋ชจ๊ฐ ์ปค์ง๋ค๋ฉด View์์๋ ๋๋ฉ์ธ ๋ณดํธ๋ฅผ ์ํด Dto๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ด ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ค.