From 31194c9e5557f4441975a95b2170d6c4f6771788 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 09:17:22 +0900
Subject: [PATCH 01/13] =?UTF-8?q?refactor(review):=20groupingBy=20?=
 =?UTF-8?q?=EC=82=AC=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/domain/Lottos.kt | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/main/kotlin/lotto/domain/Lottos.kt b/src/main/kotlin/lotto/domain/Lottos.kt
index dd59384b6..9c07ba617 100644
--- a/src/main/kotlin/lotto/domain/Lottos.kt
+++ b/src/main/kotlin/lotto/domain/Lottos.kt
@@ -19,7 +19,8 @@ class Lottos(private val tickets: List<Lotto>) {
     ): Map<Rank, Int> {
         return tickets
             .map { it.match(winningNumber, bonusBall) }
-            .groupBy { it }
-            .mapValues { it.value.size }
+            .groupingBy {
+                it
+            }.eachCount()
     }
 }

From ab0105e88f921f605e0f7f07bdc55af4221b42f3 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 19:20:30 +0900
Subject: [PATCH 02/13] =?UTF-8?q?feat:=20LottoPurchaseManager=20=EC=83=9D?=
 =?UTF-8?q?=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../lotto/domain/LottoPurchaseManager.kt      | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 src/main/kotlin/lotto/domain/LottoPurchaseManager.kt

diff --git a/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt b/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt
new file mode 100644
index 000000000..c0d75fc8c
--- /dev/null
+++ b/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt
@@ -0,0 +1,21 @@
+package lotto.domain
+
+import Lottos
+
+class LottoPurchaseManager {
+    fun purchase(
+        price: LottoPrice,
+        manualLottos: List<Lotto>,
+    ): Lottos {
+        val totalCount = price.calculatePurchaseCount()
+        val autoCount = totalCount - manualLottos.size
+        require(autoCount >= 0) { "수동 구매 개수가 총 구매 가능 개수를 초과했습니다." }
+
+        val autoLottos = createAutoLottos(autoCount)
+        return Lottos(manualLottos + autoLottos)
+    }
+
+    private fun createAutoLottos(count: Int): List<Lotto> {
+        return (1..count).map { LottoRandomGenerator.randomGenerate() }
+    }
+}

From 3fcae1fdda637a1e5677e407ca46b512c03d12cf Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 19:20:48 +0900
Subject: [PATCH 03/13] =?UTF-8?q?test:=20LottoPurchaseManger=EC=B6=94?=
 =?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/test/kotlin/lotto/LottoGameTest.kt | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/test/kotlin/lotto/LottoGameTest.kt b/src/test/kotlin/lotto/LottoGameTest.kt
index 6ed1335ba..1d30e7469 100644
--- a/src/test/kotlin/lotto/LottoGameTest.kt
+++ b/src/test/kotlin/lotto/LottoGameTest.kt
@@ -4,6 +4,7 @@ import Lottos
 import lotto.domain.Lotto
 import lotto.domain.LottoNumber
 import lotto.domain.LottoPrice
+import lotto.domain.LottoPurchaseManager
 import lotto.domain.Rank
 import lotto.service.LottoService
 import org.assertj.core.api.Assertions.assertThat
@@ -20,7 +21,7 @@ class LottoGameTest {
     @CsvSource("1000", "2000", "3000")
     fun `구매한 로또 만큼 랜덤한 번호를 생성한다`(money: Int) {
         val lottoPrice = LottoPrice(money)
-        val purchasedLottos = LottoService().purchase(lottoPrice)
+        val purchasedLottos = LottoService(LottoPurchaseManager()).purchase(lottoPrice)
 
         assertThat(purchasedLottos.size).isEqualTo(lottoPrice.calculatePurchaseCount())
 
@@ -58,7 +59,7 @@ class LottoGameTest {
         val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6))
         val bonusNumber = LottoNumber(7)
 
-        val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber)
+        val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber)
 
         assertThat(winningResult.getWinningCount(Rank.NONE)).isEqualTo(0)
     }
@@ -77,7 +78,7 @@ class LottoGameTest {
         val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6))
         val bonusNumber = LottoNumber(7)
 
-        val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber)
+        val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber)
 
         assertThat(winningResult.getWinningCount(Rank.FIRST)).isEqualTo(1)
         assertThat(winningResult.getWinningCount(Rank.SECOND)).isEqualTo(1)
@@ -96,7 +97,7 @@ class LottoGameTest {
         val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6))
         val bonusNumber = LottoNumber(7)
 
-        val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber)
+        val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber)
 
         assertThat(winningResult.getWinningCount(Rank.FIFTH)).isEqualTo(0)
         assertThat(winningResult.getWinningCount(Rank.FOURTH)).isEqualTo(0)
@@ -118,7 +119,7 @@ class LottoGameTest {
         val winningNumbers = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6))
         val bonusNumber = LottoNumber(7)
 
-        val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusNumber)
+        val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusNumber)
 
         assertThat(winningResult.getWinningCount(Rank.FIRST)).isEqualTo(1)
         assertThat(winningResult.getWinningCount(Rank.SECOND)).isEqualTo(1)

From a516fb134a6c2fc22aae6ed76069120d71fe8364 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 19:21:11 +0900
Subject: [PATCH 04/13] =?UTF-8?q?refactor:=20=ED=94=84=EB=A1=9C=ED=8D=BC?=
 =?UTF-8?q?=ED=8B=B0=20private=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/domain/LottoPrice.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/kotlin/lotto/domain/LottoPrice.kt b/src/main/kotlin/lotto/domain/LottoPrice.kt
index b3c5af4fa..1c6bda316 100644
--- a/src/main/kotlin/lotto/domain/LottoPrice.kt
+++ b/src/main/kotlin/lotto/domain/LottoPrice.kt
@@ -1,7 +1,7 @@
 package lotto.domain
 
 @JvmInline
-value class LottoPrice(val money: Int) {
+value class LottoPrice(private val money: Int) {
     init {
         require(money >= LottoConstants.LOTTO_PRICE) { "로또 구입 금액은 1000원 이상이어야 합니다." }
     }

From 1f4dddac351501eca9d55673a4eae02970d01244 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 19:22:34 +0900
Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=EB=A1=9C?=
 =?UTF-8?q?=EB=98=90=20input=20view=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?=
 =?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/view/InputView.kt | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt
index 005b166e7..5854c068c 100644
--- a/src/main/kotlin/lotto/view/InputView.kt
+++ b/src/main/kotlin/lotto/view/InputView.kt
@@ -29,4 +29,20 @@ class InputView {
         require(!winningNumbers.numbers.contains(bonusBallNumber)) { "보너스 볼은 당첨 번호와 중복될 수 없습니다." }
         return bonusBallNumber
     }
+
+    fun readManualLottoCount(): Int {
+        println("\n수동으로 구매할 로또 수를 입력해 주세요.")
+        return readln().toInt()
+    }
+
+    fun readManualLottoNumbers(count: Int): List<Lotto> {
+        println("\n수동으로 구매할 번호를 입력해 주세요.")
+        return (1..count).map {
+            val numbers =
+                readln().split(",")
+                    .map { it.trim().toInt() }
+                    .map { LottoNumber(it) }
+            Lotto(numbers)
+        }
+    }
 }

From 361771bbae144288085f86e61c76e4275db3e620 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 19:26:56 +0900
Subject: [PATCH 06/13] =?UTF-8?q?feat:=20=EC=9A=94=EA=B5=AC=EC=82=AC?=
 =?UTF-8?q?=ED=95=AD=20controller=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 README.md                                           |  4 ++--
 src/main/kotlin/lotto/controller/LottoController.kt | 11 ++++++++---
 src/main/kotlin/lotto/service/LottoService.kt       | 11 +++++++----
 src/main/kotlin/lotto/view/ResultView.kt            | 10 +++++++---
 4 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/README.md b/README.md
index e591a7678..0331793eb 100644
--- a/README.md
+++ b/README.md
@@ -3,5 +3,5 @@
 🚀 3단계 - 로또(2등)
 ## 구현 기능 목록
 
-- [x] 2등을 위해 추가 번호를 하나 더 추첨한다.
-- [x] 당첨 통계에 2등을 추가한다.
+- [x] 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다
+- [x] 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.
diff --git a/src/main/kotlin/lotto/controller/LottoController.kt b/src/main/kotlin/lotto/controller/LottoController.kt
index b36203a67..6eeb290ed 100644
--- a/src/main/kotlin/lotto/controller/LottoController.kt
+++ b/src/main/kotlin/lotto/controller/LottoController.kt
@@ -1,18 +1,23 @@
 package lotto.controller
 
 import lotto.domain.LottoPrice
+import lotto.domain.LottoPurchaseManager
 import lotto.service.LottoService
 import lotto.view.InputView
 import lotto.view.ResultView
 
 fun main() {
     val purchaseAmount = InputView().readPurchaseAmount()
-    val lottos = LottoService().purchase(LottoPrice(purchaseAmount))
-    ResultView().printPurchaseResult(lottos)
+
+    val manualCount = InputView().readManualLottoCount()
+    val manualLottos = InputView().readManualLottoNumbers(manualCount)
+
+    val lottos = LottoService(LottoPurchaseManager()).purchase(LottoPrice(purchaseAmount), manualLottos)
+    ResultView().printPurchaseResult(manualCount, lottos)
 
     val winningNumbers = InputView().readWinningNumbers()
     val bonusBall = InputView().readBonusBall(winningNumbers)
-    val winningResult = LottoService().checkWinning(lottos, winningNumbers, bonusBall)
+    val winningResult = LottoService(LottoPurchaseManager()).checkWinning(lottos, winningNumbers, bonusBall)
 
     ResultView().printWinningStatistics(winningResult)
     ResultView().printProfitRate(winningResult.calculateProfitRate(purchaseAmount))
diff --git a/src/main/kotlin/lotto/service/LottoService.kt b/src/main/kotlin/lotto/service/LottoService.kt
index 741cb4a65..a3cc442b2 100644
--- a/src/main/kotlin/lotto/service/LottoService.kt
+++ b/src/main/kotlin/lotto/service/LottoService.kt
@@ -2,15 +2,18 @@ package lotto.service
 
 import Lottos
 import lotto.domain.Lotto
-import lotto.domain.LottoFactory
 import lotto.domain.LottoNumber
 import lotto.domain.LottoPrice
+import lotto.domain.LottoPurchaseManager
 import lotto.domain.WinningLotto
 import lotto.domain.WinningResult
 
-class LottoService {
-    fun purchase(price: LottoPrice): Lottos {
-        return LottoFactory.create(price)
+class LottoService(private val purchaseManager: LottoPurchaseManager) {
+    fun purchase(
+        price: LottoPrice,
+        manualLottos: List<Lotto> = emptyList(),
+    ): Lottos {
+        return purchaseManager.purchase(price, manualLottos)
     }
 
     fun checkWinning(
diff --git a/src/main/kotlin/lotto/view/ResultView.kt b/src/main/kotlin/lotto/view/ResultView.kt
index cedfd2279..aa57d2ad8 100644
--- a/src/main/kotlin/lotto/view/ResultView.kt
+++ b/src/main/kotlin/lotto/view/ResultView.kt
@@ -5,9 +5,13 @@ import lotto.domain.Rank
 import lotto.domain.WinningResult
 
 class ResultView {
-    fun printPurchaseResult(lottos: Lottos) {
-        println("${lottos.size}개를 구매했습니다.")
-        lottos.lottos.forEach { println(it) }
+    fun printPurchaseResult(
+        manualCount: Int,
+        lottos: Lottos,
+    ) {
+        val autoCount = lottos.size - manualCount
+        println("\n수동으로 ${manualCount}장, 자동으로 ${autoCount}장을 구매했습니다.")
+        lottos.lottos.forEach { println(it.numbers.map { num -> num.number }) }
     }
 
     fun printWinningStatistics(winningResult: WinningResult) {

From 11fced24f346ab1a8e988272e2e328a12a36e5ed Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 19:38:16 +0900
Subject: [PATCH 07/13] =?UTF-8?q?refactor:=20=EC=88=98=EB=8F=99=20?=
 =?UTF-8?q?=EA=B5=AC=EB=A7=A4=20=EB=A1=9C=EB=98=90=20Lottos=20=EC=BB=AC?=
 =?UTF-8?q?=EB=A0=89=EC=85=98=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84?=
 =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/domain/Lottos.kt  |  4 ++++
 src/main/kotlin/lotto/view/InputView.kt | 20 ++++++++++++--------
 2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/main/kotlin/lotto/domain/Lottos.kt b/src/main/kotlin/lotto/domain/Lottos.kt
index 9c07ba617..c4c3f8aa4 100644
--- a/src/main/kotlin/lotto/domain/Lottos.kt
+++ b/src/main/kotlin/lotto/domain/Lottos.kt
@@ -23,4 +23,8 @@ class Lottos(private val tickets: List<Lotto>) {
                 it
             }.eachCount()
     }
+
+    companion object {
+        fun from(lottos: List<Lotto>): Lottos = Lottos(lottos)
+    }
 }
diff --git a/src/main/kotlin/lotto/view/InputView.kt b/src/main/kotlin/lotto/view/InputView.kt
index 5854c068c..ff8e96d7e 100644
--- a/src/main/kotlin/lotto/view/InputView.kt
+++ b/src/main/kotlin/lotto/view/InputView.kt
@@ -1,5 +1,6 @@
 package lotto.view
 
+import Lottos
 import lotto.domain.Lotto
 import lotto.domain.LottoNumber
 
@@ -35,14 +36,17 @@ class InputView {
         return readln().toInt()
     }
 
-    fun readManualLottoNumbers(count: Int): List<Lotto> {
+    fun readManualLottoNumbers(count: Int): Lottos {
         println("\n수동으로 구매할 번호를 입력해 주세요.")
-        return (1..count).map {
-            val numbers =
-                readln().split(",")
-                    .map { it.trim().toInt() }
-                    .map { LottoNumber(it) }
-            Lotto(numbers)
-        }
+
+        val manualLottos =
+            (1..count).map {
+                val numbers =
+                    readln().split(",")
+                        .map { it.trim().toInt() }
+                        .map { LottoNumber(it) }
+                Lotto(numbers)
+            }
+        return Lottos.from(manualLottos)
     }
 }

From fd2be1907b7f43a228fe5d09163ad92aaf0a75c7 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 20:03:01 +0900
Subject: [PATCH 08/13] =?UTF-8?q?refactor:=20lottoFactory=20=EC=9D=B4?=
 =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D?=
 =?UTF-8?q?=EC=84=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/domain/LottoFactory.kt         | 5 ++---
 src/main/kotlin/lotto/domain/LottoPurchaseManager.kt | 8 ++------
 src/main/kotlin/lotto/service/LottoService.kt        | 2 +-
 3 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/src/main/kotlin/lotto/domain/LottoFactory.kt b/src/main/kotlin/lotto/domain/LottoFactory.kt
index 9c04073fd..21f076978 100644
--- a/src/main/kotlin/lotto/domain/LottoFactory.kt
+++ b/src/main/kotlin/lotto/domain/LottoFactory.kt
@@ -3,9 +3,8 @@ package lotto.domain
 import Lottos
 
 object LottoFactory {
-    fun create(price: LottoPrice): Lottos {
-        val purchaseCount = price.calculatePurchaseCount()
-        val tickets = (1..purchaseCount).map { LottoRandomGenerator.randomGenerate() }
+    fun create(count: Int): Lottos {
+        val tickets = (1..count).map { LottoRandomGenerator.randomGenerate() }
         return Lottos(tickets)
     }
 }
diff --git a/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt b/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt
index c0d75fc8c..ae36c4ebd 100644
--- a/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt
+++ b/src/main/kotlin/lotto/domain/LottoPurchaseManager.kt
@@ -5,17 +5,13 @@ import Lottos
 class LottoPurchaseManager {
     fun purchase(
         price: LottoPrice,
-        manualLottos: List<Lotto>,
+        manualLottos: Lottos,
     ): Lottos {
         val totalCount = price.calculatePurchaseCount()
         val autoCount = totalCount - manualLottos.size
         require(autoCount >= 0) { "수동 구매 개수가 총 구매 가능 개수를 초과했습니다." }
 
-        val autoLottos = createAutoLottos(autoCount)
+        val autoLottos = LottoFactory.create(autoCount)
         return Lottos(manualLottos + autoLottos)
     }
-
-    private fun createAutoLottos(count: Int): List<Lotto> {
-        return (1..count).map { LottoRandomGenerator.randomGenerate() }
-    }
 }
diff --git a/src/main/kotlin/lotto/service/LottoService.kt b/src/main/kotlin/lotto/service/LottoService.kt
index a3cc442b2..e4f8d11de 100644
--- a/src/main/kotlin/lotto/service/LottoService.kt
+++ b/src/main/kotlin/lotto/service/LottoService.kt
@@ -11,7 +11,7 @@ import lotto.domain.WinningResult
 class LottoService(private val purchaseManager: LottoPurchaseManager) {
     fun purchase(
         price: LottoPrice,
-        manualLottos: List<Lotto> = emptyList(),
+        manualLottos: Lottos,
     ): Lottos {
         return purchaseManager.purchase(price, manualLottos)
     }

From 6c078aac6ee148ec6102a6d970277c4c143146f1 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 20:03:44 +0900
Subject: [PATCH 09/13] =?UTF-8?q?refactor:=20purchaseAmount=20=EC=9B=90?=
 =?UTF-8?q?=EC=8B=9C=EA=B0=92=20=ED=8F=AC=EC=9E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/controller/LottoController.kt | 4 ++--
 src/main/kotlin/lotto/domain/LottoPrice.kt          | 3 +++
 src/main/kotlin/lotto/domain/WinningResult.kt       | 4 ++--
 src/test/kotlin/lotto/WinningResultTest.kt          | 3 ++-
 4 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/main/kotlin/lotto/controller/LottoController.kt b/src/main/kotlin/lotto/controller/LottoController.kt
index 6eeb290ed..7cc965e22 100644
--- a/src/main/kotlin/lotto/controller/LottoController.kt
+++ b/src/main/kotlin/lotto/controller/LottoController.kt
@@ -7,12 +7,12 @@ import lotto.view.InputView
 import lotto.view.ResultView
 
 fun main() {
-    val purchaseAmount = InputView().readPurchaseAmount()
+    val purchaseAmount = LottoPrice(InputView().readPurchaseAmount())
 
     val manualCount = InputView().readManualLottoCount()
     val manualLottos = InputView().readManualLottoNumbers(manualCount)
 
-    val lottos = LottoService(LottoPurchaseManager()).purchase(LottoPrice(purchaseAmount), manualLottos)
+    val lottos = LottoService(LottoPurchaseManager()).purchase(purchaseAmount, manualLottos)
     ResultView().printPurchaseResult(manualCount, lottos)
 
     val winningNumbers = InputView().readWinningNumbers()
diff --git a/src/main/kotlin/lotto/domain/LottoPrice.kt b/src/main/kotlin/lotto/domain/LottoPrice.kt
index 1c6bda316..fd2bc61ad 100644
--- a/src/main/kotlin/lotto/domain/LottoPrice.kt
+++ b/src/main/kotlin/lotto/domain/LottoPrice.kt
@@ -6,6 +6,9 @@ value class LottoPrice(private val money: Int) {
         require(money >= LottoConstants.LOTTO_PRICE) { "로또 구입 금액은 1000원 이상이어야 합니다." }
     }
 
+    val value: Int
+        get() = money
+
     fun calculatePurchaseCount(): Int {
         return money / LottoConstants.LOTTO_PRICE
     }
diff --git a/src/main/kotlin/lotto/domain/WinningResult.kt b/src/main/kotlin/lotto/domain/WinningResult.kt
index 07d167150..089668393 100644
--- a/src/main/kotlin/lotto/domain/WinningResult.kt
+++ b/src/main/kotlin/lotto/domain/WinningResult.kt
@@ -11,8 +11,8 @@ class WinningResult(private val winningStatistics: Map<Rank, Int>) {
         }
     }
 
-    fun calculateProfitRate(purchaseAmount: Int): Double {
-        return calculateTotalPrize().toDouble() / purchaseAmount
+    fun calculateProfitRate(purchaseAmount: LottoPrice): Double {
+        return calculateTotalPrize().toDouble() / purchaseAmount.value
     }
 
     fun getWinningCount(rank: Rank): Int {
diff --git a/src/test/kotlin/lotto/WinningResultTest.kt b/src/test/kotlin/lotto/WinningResultTest.kt
index 90eee886b..aef7fb334 100644
--- a/src/test/kotlin/lotto/WinningResultTest.kt
+++ b/src/test/kotlin/lotto/WinningResultTest.kt
@@ -1,5 +1,6 @@
 package lotto
 
+import lotto.domain.LottoPrice
 import lotto.domain.Rank
 import lotto.domain.WinningResult
 import org.assertj.core.api.Assertions.assertThat
@@ -47,7 +48,7 @@ class WinningResultTest {
     @Test
     fun `수익률을 계산한다`() {
         val winningStatistics = mapOf(Rank.FOURTH to 1)
-        val purchaseAmount = 10000
+        val purchaseAmount = LottoPrice(10000)
         val winningResult = WinningResult(winningStatistics)
         val profitRate = winningResult.calculateProfitRate(purchaseAmount)
         assertThat(profitRate).isEqualTo(0.5)

From 15f908db4bd18ae754c2995328f630dd85948979 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 20:04:11 +0900
Subject: [PATCH 10/13] =?UTF-8?q?test:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?=
 =?UTF-8?q?=EB=98=90=20=ED=85=8C=EC=8A=A4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/test/kotlin/lotto/LottoGameTest.kt | 30 +++++++++++++++++++++++++-
 1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/src/test/kotlin/lotto/LottoGameTest.kt b/src/test/kotlin/lotto/LottoGameTest.kt
index 1d30e7469..99c24bed5 100644
--- a/src/test/kotlin/lotto/LottoGameTest.kt
+++ b/src/test/kotlin/lotto/LottoGameTest.kt
@@ -21,7 +21,7 @@ class LottoGameTest {
     @CsvSource("1000", "2000", "3000")
     fun `구매한 로또 만큼 랜덤한 번호를 생성한다`(money: Int) {
         val lottoPrice = LottoPrice(money)
-        val purchasedLottos = LottoService(LottoPurchaseManager()).purchase(lottoPrice)
+        val purchasedLottos = LottoService(LottoPurchaseManager()).purchase(lottoPrice, Lottos(listOf()))
 
         assertThat(purchasedLottos.size).isEqualTo(lottoPrice.calculatePurchaseCount())
 
@@ -30,6 +30,34 @@ class LottoGameTest {
         }
     }
 
+    @ParameterizedTest
+    @CsvSource(
+        "1000, 0",
+        "2000, 1",
+        "3000, 2",
+    )
+    fun `수동과 자동 로또를 합쳐서 구매 금액만큼 로또를 생성한다`(
+        money: Int,
+        manualCount: Int,
+    ) {
+        // given
+        val lottoPrice = LottoPrice(money)
+        val manualLottos =
+            List(manualCount) {
+                Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6))
+            }
+
+        val purchasedLottos = LottoService(LottoPurchaseManager()).purchase(lottoPrice, Lottos(manualLottos))
+
+        assertThat(purchasedLottos.size).isEqualTo(lottoPrice.calculatePurchaseCount())
+        assertThat(purchasedLottos.lottos.take(manualCount))
+            .containsExactlyElementsOf(manualLottos)
+
+        purchasedLottos.lottos.forEach {
+            assertThat(it.numbers.distinct().size).isEqualTo(6)
+        }
+    }
+
     @Test
     fun `구매한 로또번호와 당첨번호가 3개 이상 일치하면 당첨된다`() {
         val purchaseLotto = Lotto(createLottoNumbers(1, 2, 3, 4, 5, 6))

From 5a3c02f8239ef953aa0606147a35a3a40ee289c1 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 20:04:34 +0900
Subject: [PATCH 11/13] =?UTF-8?q?refactor:=20lottos=20=EC=BB=AC=EB=A0=89?=
 =?UTF-8?q?=EC=85=98=20plus=20operator=20=EC=82=AC=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/main/kotlin/lotto/domain/Lottos.kt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/main/kotlin/lotto/domain/Lottos.kt b/src/main/kotlin/lotto/domain/Lottos.kt
index c4c3f8aa4..baf4cfc6e 100644
--- a/src/main/kotlin/lotto/domain/Lottos.kt
+++ b/src/main/kotlin/lotto/domain/Lottos.kt
@@ -24,6 +24,10 @@ class Lottos(private val tickets: List<Lotto>) {
             }.eachCount()
     }
 
+    operator fun plus(lotto: Lottos): List<Lotto> {
+        return tickets + lotto.tickets
+    }
+
     companion object {
         fun from(lottos: List<Lotto>): Lottos = Lottos(lottos)
     }

From 169d68e71ba2a193107ec97c0bcce7fe317d3add Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 20:27:53 +0900
Subject: [PATCH 12/13] =?UTF-8?q?style:=20lint=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/test/kotlin/lotto/LottoGameTest.kt | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/src/test/kotlin/lotto/LottoGameTest.kt b/src/test/kotlin/lotto/LottoGameTest.kt
index 99c24bed5..3b9978f8e 100644
--- a/src/test/kotlin/lotto/LottoGameTest.kt
+++ b/src/test/kotlin/lotto/LottoGameTest.kt
@@ -17,22 +17,8 @@ class LottoGameTest {
         return numbers.map { LottoNumber(it) }
     }
 
-    @ParameterizedTest
-    @CsvSource("1000", "2000", "3000")
-    fun `구매한 로또 만큼 랜덤한 번호를 생성한다`(money: Int) {
-        val lottoPrice = LottoPrice(money)
-        val purchasedLottos = LottoService(LottoPurchaseManager()).purchase(lottoPrice, Lottos(listOf()))
-
-        assertThat(purchasedLottos.size).isEqualTo(lottoPrice.calculatePurchaseCount())
-
-        purchasedLottos.lottos.forEach {
-            assertThat(it.numbers.distinct().size).isEqualTo(6)
-        }
-    }
-
     @ParameterizedTest
     @CsvSource(
-        "1000, 0",
         "2000, 1",
         "3000, 2",
     )
@@ -40,7 +26,6 @@ class LottoGameTest {
         money: Int,
         manualCount: Int,
     ) {
-        // given
         val lottoPrice = LottoPrice(money)
         val manualLottos =
             List(manualCount) {

From 04e9740f18cb4467bdb9cea1151748066639b5c3 Mon Sep 17 00:00:00 2001
From: Aimbe <qkr2435@gmail.com>
Date: Fri, 20 Dec 2024 20:33:16 +0900
Subject: [PATCH 13/13] =?UTF-8?q?test:=20Rank=20=ED=85=8C=EC=8A=A4?=
 =?UTF-8?q?=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/test/kotlin/lotto/domain/RankTest.kt | 47 ++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 src/test/kotlin/lotto/domain/RankTest.kt

diff --git a/src/test/kotlin/lotto/domain/RankTest.kt b/src/test/kotlin/lotto/domain/RankTest.kt
new file mode 100644
index 000000000..fbbc07bed
--- /dev/null
+++ b/src/test/kotlin/lotto/domain/RankTest.kt
@@ -0,0 +1,47 @@
+package lotto.domain
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+class RankTest {
+    @Test
+    fun `6개 맞추면 1등이다`() {
+        val rank = Rank.from(matchCount = 6, matchBonus = false)
+        assertThat(rank).isEqualTo(Rank.FIRST)
+    }
+
+    @Test
+    fun `5개와 보너스 볼을 맞추면 2등이다`() {
+        val rank = Rank.from(matchCount = 5, matchBonus = true)
+        assertThat(rank).isEqualTo(Rank.SECOND)
+    }
+
+    @Test
+    fun `5개 맞추고 보너스 볼을 못 맞추면 3등이다`() {
+        val rank = Rank.from(matchCount = 5, matchBonus = false)
+        assertThat(rank).isEqualTo(Rank.THIRD)
+    }
+
+    @Test
+    fun `4개 맞추면 4등이다`() {
+        val rank = Rank.from(matchCount = 4, matchBonus = false)
+        assertThat(rank).isEqualTo(Rank.FOURTH)
+    }
+
+    @Test
+    fun `3개 맞추면 5등이다`() {
+        val rank = Rank.from(matchCount = 3, matchBonus = false)
+        assertThat(rank).isEqualTo(Rank.FIFTH)
+    }
+
+    @Test
+    fun `2개 이하 맞추면 미당첨이다`() {
+        val rank2 = Rank.from(matchCount = 2, matchBonus = false)
+        val rank1 = Rank.from(matchCount = 1, matchBonus = false)
+        val rank0 = Rank.from(matchCount = 0, matchBonus = false)
+
+        assertThat(rank2).isEqualTo(Rank.NONE)
+        assertThat(rank1).isEqualTo(Rank.NONE)
+        assertThat(rank0).isEqualTo(Rank.NONE)
+    }
+}