diff --git a/README.md b/README.md
index 5fa2560b46..b90b672af2 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,244 @@
-# java-lotto-precourse
+# ๐ฐ ๋ก๋ ๊ฒ์ (Lotto Game)
+
+์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๊ธ์ก๋งํผ ๋ก๋๋ฅผ ์๋์ผ๋ก ๋ฐํํ๊ณ , ๋น์ฒจ ๋ฒํธ์ ๋ณด๋์ค ๋ฒํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ฒฐ๊ณผ ํต๊ณ์ ์์ต๋ฅ ์ ์ถ๋ ฅํ๋ ์ฝ์ ๊ธฐ๋ฐ ํ๋ก๊ทธ๋จ์
๋๋ค.
+
+Java 21 ํ๊ฒฝ์์ ๋์ํ๋ฉฐ, ๊ธฐ๋ฅ๋ณ ์ฑ
์์ ๋ถ๋ฆฌํด ๊ตฌํํ๋ ๋ฐ ์ค์ ์ ๋์์ต๋๋ค.
+
+
+
+
+## ๐ญ **๐ฏ ํ๋ก์ ํธ์ ์งํฅ์ ๋ฐ** ๋ฐฉํฅ
+
+๊ด๋ จ๋ ๋ฐ์ดํฐ์ ๋์์ ํจ๊ป ๋ฌถ์ด **๊ฐ ํด๋์ค๊ฐ ๋ถ๋ช
ํ ์ฑ
์์ ๊ฐ๋๋ก ๊ตฌ์ฑ**ํ๊ณ , **์ฌ๋ฌ ๊ฐ์ฒด๋ค์ด ์ญํ ์ ๋ฐ๋ผ ํ๋ ฅ**ํ๋ฉฐ ํ๋์ ๊ธฐ๋ฅ์ ์์ฑํ ์ ์๋๋ก ์ค๊ณํฉ๋๋ค.
+
+TDD ์ฌ์ดํด ์ค **Inside-Out ๋ฐฉ์์ ๊ธฐ๋ฐ์ผ๋ก**, **์
์ถ๋ ฅ๋ณด๋ค๋ ๋จผ์ ๋๋ฉ์ธ ๋ก์ง์ ์ค์ฌ์ผ๋ก ๊ฐ๋ฐ์ ์์**ํ๊ณ , ํ
์คํธ ๊ฐ๋ฅํ ์์ ๋จ์๋ถํฐ ์ ์ฐจ ํ์ฅํด ๋๊ฐ๋๋ค.
+
+
+
+
+## ๐ ์ํ์ค ๋ค์ด์ด๊ทธ๋จ
+
+์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ฒด ๋์ ๊ณผ์ ๊ณผ ๊ฐ์ฒด ๊ฐ์ ์ํธ์์ฉ์ ์๊ฐํํ๊ธฐ ์ํด ์ํ์ค ๋ค์ด์ด๊ทธ๋จ์ ์ฌ์ฉํ์ต๋๋ค.
+
+```mermaid
+sequenceDiagram
+ participant U as ์ฌ์ฉ์
+ participant I as InputView
+ participant O as OutputView
+ participant C as LottoController
+ participant M as Money
+ participant F as LottoFactory
+ participant G as LottoGenerator
+ participant L as Lotto
+ participant W as WinningNumbers
+ participant R as LottoResults
+
+Note over U,C: ๐ซ 1๋จ๊ณ: ๋ก๋ ๊ตฌ๋งค
+ loop ์ ํจํ ๊ธ์ก์ด ์
๋ ฅ๋ ๋๊น์ง
+ U->>I: ๊ตฌ์
๊ธ์ก ์
๋ ฅ
+ I->>C: ๊ธ์ก ์ ๋ฌ
+ C->>M: Money ์์ฑ ๋ฐ ๊ฒ์ฆ
+ alt ๊ฒ์ฆ ์ฑ๊ณต
+ M-->>C: ์ ํจํ Money ๊ฐ์ฒด
+ else ๊ฒ์ฆ ์คํจ
+ M-->>C: IllegalArgumentException
+ C->>O: ์๋ฌ ๋ฉ์์ง ์ถ๋ ฅ
+ O->>U: ์๋ฌ ํ์
+ end
+ end
+
+ C->>M: getLottoCount() ํธ์ถ
+ M-->>C: ๊ตฌ๋งค ๊ฐ๋ฅํ ๋ก๋ ๊ฐ์(count)
+
+ C->>F: generate(count) ํธ์ถ
+ F->>G: generate(count) ์์
+
+ loop count๋ฒ
+ G->>G: Randoms.pickUniqueNumbersInRange(1,45,6)
+ G->>L: Lotto(numbers) ์์ฑ
+ end
+ G-->>F: List ๋ฐํ
+ F-->>C: List ๋ฐํ
+
+ C->>O: ๊ตฌ๋งคํ ๋ก๋ ๋ชฉ๋ก ์ถ๋ ฅ
+ O->>U: ๋ก๋ ๋ชฉ๋ก ํ์
+
+Note over U,C: ๐ 2๋จ๊ณ: ๋น์ฒจ ๋ฒํธ ์
๋ ฅ
+ loop ์ ํจํ ๋น์ฒจ ๋ฒํธ๊ฐ ์
๋ ฅ๋ ๋๊น์ง
+ U->>I: ๋น์ฒจ ๋ฒํธ ์
๋ ฅ (์ผํ ๊ตฌ๋ถ)
+ I->>C: ๋น์ฒจ ๋ฒํธ ์ ๋ฌ
+ C->>W: WinningNumbers ์์ฑ ๋ฐ ๊ฒ์ฆ
+ alt ๊ฒ์ฆ ์ฑ๊ณต
+ W-->>C: ๋น์ฒจ ๋ฒํธ ๊ฐ์ฒด ์์ฑ๋จ
+ else ๊ฒ์ฆ ์คํจ
+ W-->>C: IllegalArgumentException
+ C->>O: ์๋ฌ ๋ฉ์์ง ์ถ๋ ฅ
+ O->>U: ์๋ฌ ํ์
+ end
+ end
+
+ loop ์ ํจํ ๋ณด๋์ค ๋ฒํธ๊ฐ ์
๋ ฅ๋ ๋๊น์ง
+ U->>I: ๋ณด๋์ค ๋ฒํธ ์
๋ ฅ
+ I->>C: ๋ณด๋์ค ๋ฒํธ ์ ๋ฌ
+ C->>W: ๋ณด๋์ค ๋ฒํธ ์ค์ ๋ฐ ๊ฒ์ฆ
+ alt ๊ฒ์ฆ ์ฑ๊ณต
+ W-->>C: ์ต์ข
๋น์ฒจ ๋ฒํธ ๊ฐ์ฒด ์์ฑ
+ else ๊ฒ์ฆ ์คํจ
+ W-->>C: IllegalArgumentException
+ C->>O: ์๋ฌ ๋ฉ์์ง ์ถ๋ ฅ
+ O->>U: ์๋ฌ ํ์
+ end
+ end
+ Note over U,C: ๐ 3๋จ๊ณ: ๊ฒฐ๊ณผ ๋ถ์ ๋ฐ ์ถ๋ ฅ
+ C->>R: LottoResults ์์ฑ (List, WinningNumbers)
+ loop ๊ฐ ๋ก๋์ ๋ํด
+ R->>R: ๋ฑ์๋ณ ์นด์ดํธ ์ฆ๊ฐ
+ R->>R: ์ด ์๊ธ ๋์
+ end
+ C->>R: calculateYield(๊ตฌ์
๊ธ์ก) ํธ์ถ
+ R-->>C: ์์ต๋ฅ ๋ฐํ
+ C->>O: ๋น์ฒจ ํต๊ณ ์ถ๋ ฅ
+ O->>U: ํต๊ณ ํ์
+ C->>O: ์์ต๋ฅ ์ถ๋ ฅ
+ O->>U: ์์ต๋ฅ ํ์
+```
+
+
+
+
+## **๐งฑ ๊ฐ์ฒด ์ญํ ์์ฝ**
+
+| ๊ฐ์ฒด ์ด๋ฆ | ์ญํ (์ฑ
์) | ๋ณด์ ๊ฐ(์ํ) | ์ฃผ์ ํ์ |
+|------------------------|---------------------------------------------|---------------------------------|------------------------------------------------------------------|
+| `Money` | ๊ตฌ์
๊ธ์ก์ ํํํ๊ณ ๊ด๋ จ ๊ท์น์ ๊ฒ์ฆํ๋ ๊ฐ ๊ฐ์ฒด | * ๊ตฌ์
๊ธ์ก | - ๊ธ์ก ์ ํจ์ฑ ๊ฒ์ฌ, ๊ตฌ๋งค ๊ฐ๋ฅํ ๋ก๋ ์ ๊ณ์ฐ |
+| `Lotto` | ๋ก๋ ํ ์ฅ์ ํํํ๋ฉฐ ๋ฒํธ ๊ท์น(๊ฐ์, ๋ฒ์, ์ค๋ณต)์ ๋ถ๋ณ์ฑ์ ๋ณด์ฅ | * 6๊ฐ์ ๋ก๋ ๋ฒํธ | - ๋ฒํธ ์ ๋ ฌ ๋ฐ ์ค๋ณต ๊ฒ์ฌ, ๋น์ฒจ ๋ฒํธ์ ์ผ์น ๊ฐ์ ๊ณ์ฐ, ๋ณด๋์ค ํฌํจ ์ฌ๋ถ ํ์ธ |
+| `LottoGenerator` | ๋ก๋ ๋ฒํธ ์์ฑ ์ ๋ต์ ์ถ์ํํ๋ ์ธํฐํ์ด์ค | | - ์์ฒญ๋ ๊ฐ์๋งํผ `Lotto` ๋ฆฌ์คํธ ์์ฑ |
+| `RandomLottoGenerator` | ๋๋ค ์ ๋ต์ผ๋ก ๋ก๋ ๊ท์น์ ๋ง๋ ๋ฒํธ๋ฅผ ์์ฑ | | - `Randoms.pickUniqueNumbersInRange()`๋ฅผ ์ฌ์ฉํด ๋ฒํธ ์์ฑ, `Lotto` ๊ฐ์ฒด ์์ฑ |
+| `LottoFactory` | ๋ก๋ ์์ฑ ์ ๋ต(`LottoGenerator`)์ ์์ฑ ์์ฒญ์ ์์ํ๋ ํฉํ ๋ฆฌ | * `LottoGenerator` ๊ตฌํ์ฒด | - ์์ฑ๊ธฐ null ๊ฒ์ฆ, ์์ฑ ๊ฐ์ ๊ฒ์ฆ, ์์ฑ๊ธฐ์ ๋ก๋ ์์ฑ ์์ |
+| `WinningNumbers` | ๋น์ฒจ ๋ฒํธ์ ๋ณด๋์ค ๋ฒํธ๋ฅผ ๊ฐ์ง๋ฉฐ ๊ด๋ จ ๊ท์น(๊ฐ์, ๋ฒ์, ์ค๋ณต)์ ์ฑ
์์ง | * 6๊ฐ ๋น์ฒจ ๋ฒํธ, 1๊ฐ ๋ณด๋์ค ๋ฒํธ | - ๋น์ฒจ ๋ฒํธ ์ ํจ์ฑ ๊ฒ์ฌ, ๋ณด๋์ค ๋ฒํธ ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ์ค๋ณต ํ์ธ |
+| `LottoRank` | ๋น์ฒจ ๋ฑ์(1~5๋ฑ, ๊ฝ)์ ์กฐ๊ฑด(์ผ์น ๊ฐ์, ๋ณด๋์ค)๊ณผ ์๊ธ์ ์ ์ | * Enum(์ผ์น ๊ฐ์, ๋ณด๋์ค ํฌํจ ์ฌ๋ถ, ๋น์ฒจ ๊ธ์ก) | - ์ผ์น ์/๋ณด๋์ค ํฌํจ ์ฌ๋ถ๋ก ๋ฑ์ ๋ฐํ, ์๊ธ ๋ฐํ |
+| `LottoResults` | ์ ์ฒด ๋น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ์ง๊ณํ๊ณ ์ต์ข
์์ต๋ฅ ์ ๊ณ์ฐ | * ๋ฑ์๋ณ ๋น์ฒจ ํ์, ๋์ ์๊ธ | - ๊ฐ ๋ก๋์ ๋ํ ๋ฑ์ ๊ณ์ฐ, ๋น์ฒจ ๊ฒฐ๊ณผ ๋์ , ์์ต๋ฅ ๊ณ์ฐ |
+| `InputView` | ์ฌ์ฉ์๋ก๋ถํฐ ์
๋ ฅ์ ๋ฐ๋ ๋ชจ๋ ๋ก์ง์ ๋ด๋น | * ์
๋ ฅ ์ ๋์ฌ | - ์ฌ์ฉ์๋ก๋ถํฐ ์
๋ ฅ์ ๋ฐ์, `Console.readLine()` ์ฌ์ฉ |
+| `OutputView` | ์ฌ์ฉ์์๊ฒ ์ ๋ณด๋ฅผ ์ถ๋ ฅํ๋ ๋ชจ๋ ๋ก์ง์ ๋ด๋น | * ์ถ๋ ฅ ์ ๋์ฌ | - ๊ตฌ๋งคํ ๋ก๋ ๋ชฉ๋ก ์ถ๋ ฅ, ๋น์ฒจ ๊ฒฐ๊ณผ ์ถ๋ ฅ, ์์ต๋ฅ ์ถ๋ ฅ |
+| `InputValidator` | ๋ฌธ์์ด ์
๋ ฅ์ ์ซ์/๋ฆฌ์คํธ๋ก ๋ณํํ๊ณ ํ์์ ๊ฒ์ฆํ๋ ์ ํธ๋ฆฌํฐ | - | - ๊ตฌ์
๊ธ์ก/๋น์ฒจ ๋ฒํธ/๋ณด๋์ค ๋ฒํธ ๋ฌธ์์ด ํ์ฑ, ๊ณต๋ฐฑ ๋ฐ ์ซ์ ํ์ ๊ฒ์ฆ |
+| `LottoController` | ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์ฒด ์คํ ํ๋ฆ์ ์กฐ์จ. View์ Domain ๊ฐ์ฒด๋ค์ ์ฐ๊ฒฐ | | - ์ ์ฒด ํ๋ฆ ์ ์ด |
+
+
+
+
+## **๐ฐ ๊ตฌํ ์์ (Inside-Out ๋ฐฉ์)**
+
+### **1๋จ๊ณ: ํต์ฌ ๋๋ฉ์ธ ๋ชจ๋ธ ๊ตฌํ**
+
+> ๋น์ฆ๋์ค์ ๊ฐ์ฅ ๋ณธ์ง์ ์ธ ๊ท์น๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ ์ํ๊ณ ํ
์คํธํฉ๋๋ค.
+>
+
+- `Money`: ๊ธ์ก์ ํํํ๊ณ , 1,000์ ๋จ์ ๊ฒ์ฆ ๋ฐ ๋ก๋ ๊ตฌ๋งค ๊ฐ๋ฅ ๊ฐ์ ๊ณ์ฐ ์ฑ
์์ ๊ฐ์ง๋๋ค.
+- `Lotto`: ๋ก๋ ํ ์ฅ(6๊ฐ ๋ฒํธ)์ ํํํ๋ฉฐ, ๋ฒํธ ๊ฐ์, ๋ฒ์, ์ค๋ณต ๋ฑ ๊ท์น์ ๋ถ๋ณ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
+
+### **2๋จ๊ณ: ๋น์ฒจ ๊ท์น ๋ฐ ํ์ ๋ก์ง ๊ตฌํ**
+
+> ๋น์ฒจ์ ๊ฒฐ์ ํ๋ ๊ธฐ์ค๊ณผ ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
+>
+
+- `WinningNumbers`: ๋น์ฒจ ๋ฒํธ(6๊ฐ)์ ๋ณด๋์ค ๋ฒํธ(1๊ฐ)๋ฅผ ๊ฐ์ง๋ฉฐ, ๊ด๋ จ๋ ์ ํจ์ฑ ๊ฒ์ฆ ์ฑ
์์ ๊ฐ์ง๋๋ค.
+- `LottoRank`: 5๊ฐ ๋ฑ์์ '๊ฝ'์ ๋ํ ์กฐ๊ฑด(์ผ์น ๊ฐ์, ๋ณด๋์ค ์ฌ๋ถ)๊ณผ ์๊ธ์ `enum`์ผ๋ก ์ ์ํฉ๋๋ค. **(๋ฑ์ ํ์ ๋ก์ง ํฌํจ)**
+
+### **3๋จ๊ณ: ๊ฒฐ๊ณผ ์ง๊ณ ๋ฐ ๋ถ์ ๋ก์ง ๊ตฌํ**
+
+> ๊ตฌ๋งคํ ๋ก๋๋ค์ ๊ฒฐ๊ณผ๋ฅผ ์ข
ํฉํ์ฌ ํต๊ณ๋ฅผ ๋ด๋ ์ฑ
์์ ๊ตฌํํฉ๋๋ค.
+>
+
+- `LottoResults`: ๊ตฌ๋งคํ ๋ชจ๋ `Lotto`์ `WinningNumbers`๋ฅผ ๋น๊ตํ์ฌ, ๋ฑ์๋ณ ๋น์ฒจ ํ์๋ฅผ ์ง๊ณํ๊ณ ์ต์ข
์์ต๋ฅ ์ ๊ณ์ฐํฉ๋๋ค.
+
+### **4๋จ๊ณ: ๋ก๋ ์์ฑ ๋ก์ง ๊ตฌํ**
+
+> ๋๋ฉ์ธ ๊ท์น์ ๋ฐ๋ผ ๋ก๋๋ฅผ ์์ฑํ๋ ์ฑ
์์ ๊ตฌํํฉ๋๋ค.
+>
+
+- `LottoFactory`: `LottoGenerator`๋ฅผ ์ฌ์ฉํด ๊ท์น์ ๋ง๋ ๋ก๋ ๋ชฉ๋ก์ ์์ฑํฉ๋๋ค.
+
+### **5๋จ๊ณ: ์
์ถ๋ ฅ(UI) ๊ณ์ธต ๊ตฌํ**
+
+> ์ฌ์ฉ์์ ์ํธ์์ฉํ๋ ๋ถ๋ถ์ ๊ตฌํํฉ๋๋ค.
+>
+
+- `InputView`: ์ฌ์ฉ์๋ก๋ถํฐ ๊ตฌ์
๊ธ์ก, ๋น์ฒจ ๋ฒํธ, ๋ณด๋์ค ๋ฒํธ๋ฅผ ์
๋ ฅ๋ฐ๋ ์ฑ
์์ ๊ฐ์ง๋๋ค.
+- `OutputView`: ๊ตฌ๋งค ๋ด์ญ, ๋น์ฒจ ํต๊ณ, ์์ต๋ฅ , ์๋ฌ ๋ฉ์์ง ๋ฑ ๋ชจ๋ ์ถ๋ ฅ ๋ด์ฉ์ ํ์์ ๋ง๊ฒ ๋ณด์ฌ์ฃผ๋ ์ฑ
์์ ๊ฐ์ง๋๋ค.
+- `InputValidator`: `InputView`๋ฅผ ํตํด ๋ค์ด์จ ์ฌ์ฉ์ ์
๋ ฅ ๊ฐ์ ํ์(์ซ์์ธ์ง, ์ผํ๋ก ๊ตฌ๋ถ๋๋์ง ๋ฑ)์ ๊ฒ์ฆํฉ๋๋ค.
+
+### **6๋จ๊ณ: ์ ํ๋ฆฌ์ผ์ด์
ํตํฉ ๋ฐ ์คํ**
+
+> ๊ตฌํ๋ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ์กฐ๋ฆฝํ์ฌ ํ๋ก๊ทธ๋จ์ ์์ฑํฉ๋๋ค.
+>
+
+- `LottoController`: ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์ ์คํ ํ๋ฆ(๋ก๋ ๊ตฌ๋งค โ ๋น์ฒจ ๋ฒํธ ์
๋ ฅ โ ๊ฒฐ๊ณผ ๋ฐํ)์ ์ ์ดํ๊ณ , ์์ธ ๋ฐ์ ์ ์ฌ์
๋ ฅ ๋ก์ง์ ์ฒ๋ฆฌํฉ๋๋ค.
+- `Application`: ํ๋ก๊ทธ๋จ์ ์์์ (`main` ๋ฉ์๋)์ผ๋ก, `LottoController`๋ฅผ ์์ฑํ๊ณ ์คํํฉ๋๋ค.
+
+### **7๋จ๊ณ: ๋ฆฌํฉํ ๋ง ๋ฐ ์ต์ข
๊ฒํ **
+
+> ์ฝ๋ ํ์ง์ ๊ฐ์ ํ๊ณ ์๊ตฌ์ฌํญ์ ์ต์ข
์ ๊ฒํฉ๋๋ค.
+>
+
+- `ValidationUtil`, `Retry` ๋ฑ ์ค๋ณต ๋ก์ง์ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ก ์ถ์ถํฉ๋๋ค.
+- ์ ์ฒด ์ฝ๋์ ๋ํด ํ๋ก๊ทธ๋๋ฐ ์ ์ฝ ์กฐ๊ฑด(๋ค์ฌ์ฐ๊ธฐ, ๋ฉ์๋ ๊ธธ์ด ๋ฑ) ์ค์ ์ฌ๋ถ๋ฅผ ๊ฒํ ํฉ๋๋ค.
+
+
+
+
+## ๐ ์์ธ ์ฒ๋ฆฌ
+
+์ฌ์ฉ์์ ์
๋ ฅ ์ค๋ฅ๋ก ์์ธ๊ฐ ๋ฐ์ํ๋ฉด **`[ERROR] ...` ๋ก ์์ํ๋ ์๋ฌ ๋ฉ์์ง๋ฅผ ์ฌ์ฉ์์๊ฒ ์ถ๋ ฅํ๊ณ ํด๋น ์
๋ ฅ๋ถํฐ ๋ค์ ์
๋ ฅ์ ์งํํ ์ ์๋๋ก ํฉ๋๋ค.**
+
+| ์์ธ ์ํฉ | ์๋ฌ ๋ฉ์์ง |
+|----------------------------|-------------------------------------------|
+| ๋น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํ ๋ก๋ ๋ชฉ๋ก์ด null์ธ ๊ฒฝ์ฐ | `[ERROR] ๋น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํ ๋ก๋ ๋ชฉ๋ก์ null์ผ ์ ์์ต๋๋ค.` |
+| ๋ก๋ ๋ชฉ๋ก์ null์ด ํฌํจ๋ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ๋ชฉ๋ก์ null์ด ํฌํจ๋ ์ ์์ต๋๋ค.` |
+| ๋น์ฒจ ๋ฒํธ๊ฐ null์ธ ๊ฒฝ์ฐ | `[ERROR] ๋น์ฒจ ๋ฒํธ๋ null์ผ ์ ์์ต๋๋ค.` |
+| ์์ต๋ฅ ๊ณ์ฐ์ ๊ตฌ์
๊ธ์ก์ด 0 ์ดํ์ธ ๊ฒฝ์ฐ | `[ERROR] ๊ตฌ์
๊ธ์ก์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.` |
+| ๊ตฌ์
๊ธ์ก์ด 0 ์ดํ์ธ ๊ฒฝ์ฐ | `[ERROR] ๊ตฌ์
๊ธ์ก์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.` |
+| ๊ตฌ์
๊ธ์ก์ด 1,000์ ๋จ์๊ฐ ์๋ ๊ฒฝ์ฐ | `[ERROR] 1,000์ ๋จ์๋ก ์
๋ ฅํด ์ฃผ์ธ์.` |
+| ๋ก๋ ๋ฒํธ ๋ฆฌ์คํธ๊ฐ null์ธ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ๋ฒํธ๋ null์ผ ์ ์์ต๋๋ค.` |
+| ๋น๊ตํ ๋ก๋ ๋ฒํธ ๋ฆฌ์คํธ๊ฐ null์ธ ๊ฒฝ์ฐ | `[ERROR] ๋น๊ตํ ๋ก๋๊ฐ null์ผ ์ ์์ต๋๋ค.` |
+| ๋ก๋ ๋ฒํธ๊ฐ 6๊ฐ๊ฐ ์๋ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ๋ฒํธ๋ 6๊ฐ์ฌ์ผ ํฉ๋๋ค.` |
+| ๋ก๋ ๋ฒํธ์ ์ค๋ณต์ด ์๋ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ๋ฒํธ๋ ์ค๋ณต๋ ์ ์์ต๋๋ค.` |
+| ๋ก๋ ๋ฒํธ๊ฐ 1~45 ๋ฒ์๋ฅผ ๋ฒ์ด๋๋ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ๋ฒํธ๋ 1๋ถํฐ 45 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค.` |
+| ๋ณด๋์ค ๋ฒํธ๊ฐ 1~45 ๋ฒ์๋ฅผ ๋ฒ์ด๋ ๊ฒฝ์ฐ | `[ERROR] ๋ณด๋์ค ๋ฒํธ๋ 1๋ถํฐ 45 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค.` |
+| ๋ณด๋์ค ๋ฒํธ๊ฐ ๋น์ฒจ ๋ฒํธ์ ์ค๋ณต๋๋ ๊ฒฝ์ฐ | `[ERROR] ๋ณด๋์ค ๋ฒํธ๋ ๋น์ฒจ ๋ฒํธ์ ์ค๋ณต๋ ์ ์์ต๋๋ค.` |
+| ๋ก๋ ์์ฑ๊ธฐ๊ฐ null์ธ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ์์ฑ๊ธฐ๋ null์ผ ์ ์์ต๋๋ค.` |
+| ๋ก๋ ์์ฑ ๊ฐ์๊ฐ 0 ์ดํ์ธ ๊ฒฝ์ฐ | `[ERROR] ๋ก๋ ์์ฑ ๊ฐ์๋ 1๊ฐ ์ด์์ด์ด์ผ ํฉ๋๋ค.` |
+| ์
๋ ฅ ๋ฌธ์์ด์ด null ๋๋ ๊ณต๋ฐฑ์ธ ๊ฒฝ์ฐ | `[ERROR] ์
๋ ฅ์ ๋น์ด ์์ ์ ์์ต๋๋ค.` |
+| ๊ตฌ์
๊ธ์ก ์
๋ ฅ์ด ์ซ์๊ฐ ์๋ ๊ฒฝ์ฐ | `[ERROR] ๊ตฌ์
๊ธ์ก์ ์ซ์์ฌ์ผ ํฉ๋๋ค.` |
+| ๋น์ฒจ ๋ฒํธ ์
๋ ฅ์ ์ซ์๊ฐ ์๋ ๊ฐ์ด ํฌํจ๋ ๊ฒฝ์ฐ | `[ERROR] ๋น์ฒจ ๋ฒํธ๋ ์ซ์์ฌ์ผ ํฉ๋๋ค.` |
+| ๋ณด๋์ค ๋ฒํธ ์
๋ ฅ์ด ์ซ์๊ฐ ์๋ ๊ฒฝ์ฐ | `[ERROR] ๋ณด๋์ค ๋ฒํธ๋ ์ซ์์ฌ์ผ ํฉ๋๋ค.` |
+
+
+
+
+## โ๏ธ ๊ฐ๋ฐ ๋ฐ ๊ธฐ์ ์ ์ฝ
+
+- ์ด ํ๋ก๊ทธ๋จ์ **Java 21**์์ ์คํ๋์ด์ผ ํ๋ค.
+- ํ๋ก๊ทธ๋จ์ ์์์ ์ **`Application` ํด๋์ค์ `main()`** ๋ฉ์๋์ฌ์ผ ํ๋ค.
+- **์ ๊ณต๋ `build.gradle` ํ์ผ์ ์์ ํ ์ ์๋ค.** ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ์ง ์๋๋ค.
+- ๋ก๋ ๋ฒํธ ์์ฑ์ **`camp.nextstep.edu.missionutils.Randoms.pickUniqueNumbersInRange(1, 45, 6)`** ์ ์ฌ์ฉํ๋ค.
+- ์ฌ์ฉ์ ์
๋ ฅ์ **`camp.nextstep.edu.missionutils.Console.readLine()`** ์ผ๋ก๋ง ๋ฐ๋๋ค.
+- ํ๋ก๊ทธ๋จ ์ข
๋ฃ๋ฅผ ์ํด **`System.exit()`๋ฅผ ํธ์ถํด์๋ ์ ๋๋ค.**
+- ๋ค์ฌ์ฐ๊ธฐ๋ **2๋จ๊ณ ์ด๋ด**๋ก ์ ์งํ๋ค.
+- **`else` ๋ฌธ, `switch/case`, ์ผํญ ์ฐ์ฐ์**๋ ์ฌ์ฉํ์ง ์๋๋ค.
+- ํ๋์ ๋ฉ์๋๋ **ํ ๊ฐ์ง ์ผ๋ง** ํ๋๋ก ์์ฑํ๊ณ , **15์ค์ ๋๊ธฐ์ง ์๋๋ก** ํ๋ค.
+- ์ ๊ณต๋ `Lotto` ํด๋์ค์๋ **ํ๋๋ฅผ ์ถ๊ฐํ์ง ์๋๋ค.**
+
+
+
+
+## ๐ ๊ตฌํ ๊ธฐ๋ก / ๊ณ ๋ฏผ ์ ๋ณด
+
+๊ตฌํ ๊ณผ์ ์์ ๋ฐฐ์ฐ๊ณ ๊ณ ๋ฏผํ๋ ๊ฒ๋ค์ ๊ธฐ๋กํ๋ ๊ณต๊ฐ์
๋๋ค.
+
+- [๋๋ฉ์ธ ์ค๊ณ์ ๊ณ ๋ คํ๋ ๊ฒ](https://github.com/JohnPrk/java-lotto-8/issues/1)
+- [Inside-Out vs Outside-In TDD ์ ํ ์ด์ ](https://github.com/JohnPrk/java-lotto-8/issues/2)
+- [Supplier๋ ๋ฌด์์ธ๊ฐ?](https://github.com/JohnPrk/java-lotto-8/issues/3)
+- [2023๋
๊ตฌํ vs ์ง๊ธ ๊ตฌํ ๋น๊ต[ํ๊ณ ]](https://github.com/JohnPrk/java-lotto-8/issues/4)
+
+
+
diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java
index d190922ba4..14e77be8a8 100644
--- a/src/main/java/lotto/Application.java
+++ b/src/main/java/lotto/Application.java
@@ -1,7 +1,14 @@
package lotto;
+import lotto.controller.LottoController;
+import lotto.domain.LottoFactory;
+import lotto.domain.RandomLottoGenerator;
+
public class Application {
+
public static void main(String[] args) {
- // TODO: ํ๋ก๊ทธ๋จ ๊ตฌํ
+ LottoFactory lottoFactory = new LottoFactory(new RandomLottoGenerator());
+ LottoController controller = new LottoController(lottoFactory);
+ controller.run();
}
}
diff --git a/src/main/java/lotto/Lotto.java b/src/main/java/lotto/Lotto.java
deleted file mode 100644
index 88fc5cf12b..0000000000
--- a/src/main/java/lotto/Lotto.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package lotto;
-
-import java.util.List;
-
-public class Lotto {
- private final List numbers;
-
- public Lotto(List numbers) {
- validate(numbers);
- this.numbers = numbers;
- }
-
- private void validate(List numbers) {
- if (numbers.size() != 6) {
- throw new IllegalArgumentException("[ERROR] ๋ก๋ ๋ฒํธ๋ 6๊ฐ์ฌ์ผ ํฉ๋๋ค.");
- }
- }
-
- // TODO: ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ
-}
diff --git a/src/main/java/lotto/controller/LottoController.java b/src/main/java/lotto/controller/LottoController.java
new file mode 100644
index 0000000000..5864bd78c5
--- /dev/null
+++ b/src/main/java/lotto/controller/LottoController.java
@@ -0,0 +1,71 @@
+package lotto.controller;
+
+import lotto.domain.*;
+import lotto.view.InputView;
+import lotto.view.OutputView;
+
+import java.util.List;
+
+public class LottoController {
+
+ private final LottoFactory lottoFactory;
+
+ public LottoController(LottoFactory lottoFactory) {
+ this.lottoFactory = lottoFactory;
+ }
+
+ public void run() {
+ Money money = readMoney();
+ int lottoCount = money.getLottoCount();
+ List lottos = lottoFactory.generate(lottoCount);
+ OutputView.printLottos(lottos);
+ WinningNumbers winningNumbers = readWinningNumbers();
+ LottoResults results = new LottoResults(lottos, winningNumbers);
+ OutputView.printStatistics(results);
+ double yield = results.calculateYield(money.getAmount());
+ OutputView.printYield(yield);
+ }
+
+ private Money readMoney() {
+ while (true) {
+ try {
+ int purchaseAmount = InputView.readPurchaseAmount();
+ return new Money(purchaseAmount);
+ } catch (IllegalArgumentException e) {
+ OutputView.printError(e.getMessage());
+ }
+ }
+ }
+
+ private WinningNumbers readWinningNumbers() {
+ while (true) {
+ try {
+ List numbers = readWinningNumberList();
+ int bonus = readBonusNumber();
+ return new WinningNumbers(numbers, bonus);
+ } catch (IllegalArgumentException e) {
+ OutputView.printError(e.getMessage());
+ }
+ }
+ }
+
+ private List readWinningNumberList() {
+ while (true) {
+ try {
+ return InputView.readWinningNumbers();
+ } catch (IllegalArgumentException e) {
+ OutputView.printError(e.getMessage());
+ }
+ }
+ }
+
+ private int readBonusNumber() {
+ while (true) {
+ try {
+ return InputView.readBonusNumber();
+ } catch (IllegalArgumentException e) {
+ OutputView.printError(e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/main/java/lotto/domain/Lotto.java b/src/main/java/lotto/domain/Lotto.java
new file mode 100644
index 0000000000..18bdc0ef86
--- /dev/null
+++ b/src/main/java/lotto/domain/Lotto.java
@@ -0,0 +1,78 @@
+package lotto.domain;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class Lotto {
+
+ private static final int LOTTO_SIZE = 6;
+ private static final int MIN_NUMBER = 1;
+ private static final int MAX_NUMBER = 45;
+ private final List numbers;
+
+ public Lotto(List numbers) {
+ validateNotNull(numbers);
+ validateCount(numbers);
+ validateRange(numbers);
+ validateDuplicate(numbers);
+ this.numbers = sortNumbers(numbers);
+ }
+
+ public List getNumbers() {
+ return Collections.unmodifiableList(numbers);
+ }
+
+ public boolean containsNumber(int number) {
+ return numbers.contains(number);
+ }
+
+ public int countMatchingNumbers(Lotto otherLotto) {
+ validateOtherLottoNotNull(otherLotto);
+ Set otherNumbers = new HashSet<>(otherLotto.numbers);
+ return (int) numbers.stream()
+ .filter(otherNumbers::contains)
+ .count();
+ }
+
+ private void validateOtherLottoNotNull(Lotto otherLotto) {
+ if (otherLotto == null) {
+ throw new IllegalArgumentException("[ERROR] ๋น๊ตํ ๋ก๋๊ฐ null์ผ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private void validateNotNull(List numbers) {
+ if (numbers == null) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ๋ฒํธ๋ null์ผ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private void validateCount(List numbers) {
+ if (numbers.size() != LOTTO_SIZE) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ๋ฒํธ๋ 6๊ฐ์ฌ์ผ ํฉ๋๋ค.");
+ }
+ }
+
+ private void validateRange(List numbers) {
+ for (int number : numbers) {
+ if (number < MIN_NUMBER || number > MAX_NUMBER) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ๋ฒํธ๋ 1๋ถํฐ 45 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค.");
+ }
+ }
+ }
+
+ private void validateDuplicate(List numbers) {
+ Set uniqueNumbers = new HashSet<>(numbers);
+ if (uniqueNumbers.size() != LOTTO_SIZE) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ๋ฒํธ๋ ์ค๋ณต๋ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private List sortNumbers(List numbers) {
+ return numbers.stream()
+ .sorted()
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/lotto/domain/LottoFactory.java b/src/main/java/lotto/domain/LottoFactory.java
new file mode 100644
index 0000000000..8315a0c24b
--- /dev/null
+++ b/src/main/java/lotto/domain/LottoFactory.java
@@ -0,0 +1,30 @@
+package lotto.domain;
+
+import java.util.List;
+
+public class LottoFactory {
+
+ private final LottoGenerator generator;
+
+ public LottoFactory(LottoGenerator generator) {
+ validateGenerator(generator);
+ this.generator = generator;
+ }
+
+ public List generate(int count) {
+ validateCount(count);
+ return generator.generate(count);
+ }
+
+ private void validateGenerator(LottoGenerator generator) {
+ if (generator == null) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ์์ฑ๊ธฐ๋ null์ผ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private void validateCount(int count) {
+ if (count <= 0) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ์์ฑ ๊ฐ์๋ 1๊ฐ ์ด์์ด์ด์ผ ํฉ๋๋ค.");
+ }
+ }
+}
diff --git a/src/main/java/lotto/domain/LottoGenerator.java b/src/main/java/lotto/domain/LottoGenerator.java
new file mode 100644
index 0000000000..d3e9241185
--- /dev/null
+++ b/src/main/java/lotto/domain/LottoGenerator.java
@@ -0,0 +1,9 @@
+package lotto.domain;
+
+import java.util.List;
+
+public interface LottoGenerator {
+
+ List generate(int count);
+
+}
diff --git a/src/main/java/lotto/domain/LottoRank.java b/src/main/java/lotto/domain/LottoRank.java
new file mode 100644
index 0000000000..d2496e1afe
--- /dev/null
+++ b/src/main/java/lotto/domain/LottoRank.java
@@ -0,0 +1,37 @@
+package lotto.domain;
+
+import java.util.Arrays;
+
+public enum LottoRank {
+
+ FIRST(6, false, 2_000_000_000L),
+ SECOND(5, true, 30_000_000L),
+ THIRD(5, false, 1_500_000L),
+ FOURTH(4, false, 50_000L),
+ FIFTH(3, false, 5_000L),
+ NONE(0, false, 0L);
+
+ private final int matchCount;
+ private final boolean requiresBonus;
+ private final long prize;
+
+ LottoRank(int matchCount, boolean requiresBonus, long prize) {
+ this.matchCount = matchCount;
+ this.requiresBonus = requiresBonus;
+ this.prize = prize;
+ }
+
+ public long getPrize() {
+ return prize;
+ }
+
+ public static LottoRank from(int matchCount, boolean bonusMatched) {
+ if (matchCount == 5) {
+ return bonusMatched ? SECOND : THIRD;
+ }
+ return Arrays.stream(values())
+ .filter(rank -> rank.matchCount == matchCount)
+ .findFirst()
+ .orElse(NONE);
+ }
+}
diff --git a/src/main/java/lotto/domain/LottoResults.java b/src/main/java/lotto/domain/LottoResults.java
new file mode 100644
index 0000000000..9a512b497c
--- /dev/null
+++ b/src/main/java/lotto/domain/LottoResults.java
@@ -0,0 +1,71 @@
+package lotto.domain;
+
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+public class LottoResults {
+
+ private final Map results = new EnumMap<>(LottoRank.class);
+ private long totalPrize;
+
+ public LottoResults(List lottos, WinningNumbers winningNumbers) {
+ validateLottos(lottos);
+ validateLottoElements(lottos);
+ validateWinningNumbers(winningNumbers);
+ countResults(lottos, winningNumbers);
+ }
+
+ public int getCountOf(LottoRank rank) {
+ return results.getOrDefault(rank, 0);
+ }
+
+ public long getTotalPrize() {
+ return totalPrize;
+ }
+
+ public double calculateYield(long purchaseAmount) {
+ if (purchaseAmount <= 0) {
+ throw new IllegalArgumentException("[ERROR] ๊ตฌ์
๊ธ์ก์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.");
+ }
+ return (double) totalPrize / purchaseAmount;
+ }
+
+ private void validateLottos(List lottos) {
+ if (lottos == null) {
+ throw new IllegalArgumentException("[ERROR] ๋น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํ ๋ก๋ ๋ชฉ๋ก์ null์ผ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private void validateLottoElements(List lottos) {
+ for (Lotto lotto : lottos) {
+ if (lotto == null) {
+ throw new IllegalArgumentException("[ERROR] ๋ก๋ ๋ชฉ๋ก์ null์ด ํฌํจ๋ ์ ์์ต๋๋ค.");
+ }
+ }
+ }
+
+ private void validateWinningNumbers(WinningNumbers winningNumbers) {
+ if (winningNumbers == null) {
+ throw new IllegalArgumentException("[ERROR] ๋น์ฒจ ๋ฒํธ๋ null์ผ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private void countResults(List lottos, WinningNumbers winningNumbers) {
+ for (Lotto lotto : lottos) {
+ LottoRank rank = toRank(lotto, winningNumbers);
+ incrementRankCount(rank);
+ totalPrize += rank.getPrize();
+ }
+ }
+
+ private LottoRank toRank(Lotto lotto, WinningNumbers winningNumbers) {
+ int matchCount = winningNumbers.countMatchingNumbers(lotto);
+ boolean bonusMatched = winningNumbers.containsBonusNumber(lotto);
+ return LottoRank.from(matchCount, bonusMatched);
+ }
+
+ private void incrementRankCount(LottoRank rank) {
+ results.put(rank, results.getOrDefault(rank, 0) + 1);
+ }
+}
diff --git a/src/main/java/lotto/domain/Money.java b/src/main/java/lotto/domain/Money.java
new file mode 100644
index 0000000000..257fdfee61
--- /dev/null
+++ b/src/main/java/lotto/domain/Money.java
@@ -0,0 +1,33 @@
+package lotto.domain;
+
+public class Money {
+
+ private static final int UNIT_MONEY_FOR_BUY_LOTTO = 1000;
+ private final int amount;
+
+ public Money(int amount) {
+ validatePositive(amount);
+ validateUnit(amount);
+ this.amount = amount;
+ }
+
+ public int getAmount() {
+ return amount;
+ }
+
+ private void validatePositive(int amount) {
+ if (amount <= 0) {
+ throw new IllegalArgumentException("[ERROR] ๊ตฌ์
๊ธ์ก์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.");
+ }
+ }
+
+ private void validateUnit(int amount) {
+ if (amount % UNIT_MONEY_FOR_BUY_LOTTO != 0) {
+ throw new IllegalArgumentException("[ERROR] 1,000์ ๋จ์๋ก ์
๋ ฅํด ์ฃผ์ธ์.");
+ }
+ }
+
+ public int getLottoCount() {
+ return amount / UNIT_MONEY_FOR_BUY_LOTTO;
+ }
+}
diff --git a/src/main/java/lotto/domain/RandomLottoGenerator.java b/src/main/java/lotto/domain/RandomLottoGenerator.java
new file mode 100644
index 0000000000..4897ff372b
--- /dev/null
+++ b/src/main/java/lotto/domain/RandomLottoGenerator.java
@@ -0,0 +1,23 @@
+package lotto.domain;
+
+import camp.nextstep.edu.missionutils.Randoms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RandomLottoGenerator implements LottoGenerator {
+
+ private static final int LOTTO_NUMBER_MIN = 1;
+ private static final int LOTTO_NUMBER_MAX = 45;
+ private static final int LOTTO_SIZE = 6;
+
+ @Override
+ public List generate(int count) {
+ List lottos = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ List numbers = Randoms.pickUniqueNumbersInRange(LOTTO_NUMBER_MIN, LOTTO_NUMBER_MAX, LOTTO_SIZE);
+ lottos.add(new Lotto(numbers));
+ }
+ return lottos;
+ }
+}
diff --git a/src/main/java/lotto/domain/WinningNumbers.java b/src/main/java/lotto/domain/WinningNumbers.java
new file mode 100644
index 0000000000..b90dc0b219
--- /dev/null
+++ b/src/main/java/lotto/domain/WinningNumbers.java
@@ -0,0 +1,45 @@
+package lotto.domain;
+
+import java.util.List;
+
+public class WinningNumbers {
+
+ private final Lotto winningLotto;
+ private final int bonusNumber;
+
+ public WinningNumbers(List winningNumbers, int bonusNumber) {
+ Lotto lotto = new Lotto(winningNumbers);
+ validateBonusNumber(lotto, bonusNumber);
+ this.winningLotto = lotto;
+ this.bonusNumber = bonusNumber;
+ }
+
+ public int getBonusNumber() {
+ return bonusNumber;
+ }
+
+ public int countMatchingNumbers(Lotto lotto) {
+ return winningLotto.countMatchingNumbers(lotto);
+ }
+
+ public boolean containsBonusNumber(Lotto lotto) {
+ return lotto.containsNumber(bonusNumber);
+ }
+
+ private void validateBonusNumber(Lotto winningLotto, int bonusNumber) {
+ validateBonusRange(bonusNumber);
+ validateBonusDuplicate(winningLotto, bonusNumber);
+ }
+
+ private void validateBonusRange(int bonusNumber) {
+ if (bonusNumber < 1 || bonusNumber > 45) {
+ throw new IllegalArgumentException("[ERROR] ๋ณด๋์ค ๋ฒํธ๋ 1๋ถํฐ 45 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค.");
+ }
+ }
+
+ private void validateBonusDuplicate(Lotto winningLotto, int bonusNumber) {
+ if (winningLotto.containsNumber(bonusNumber)) {
+ throw new IllegalArgumentException("[ERROR] ๋ณด๋์ค ๋ฒํธ๋ ๋น์ฒจ ๋ฒํธ์ ์ค๋ณต๋ ์ ์์ต๋๋ค.");
+ }
+ }
+}
diff --git a/src/main/java/lotto/view/InputValidator.java b/src/main/java/lotto/view/InputValidator.java
new file mode 100644
index 0000000000..17c772e86b
--- /dev/null
+++ b/src/main/java/lotto/view/InputValidator.java
@@ -0,0 +1,54 @@
+package lotto.view;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class InputValidator {
+
+ private static final String NUMBER_DELIMITER = ",";
+
+ private InputValidator() {
+ }
+
+ public static int parsePurchaseAmount(String input) {
+ validateNotBlank(input);
+ String trimmed = input.trim();
+ try {
+ return Integer.parseInt(trimmed);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("[ERROR] ๊ตฌ์
๊ธ์ก์ ์ซ์์ฌ์ผ ํฉ๋๋ค.");
+ }
+ }
+
+ public static List parseWinningNumbers(String input) {
+ validateNotBlank(input);
+ String[] parts = input.split(NUMBER_DELIMITER);
+ return Arrays.stream(parts)
+ .map(String::trim)
+ .map(part -> parseNumber(part, "[ERROR] ๋น์ฒจ ๋ฒํธ๋ ์ซ์์ฌ์ผ ํฉ๋๋ค."))
+ .toList();
+ }
+
+ public static int parseBonusNumber(String input) {
+ validateNotBlank(input);
+ String trimmed = input.trim();
+ return parseNumber(trimmed, "[ERROR] ๋ณด๋์ค ๋ฒํธ๋ ์ซ์์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ private static void validateNotBlank(String input) {
+ if (input == null) {
+ throw new IllegalArgumentException("[ERROR] ์
๋ ฅ์ ๋น์ด ์์ ์ ์์ต๋๋ค.");
+ }
+ if (input.trim().isEmpty()) {
+ throw new IllegalArgumentException("[ERROR] ์
๋ ฅ์ ๋น์ด ์์ ์ ์์ต๋๋ค.");
+ }
+ }
+
+ private static int parseNumber(String value, String errorMessage) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(errorMessage);
+ }
+ }
+}
diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java
new file mode 100644
index 0000000000..6d48cbfb6f
--- /dev/null
+++ b/src/main/java/lotto/view/InputView.java
@@ -0,0 +1,29 @@
+package lotto.view;
+
+import camp.nextstep.edu.missionutils.Console;
+
+import java.util.List;
+
+public class InputView {
+
+ public static int readPurchaseAmount() {
+ System.out.println("๊ตฌ์
๊ธ์ก์ ์
๋ ฅํด ์ฃผ์ธ์.");
+ String input = Console.readLine();
+ System.out.println();
+ return InputValidator.parsePurchaseAmount(input);
+ }
+
+ public static List readWinningNumbers() {
+ System.out.println("๋น์ฒจ ๋ฒํธ๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.");
+ String input = Console.readLine();
+ System.out.println();
+ return InputValidator.parseWinningNumbers(input);
+ }
+
+ public static int readBonusNumber() {
+ System.out.println("๋ณด๋์ค ๋ฒํธ๋ฅผ ์
๋ ฅํด ์ฃผ์ธ์.");
+ String input = Console.readLine();
+ System.out.println();
+ return InputValidator.parseBonusNumber(input);
+ }
+}
diff --git a/src/main/java/lotto/view/OutputView.java b/src/main/java/lotto/view/OutputView.java
new file mode 100644
index 0000000000..60569371a9
--- /dev/null
+++ b/src/main/java/lotto/view/OutputView.java
@@ -0,0 +1,41 @@
+package lotto.view;
+
+import lotto.domain.Lotto;
+import lotto.domain.LottoRank;
+import lotto.domain.LottoResults;
+
+import java.util.List;
+
+public class OutputView {
+
+ public static void printLottos(List lottos) {
+ System.out.printf("%d๊ฐ๋ฅผ ๊ตฌ๋งคํ์ต๋๋ค.%n", lottos.size());
+ for (Lotto lotto : lottos) {
+ System.out.println(lotto.getNumbers());
+ }
+ }
+
+ public static void printStatistics(LottoResults results) {
+ System.out.println("๋น์ฒจ ํต๊ณ");
+ System.out.println("---");
+ printRankResult(results, LottoRank.FIFTH, "3๊ฐ ์ผ์น (5,000์) - %d๊ฐ");
+ printRankResult(results, LottoRank.FOURTH, "4๊ฐ ์ผ์น (50,000์) - %d๊ฐ");
+ printRankResult(results, LottoRank.THIRD, "5๊ฐ ์ผ์น (1,500,000์) - %d๊ฐ");
+ printRankResult(results, LottoRank.SECOND, "5๊ฐ ์ผ์น, ๋ณด๋์ค ๋ณผ ์ผ์น (30,000,000์) - %d๊ฐ");
+ printRankResult(results, LottoRank.FIRST, "6๊ฐ ์ผ์น (2,000,000,000์) - %d๊ฐ");
+ }
+
+ private static void printRankResult(LottoResults results, LottoRank rank, String format) {
+ int count = results.getCountOf(rank);
+ System.out.printf(format + "%n", count);
+ }
+
+ public static void printYield(double yield) {
+ double percentage = yield * 100;
+ System.out.printf("์ด ์์ต๋ฅ ์ %.1f%%์
๋๋ค.%n", percentage);
+ }
+
+ public static void printError(String message) {
+ System.out.println(message);
+ }
+}
diff --git a/src/test/java/lotto/LottoTest.java b/src/test/java/lotto/LottoTest.java
deleted file mode 100644
index 309f4e50ae..0000000000
--- a/src/test/java/lotto/LottoTest.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package lotto;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-class LottoTest {
- @Test
- void ๋ก๋_๋ฒํธ์_๊ฐ์๊ฐ_6๊ฐ๊ฐ_๋์ด๊ฐ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
- assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 6, 7)))
- .isInstanceOf(IllegalArgumentException.class);
- }
-
- @DisplayName("๋ก๋ ๋ฒํธ์ ์ค๋ณต๋ ์ซ์๊ฐ ์์ผ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.")
- @Test
- void ๋ก๋_๋ฒํธ์_์ค๋ณต๋_์ซ์๊ฐ_์์ผ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
- assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5, 5)))
- .isInstanceOf(IllegalArgumentException.class);
- }
-
- // TODO: ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ์ ๋ฐ๋ฅธ ํ
์คํธ ์ฝ๋ ์์ฑ
-}
diff --git a/src/test/java/lotto/domain/FakeLottoGeneratorTest.java b/src/test/java/lotto/domain/FakeLottoGeneratorTest.java
new file mode 100644
index 0000000000..13a66540ee
--- /dev/null
+++ b/src/test/java/lotto/domain/FakeLottoGeneratorTest.java
@@ -0,0 +1,62 @@
+package lotto.domain;
+
+import lotto.domain.testDouble.FakeLottoGenerator;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class FakeLottoGeneratorTest {
+
+ @Test
+ void ์ค๋น๋_๋ฒํธ_์ํ์ค๋ฅผ_์ฌ์ฉํด_์์ฒญํ_๊ฐ์๋งํผ_๋ก๋๋ฅผ_์์ฑํ๋ค() {
+
+ // given
+ List> numbersSequences = List.of(
+ List.of(1, 2, 3, 4, 5, 6),
+ List.of(7, 8, 9, 10, 11, 12)
+ );
+ LottoGenerator generator = new FakeLottoGenerator(numbersSequences);
+
+ // when
+ List lottos = generator.generate(2);
+
+ // then
+ assertThat(lottos).hasSize(2);
+ assertThat(lottos.get(0).getNumbers()).containsExactly(1, 2, 3, 4, 5, 6);
+ assertThat(lottos.get(1).getNumbers()).containsExactly(7, 8, 9, 10, 11, 12);
+ }
+
+ @Test
+ void ์ค๋น๋_๋ฒํธ๋ณด๋ค_๋ง์_๊ฐ์๋ฅผ_์์ฒญํ๋ฉด_NoSuchElementException์ด_๋ฐ์ํ๋ค() {
+
+ // given
+ List> numbersSequences = List.of(
+ List.of(1, 2, 3, 4, 5, 6)
+ );
+ LottoGenerator generator = new FakeLottoGenerator(numbersSequences);
+
+ // when & then
+ assertThatThrownBy(() -> generator.generate(2))
+ .isInstanceOf(NoSuchElementException.class);
+ }
+
+ @Test
+ void of_์ ์ _ํฉํฐ๋ฆฌ_๋ฉ์๋๋_๋จ์ผ_๋ฒํธ_์ธํธ๋ก_๋ก๋๋ฅผ_์์ฑํ๋ค() {
+
+ // given
+ FakeLottoGenerator generator = FakeLottoGenerator.of(
+ List.of(1, 2, 3, 4, 5, 6)
+ );
+
+ // when
+ List lottos = generator.generate(1);
+
+ // then
+ assertThat(lottos).hasSize(1);
+ assertThat(lottos.get(0).getNumbers()).containsExactly(1, 2, 3, 4, 5, 6);
+ }
+}
diff --git a/src/test/java/lotto/domain/LottoFactoryTest.java b/src/test/java/lotto/domain/LottoFactoryTest.java
new file mode 100644
index 0000000000..a2f0bd5791
--- /dev/null
+++ b/src/test/java/lotto/domain/LottoFactoryTest.java
@@ -0,0 +1,61 @@
+package lotto.domain;
+
+import lotto.domain.testDouble.FakeLottoGenerator;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class LottoFactoryTest {
+
+ @Test
+ void ์ ํจํ_์์ฑ๊ธฐ์_๊ฐ์๋ก_๋ก๋_๋ชฉ๋ก์_์์ฑํ๋ค() {
+
+ // given
+ FakeLottoGenerator generator = new FakeLottoGenerator(
+ List.of(
+ List.of(1, 2, 3, 4, 5, 6),
+ List.of(7, 8, 9, 10, 11, 12),
+ List.of(13, 14, 15, 16, 17, 18)
+ )
+ );
+ LottoFactory factory = new LottoFactory(generator);
+ int count = 3;
+
+ // when
+ List lottos = factory.generate(count);
+
+ // then
+ assertThat(lottos).hasSize(count);
+ assertThat(lottos).allSatisfy(lotto -> assertThat(lotto).isNotNull());
+ }
+
+ @ParameterizedTest
+ @NullSource
+ void ์์ฑ๊ธฐ๊ฐ_null์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(LottoGenerator generator) {
+
+ // when & then
+ assertThatThrownBy(() -> new LottoFactory(generator))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ์์ฑ๊ธฐ๋ null์ผ ์ ์์ต๋๋ค.");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, -1, -10})
+ void ์์ฑ_๊ฐ์๊ฐ_0_์ดํ์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(int count) {
+
+ // given
+ LottoGenerator generator = FakeLottoGenerator.of(List.of(1, 2, 3, 4, 5, 6));
+ LottoFactory factory = new LottoFactory(generator);
+
+ // when & then
+ assertThatThrownBy(() -> factory.generate(count))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ์์ฑ ๊ฐ์๋ 1๊ฐ ์ด์์ด์ด์ผ ํฉ๋๋ค.");
+ }
+}
diff --git a/src/test/java/lotto/domain/LottoRankTest.java b/src/test/java/lotto/domain/LottoRankTest.java
new file mode 100644
index 0000000000..ffbb85c6a7
--- /dev/null
+++ b/src/test/java/lotto/domain/LottoRankTest.java
@@ -0,0 +1,152 @@
+package lotto.domain;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class LottoRankTest {
+
+ @Test
+ void ์ฌ์ฏ_๊ฐ๊ฐ_๋ชจ๋_์ผ์นํ๋ฉด_1๋ฑ์ด๋ค() {
+
+ // given
+ int matchCount = 6;
+ boolean bonusMatched = false;
+
+ // when
+ LottoRank rank = LottoRank.from(matchCount, bonusMatched);
+
+ // then
+ assertThat(rank).isEqualTo(LottoRank.FIRST);
+ }
+
+ @Test
+ void ๋ค์ฏ_๊ฐ์_๋ณด๋์ค๊น์ง_์ผ์นํ๋ฉด_2๋ฑ์ด๋ค() {
+
+ // given
+ int matchCount = 5;
+ boolean bonusMatched = true;
+
+ // when
+ LottoRank rank = LottoRank.from(matchCount, bonusMatched);
+
+ // then
+ assertThat(rank).isEqualTo(LottoRank.SECOND);
+ }
+
+ @Test
+ void ๋ค์ฏ_๊ฐ๋ง_์ผ์นํ๊ณ _๋ณด๋์ค๋_๋ค๋ฅด๋ฉด_3๋ฑ์ด๋ค() {
+
+ // given
+ int matchCount = 5;
+ boolean bonusMatched = false;
+
+ // when
+ LottoRank rank = LottoRank.from(matchCount, bonusMatched);
+
+ // then
+ assertThat(rank).isEqualTo(LottoRank.THIRD);
+ }
+
+ @Test
+ void ๋ค_๊ฐ๊ฐ_์ผ์นํ๋ฉด_4๋ฑ์ด๋ค() {
+
+ // given
+ int matchCount = 4;
+
+ // when
+ LottoRank rank = LottoRank.from(matchCount, false);
+
+ // then
+ assertThat(rank).isEqualTo(LottoRank.FOURTH);
+ }
+
+ @Test
+ void ์ธ_๊ฐ๊ฐ_์ผ์นํ๋ฉด_5๋ฑ์ด๋ค() {
+
+ // given
+ int matchCount = 3;
+
+ // when
+ LottoRank rank = LottoRank.from(matchCount, false);
+
+ // then
+ assertThat(rank).isEqualTo(LottoRank.FIFTH);
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, 1, 2})
+ void ๋_๊ฐ_์ดํ๋ก_์ผ์นํ๋ฉด_๊ฝ์ด๋ค(int matchCount) {
+
+ // when
+ LottoRank rank = LottoRank.from(matchCount, false);
+
+ // then
+ assertThat(rank).isEqualTo(LottoRank.NONE);
+ }
+
+ @ParameterizedTest
+ @MethodSource("providePrizePerRank")
+ void ๋ฑ์๋ณ_๋น์ฒจ๊ธ์_๋ฐํํ๋ค(LottoRank rank, long expectedPrize) {
+
+ //when
+ long prize = rank.getPrize();
+
+ // then
+ assertThat(prize).isEqualTo(expectedPrize);
+ }
+
+
+ @ParameterizedTest
+ @MethodSource("bonusShouldBeIgnoredCases")
+ void ๋ณด๋์ค๋_๋ค์ฏ_๊ฐ๊ฐ_๋ง์_๋๋ฅผ_์ ์ธํ๊ณ ๋_์ํฅ์_์ฃผ์ง_์๋๋ค(int matchCount) {
+
+ // when
+ LottoRank withBonus = LottoRank.from(matchCount, true);
+ LottoRank withoutBonus = LottoRank.from(matchCount, false);
+
+ // then
+ assertThat(withBonus).isEqualTo(withoutBonus);
+ }
+
+ @Test
+ void ๋ค์ฏ_๊ฐ_์ผ์น_์_๋ณด๋์ค_์ฌ๋ถ์_๋ฐ๋ผ_๋ฑ์๊ฐ_๋ฐ๋๋ค() {
+
+ // when
+ LottoRank withBonus = LottoRank.from(5, true);
+ LottoRank withoutBonus = LottoRank.from(5, false);
+
+ // then
+ assertThat(withBonus).isEqualTo(LottoRank.SECOND);
+ assertThat(withoutBonus).isEqualTo(LottoRank.THIRD);
+ assertThat(withBonus).isNotEqualTo(withoutBonus);
+ }
+
+ private static Stream bonusShouldBeIgnoredCases() {
+ return Stream.of(
+ Arguments.of(0),
+ Arguments.of(1),
+ Arguments.of(2),
+ Arguments.of(3),
+ Arguments.of(4),
+ Arguments.of(6)
+ );
+ }
+
+ private static Stream providePrizePerRank() {
+ return Stream.of(
+ Arguments.of(LottoRank.FIRST, 2_000_000_000L),
+ Arguments.of(LottoRank.SECOND, 30_000_000L),
+ Arguments.of(LottoRank.THIRD, 1_500_000L),
+ Arguments.of(LottoRank.FOURTH, 50_000L),
+ Arguments.of(LottoRank.FIFTH, 5_000L),
+ Arguments.of(LottoRank.NONE, 0L)
+ );
+ }
+}
diff --git a/src/test/java/lotto/domain/LottoResultsTest.java b/src/test/java/lotto/domain/LottoResultsTest.java
new file mode 100644
index 0000000000..fbd85777bf
--- /dev/null
+++ b/src/test/java/lotto/domain/LottoResultsTest.java
@@ -0,0 +1,157 @@
+package lotto.domain;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class LottoResultsTest {
+
+ @Test
+ void ์ฃผ์ด์ง_๋ก๋๋ค๊ณผ_๋น์ฒจ_๋ฒํธ_๋ก๋๋ฅผ_๋น๊ตํ์ฌ_์์๋ฅผ_์ฌ๋ฆฌ๊ณ _์ง๊ณํ _์_์๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ Lotto first = new Lotto(List.of(1, 2, 3, 4, 5, 6));
+ Lotto second = new Lotto(List.of(1, 2, 3, 4, 5, 7));
+ Lotto third = new Lotto(List.of(1, 2, 3, 4, 5, 45));
+ Lotto fourth = new Lotto(List.of(1, 2, 3, 4, 11, 45));
+ Lotto fifth = new Lotto(List.of(1, 2, 3, 7, 11, 45));
+ Lotto none = new Lotto(List.of(10, 11, 12, 13, 14, 15));
+ List lottos = List.of(first, second, third, fourth, fifth, none);
+
+ // when
+ LottoResults results = new LottoResults(lottos, winningNumbers);
+
+ // then
+ assertThat(results.getCountOf(LottoRank.FIRST)).isEqualTo(1);
+ assertThat(results.getCountOf(LottoRank.SECOND)).isEqualTo(1);
+ assertThat(results.getCountOf(LottoRank.THIRD)).isEqualTo(1);
+ assertThat(results.getCountOf(LottoRank.FOURTH)).isEqualTo(1);
+ assertThat(results.getCountOf(LottoRank.FIFTH)).isEqualTo(1);
+ assertThat(results.getCountOf(LottoRank.NONE)).isEqualTo(1);
+ }
+
+ @Test
+ void ์ฃผ์ด์ง_๋ก๋๋ค๊ณผ_๋น์ฒจ_๋ฒํธ_๋ก๋์_์ผ์น_๊ฐ์๊ฐ_2_์ดํ๋ผ๋ฉด_NONE๋ง_์ฆ๊ฐํ๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ List lottos = List.of(
+ new Lotto(List.of(10, 11, 12, 13, 14, 15)),
+ new Lotto(List.of(20, 21, 22, 23, 24, 25)),
+ new Lotto(List.of(1, 2, 22, 23, 24, 25)),
+ new Lotto(List.of(20, 21, 3, 23, 24, 25)),
+ new Lotto(List.of(20, 21, 3, 4, 24, 25)),
+ new Lotto(List.of(20, 21, 10, 11, 5, 25)),
+ new Lotto(List.of(20, 21, 10, 11, 5, 6))
+ );
+
+ // when
+ LottoResults results = new LottoResults(lottos, winningNumbers);
+
+ // then
+ for (LottoRank rank : LottoRank.values()) {
+ if (rank == LottoRank.NONE) {
+ assertThat(results.getCountOf(rank)).isEqualTo(7);
+ continue;
+ }
+ assertThat(results.getCountOf(rank)).isEqualTo(0);
+ }
+ }
+
+ @Test
+ void ๋น์ฒจ๋_๋ก๋๋ค์_์ด_๋น์ฒจ๊ธ์_ํฉ์ฐํ๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ Lotto first = new Lotto(List.of(1, 2, 3, 4, 5, 6));
+ Lotto fifth = new Lotto(List.of(1, 2, 3, 40, 41, 42));
+ List lottos = List.of(first, fifth);
+
+ // when
+ LottoResults results = new LottoResults(lottos, winningNumbers);
+
+ // then
+ assertThat(results.getTotalPrize()).isEqualTo(2_000_005_000L);
+ }
+
+ @Test
+ void ์์ต๋ฅ ์_๊ตฌ์
๊ธ์ก์ผ๋ก_๊ณ์ฐํ๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ Lotto first = new Lotto(List.of(1, 2, 3, 4, 5, 6));
+ Lotto third = new Lotto(List.of(1, 2, 3, 4, 17, 6));
+ List lottos = List.of(first, third);
+ LottoResults results = new LottoResults(lottos, winningNumbers);
+
+ // when
+ double yield = results.calculateYield(lottos.size() * 1000);
+
+ // then
+ assertThat(yield).isEqualTo(2_001_500_000D / 2_000D);
+ }
+
+ @ParameterizedTest
+ @NullSource
+ void ๋ก๋_๋ชฉ๋ก์ด_null์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(List lottos) {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+
+ // when & then
+ assertThatThrownBy(() -> new LottoResults(lottos, winningNumbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋น์ฒจ ๊ฒฐ๊ณผ๋ฅผ ๊ณ์ฐํ ๋ก๋ ๋ชฉ๋ก์ null์ผ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋ก๋_๋ชฉ๋ก์_null์ด_ํฌํจ๋๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ List lottos = new ArrayList<>();
+ lottos.add(new Lotto(List.of(1, 2, 3, 4, 5, 6)));
+ lottos.add(null);
+
+ // when & then
+ assertThatThrownBy(() -> new LottoResults(lottos, winningNumbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ชฉ๋ก์ null์ด ํฌํจ๋ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋น์ฒจ๋ฒํธ๊ฐ_null์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+ // given
+ List lottos = List.of(new Lotto(List.of(1, 2, 3, 4, 5, 6)));
+
+ // when & then
+ assertThatThrownBy(() -> new LottoResults(lottos, null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋น์ฒจ ๋ฒํธ๋ null์ผ ์ ์์ต๋๋ค.");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, -10})
+ void ์์ต๋ฅ _๊ณ์ฐ์_๊ตฌ์
๊ธ์ก์ด_0_์ดํ์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(int purchaseAmount) {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ LottoResults results = new LottoResults(
+ List.of(new Lotto(List.of(1, 2, 3, 4, 5, 6))),
+ winningNumbers
+ );
+
+ // when & then
+ assertThatThrownBy(() -> results.calculateYield(purchaseAmount))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๊ตฌ์
๊ธ์ก์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.");
+ }
+}
diff --git a/src/test/java/lotto/domain/LottoTest.java b/src/test/java/lotto/domain/LottoTest.java
new file mode 100644
index 0000000000..d535420017
--- /dev/null
+++ b/src/test/java/lotto/domain/LottoTest.java
@@ -0,0 +1,177 @@
+package lotto.domain;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.*;
+
+class LottoTest {
+
+ @Test
+ void ๋ก๋_๋ฒํธ๊ฐ_6๊ฐ๋ณด๋ค_๋ง์ผ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5, 6, 7);
+
+ // when & then
+ assertThatThrownBy(() -> new Lotto(numbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ฒํธ๋ 6๊ฐ์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ @Test
+ void ๋ก๋_๋ฒํธ๊ฐ_6๊ฐ๋ณด๋ค_์ ์ผ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5);
+
+ // when & then
+ assertThatThrownBy(() -> new Lotto(numbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ฒํธ๋ 6๊ฐ์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ @Test
+ void ๋ก๋_๋ฒํธ์_์ค๋ณต๋_์ซ์๊ฐ_์์ผ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5, 5);
+
+ // when & then
+ assertThatThrownBy(() -> new Lotto(numbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ฒํธ๋ ์ค๋ณต๋ ์ ์์ต๋๋ค.");
+ }
+
+ @ParameterizedTest()
+ @ValueSource(ints = {0, 46})
+ void ๋ก๋_๋ฒํธ๊ฐ_1๋ถํฐ_45_์ฌ์ด์_๊ฐ์ด_์๋๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(int invalidNumber) {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5, invalidNumber);
+
+ // when & then
+ assertThatThrownBy(() -> new Lotto(numbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ฒํธ๋ 1๋ถํฐ 45 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ @ParameterizedTest
+ @NullSource
+ void ๋ก๋_๋ฒํธ๊ฐ_null์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(List numbers) {
+
+ // when & then
+ assertThatThrownBy(() -> new Lotto(numbers))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ฒํธ๋ null์ผ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋ก๋_๋ฒํธ๊ฐ_์ ์_๋ฒ์์์_๊ทน_๊ฐ์ธ_1๊ณผ_45๋ฅผ_ํฌํจํด๋_์์ธ๊ฐ_๋ฐ์ํ์ง_์๋๋ค() {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5, 45);
+
+ // when & then
+ assertThatNoException().isThrownBy(() -> new Lotto(numbers));
+ }
+
+ @Test
+ void ์ ์_๋ฒ์์_6๊ฐ_๊ฐ์_์ฌ์ฉํ๋ฉด_๊ฐ์ฒด_์์ฑ์ด_๊ฐ๋ฅํ๋ค() {
+
+ // given
+ List numbers = List.of(8, 1, 45, 3, 20, 10);
+
+ // when
+ Lotto lotto = new Lotto(numbers);
+
+ // then
+ assertThat(lotto).isNotNull();
+ assertThat(lotto.getNumbers()).hasSize(6);
+ }
+
+ @Test
+ void ๋ก๋_๋ฒํธ๋_์ค๋ฆ์ฐจ์์ผ๋ก_์ ๋ ฌ๋์ด_์ ์ฅ๋๋ค() {
+
+ // given
+ List numbers = List.of(8, 1, 45, 3, 20, 10);
+
+ // when
+ Lotto lotto = new Lotto(numbers);
+
+ // then
+ assertThat(lotto.getNumbers()).containsExactly(1, 3, 8, 10, 20, 45);
+ }
+
+ @Test
+ void ๋ก๋๊ฐ_ํน์ _๋ฒํธ๋ฅผ_ํฌํจํ๋ฉด_true๋ฅผ_๋ฐํํ๋ค() {
+
+ // given
+ Lotto lotto = new Lotto(List.of(1, 3, 5, 10, 20, 45));
+
+ // when & then
+ assertThat(lotto.containsNumber(10)).isTrue();
+ assertThat(lotto.containsNumber(7)).isFalse();
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideLottoMatchScenarios")
+ void ๋ค๋ฅธ_๋ก๋์_์ผ์นํ๋_๋ฒํธ_๊ฐ์๋ฅผ_๊ณ์ฐํ _์_์๋ค(Lotto priorLotto, Lotto nextLotto, int expectedMatchCount) {
+
+ // when
+ int matchCount = priorLotto.countMatchingNumbers(nextLotto);
+
+ // then
+ assertThat(matchCount).isEqualTo(expectedMatchCount);
+ }
+
+ @Test
+ void ๋น๊ตํ _๋ก๋๊ฐ_null์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6));
+
+ // when & then
+ assertThatThrownBy(() -> lotto.countMatchingNumbers(null))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋น๊ตํ ๋ก๋๊ฐ null์ผ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋ก๋_๋ฒํธ_๋ชฉ๋ก์_์ธ๋ถ์์_์์ ํ _์_์๋ค() {
+
+ // given
+ Lotto lotto = new Lotto(List.of(1, 2, 3, 4, 5, 6));
+
+ // when & then
+ assertThatThrownBy(() -> lotto.getNumbers().add(7))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ private static Stream provideLottoMatchScenarios() {
+ return Stream.of(
+ Arguments.of(
+ new Lotto(List.of(1, 2, 3, 4, 5, 6)),
+ new Lotto(List.of(4, 5, 6, 40, 41, 42)),
+ 3
+ ),
+ Arguments.of(
+ new Lotto(List.of(1, 2, 3, 4, 5, 6)),
+ new Lotto(List.of(7, 8, 9, 10, 11, 12)),
+ 0
+ ),
+ Arguments.of(
+ new Lotto(List.of(1, 2, 3, 4, 5, 6)),
+ new Lotto(List.of(1, 2, 3, 4, 5, 6)),
+ 6
+ )
+ );
+ }
+}
diff --git a/src/test/java/lotto/domain/MoneyTest.java b/src/test/java/lotto/domain/MoneyTest.java
new file mode 100644
index 0000000000..f28728b1dc
--- /dev/null
+++ b/src/test/java/lotto/domain/MoneyTest.java
@@ -0,0 +1,53 @@
+package lotto.domain;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class MoneyTest {
+
+ private static final int UNIT_MONEY_FOR_BUY_LOTTO = 1000;
+
+ @ParameterizedTest
+ @ValueSource(ints = {1000, 5000, 1000000000})
+ void ๋ก๋_๊ตฌ๋งค_๊ธ์ก์ด_์ฒ_์์ผ๋ก_๋๋ ์ง๋ฉด_Money_๊ฐ์ฒด๊ฐ_์์ฑ๋๋ค(int amount) {
+ // when
+ Money money = new Money(amount);
+
+ // then
+ assertThat(money).isNotNull();
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {500, 1100, 2500})
+ void ๋ก๋_๊ตฌ๋งค_๊ธ์ก์ด_์ฒ_์์ผ๋ก_๋๋ ์ง์ง_์์ผ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(int amount) {
+ // when & then
+ assertThatThrownBy(() -> new Money(amount))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] 1,000์ ๋จ์๋ก ์
๋ ฅํด ์ฃผ์ธ์.");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, -1000, -5000})
+ void ๋ก๋_๊ตฌ๋งค_๊ธ์ก์ด_0์_์ดํ๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(int amount) {
+ // when & then
+ assertThatThrownBy(() -> new Money(amount))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๊ตฌ์
๊ธ์ก์ 0๋ณด๋ค ์ปค์ผ ํฉ๋๋ค.");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1000, 3000, 100000})
+ void ๋ก๋_๊ตฌ๋งค_๊ธ์ก์_์
๋ ฅํ๋ฉด_๊ตฌ๋งค_๊ฐ๋ฅํ_๋ก๋_๊ฐ์๋ฅผ_๋ฐํํ๋ค(int amount) {
+ // given
+ Money money = new Money(amount);
+
+ // when
+ int count = money.getLottoCount();
+
+ // then
+ assertThat(count).isEqualTo(amount / UNIT_MONEY_FOR_BUY_LOTTO);
+ }
+}
diff --git a/src/test/java/lotto/domain/RandomLottoGeneratorTest.java b/src/test/java/lotto/domain/RandomLottoGeneratorTest.java
new file mode 100644
index 0000000000..18d47aa941
--- /dev/null
+++ b/src/test/java/lotto/domain/RandomLottoGeneratorTest.java
@@ -0,0 +1,25 @@
+package lotto.domain;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RandomLottoGeneratorTest {
+
+ @Test
+ void ๋ก๋_์์ฑ_์์ฒญ_๊ฐ์๋งํผ_๋๋ค_๋ก๋๋ฅผ_์์ฑํ๋ค() {
+
+ // given
+ LottoGenerator generator = new RandomLottoGenerator();
+ int count = 5;
+
+ // when
+ List lottos = generator.generate(count);
+
+ // then
+ assertThat(lottos).hasSize(count)
+ .doesNotContainNull();
+ }
+}
diff --git a/src/test/java/lotto/domain/WinningNumbersTest.java b/src/test/java/lotto/domain/WinningNumbersTest.java
new file mode 100644
index 0000000000..f802764dc6
--- /dev/null
+++ b/src/test/java/lotto/domain/WinningNumbersTest.java
@@ -0,0 +1,106 @@
+package lotto.domain;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.*;
+
+class WinningNumbersTest {
+
+ @ParameterizedTest
+ @ValueSource(ints = {1, 45})
+ void ๋ณด๋์ค_๋ฒํธ๊ฐ_์ ์_๋ฒ์์์_๊ทน_๊ฐ์ธ_1๊ณผ_45_๊ฐ์ด_๋ค์ด๊ฐ๋_์์ธ๊ฐ_๋ฐ์ํ์ง_์๋๋ค(int validBonusNumber) {
+
+ // given
+ List numbers = List.of(2, 3, 4, 5, 6, 7);
+
+ // when & then
+ assertThatNoException()
+ .isThrownBy(() -> new WinningNumbers(numbers, validBonusNumber));
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, 46})
+ void ๋ณด๋์ค_๋ฒํธ๊ฐ_์ ์_๋ฒ์๋ฅผ_๋ฒ์ด๋_1๋ถํฐ_45_์ฌ์ด์_๊ฐ์ด_์๋๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(int invalidBonusNumber) {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5, 6);
+
+ // when & then
+ assertThatThrownBy(() -> new WinningNumbers(numbers, invalidBonusNumber))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ณด๋์ค ๋ฒํธ๋ 1๋ถํฐ 45 ์ฌ์ด์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ @Test
+ void ๋ณด๋์ค_๋ฒํธ๊ฐ_๋น์ฒจ_๋ฒํธ์_์ค๋ณต๋๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ List numbers = List.of(1, 2, 3, 4, 5, 6);
+
+ // when & then
+ assertThatThrownBy(() -> new WinningNumbers(numbers, 6))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ณด๋์ค ๋ฒํธ๋ ๋น์ฒจ ๋ฒํธ์ ์ค๋ณต๋ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋น์ฒจ_๋ฒํธ_๋ก๋์_์์ฑ์_๊ฒ์ฆ์ด_๋ณด๋์ค_๋ฒํธ_๊ฒ์ฆ๋ณด๋ค_๋จผ์ _์ํ๋๋ค() {
+
+ // given
+ List invalidNumbers = List.of(1, 2, 3, 4, 5, 5);
+
+ // when & then
+ assertThatThrownBy(() -> new WinningNumbers(invalidNumbers, 7))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ก๋ ๋ฒํธ๋ ์ค๋ณต๋ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋น์ฒจ_๋ฒํธ์_๋ณด๋์ค_๋ฒํธ๊ฐ_๋ชจ๋_์ ํจํ๋ฉด_์์ฑ๋๋ค() {
+
+ // given
+ List lottoNumbers = List.of(4, 36, 3, 28, 44, 11);
+ int bonusNumber = 7;
+
+ // when
+ WinningNumbers winningNumbers = new WinningNumbers(lottoNumbers, bonusNumber);
+
+ // then
+ assertThat(winningNumbers).isNotNull();
+ assertThat(winningNumbers.getBonusNumber()).isEqualTo(bonusNumber);
+
+ Lotto testLotto = new Lotto(lottoNumbers);
+ assertThat(winningNumbers.countMatchingNumbers(testLotto)).isEqualTo(6);
+ }
+
+ @Test
+ void ๋น์ฒจ_๋ฒํธ_๋ก๋์_๋น๊ตํ _๋ก๋์_๊ฐ_์ผ์น_๊ฐ์๋ฅผ_๊ตฌํ _์_์๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ Lotto lotto = new Lotto(List.of(1, 2, 10, 11, 12, 13));
+
+ // when
+ int matchCount = winningNumbers.countMatchingNumbers(lotto);
+
+ // then
+ assertThat(matchCount).isEqualTo(2);
+ }
+
+ @Test
+ void ๋ณด๋์ค_๋ฒํธ๋ฅผ_ํฌํจํ๋์ง_ํ์ธํ _์_์๋ค() {
+
+ // given
+ WinningNumbers winningNumbers = new WinningNumbers(List.of(1, 2, 3, 4, 5, 6), 7);
+ Lotto containsBonus = new Lotto(List.of(7, 10, 20, 30, 40, 45));
+ Lotto notContainsBonus = new Lotto(List.of(8, 10, 20, 30, 40, 45));
+
+ // when & then
+ assertThat(winningNumbers.containsBonusNumber(containsBonus)).isTrue();
+ assertThat(winningNumbers.containsBonusNumber(notContainsBonus)).isFalse();
+ }
+}
diff --git a/src/test/java/lotto/domain/testDouble/FakeLottoGenerator.java b/src/test/java/lotto/domain/testDouble/FakeLottoGenerator.java
new file mode 100644
index 0000000000..012f2aed3c
--- /dev/null
+++ b/src/test/java/lotto/domain/testDouble/FakeLottoGenerator.java
@@ -0,0 +1,31 @@
+package lotto.domain.testDouble;
+
+import lotto.domain.Lotto;
+import lotto.domain.LottoGenerator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class FakeLottoGenerator implements LottoGenerator {
+
+ private final Iterator> numbersIterator;
+
+ public FakeLottoGenerator(List> numbersSequences) {
+ this.numbersIterator = numbersSequences.iterator();
+ }
+
+ @Override
+ public List generate(int count) {
+ List lottos = new ArrayList<>();
+ for (int i = 0; i < count; i++) {
+ List numbers = numbersIterator.next();
+ lottos.add(new Lotto(numbers));
+ }
+ return lottos;
+ }
+
+ public static FakeLottoGenerator of(List numbers) {
+ return new FakeLottoGenerator(List.of(numbers));
+ }
+}
diff --git a/src/test/java/lotto/view/InputValidatorTest.java b/src/test/java/lotto/view/InputValidatorTest.java
new file mode 100644
index 0000000000..e6da7d9c90
--- /dev/null
+++ b/src/test/java/lotto/view/InputValidatorTest.java
@@ -0,0 +1,132 @@
+package lotto.view;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.NullSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class InputValidatorTest {
+
+ @Test
+ void ๊ตฌ์
_๊ธ์ก_๋ฌธ์์ด์_์ ์๋ก_ํ์ฑํ๋ค() {
+
+ // given
+ String input = "3000";
+
+ // when
+ int amount = InputValidator.parsePurchaseAmount(input);
+
+ // then
+ assertThat(amount).isEqualTo(3000);
+ }
+
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(strings = {"", " ", " "})
+ void ๊ตฌ์
_๊ธ์ก_์
๋ ฅ์ด_null_๋๋_๊ณต๋ฐฑ์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(String input) {
+
+ // when & then
+ assertThatThrownBy(() -> InputValidator.parsePurchaseAmount(input))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ์
๋ ฅ์ ๋น์ด ์์ ์ ์์ต๋๋ค.");
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"abc", "1000์", "1 000", "1,000"})
+ void ๊ตฌ์
_๊ธ์ก์_์ซ์๊ฐ_์๋_๊ฐ์ด_ํฌํจ๋๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(String input) {
+
+ // when & then
+ assertThatThrownBy(() -> InputValidator.parsePurchaseAmount(input))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๊ตฌ์
๊ธ์ก์ ์ซ์์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ @Test
+ void ์ผํ๋ก_๊ตฌ๋ถ๋_๋น์ฒจ_๋ฒํธ๋ฅผ_์ ์_๋ฆฌ์คํธ๋ก_ํ์ฑํ๋ค() {
+
+ // given
+ String input = "1,2,3,4,5,6";
+
+ // when
+ List numbers = InputValidator.parseWinningNumbers(input);
+
+ // then
+ assertThat(numbers).containsExactly(1, 2, 3, 4, 5, 6);
+ }
+
+ @Test
+ void ๋น์ฒจ_๋ฒํธ_ํ์ฑ์_๊ณต๋ฐฑ์_๋ฌด์ํ๋ค() {
+
+ // given
+ String input = " 1, 2 ,3 , 4,5 ,6 ";
+
+ // when
+ List numbers = InputValidator.parseWinningNumbers(input);
+
+ // then
+ assertThat(numbers).containsExactly(1, 2, 3, 4, 5, 6);
+ }
+
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(strings = {"", " ", " "})
+ void ๋น์ฒจ_๋ฒํธ_์
๋ ฅ์ด_null_๋๋_๊ณต๋ฐฑ์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(String input) {
+
+ // when & then
+ assertThatThrownBy(() -> InputValidator.parseWinningNumbers(input))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ์
๋ ฅ์ ๋น์ด ์์ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋น์ฒจ_๋ฒํธ์_์ซ์๊ฐ_์๋_๊ฐ์ด_ํฌํจ๋๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+
+ // given
+ String input = "1, 2, ์ธ, 4, 5, 6";
+
+ // when & then
+ assertThatThrownBy(() -> InputValidator.parseWinningNumbers(input))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋น์ฒจ ๋ฒํธ๋ ์ซ์์ฌ์ผ ํฉ๋๋ค.");
+ }
+
+ @Test
+ void ๋ณด๋์ค_๋ฒํธ_๋ฌธ์์ด์_์ ์๋ก_ํ์ฑํ๋ค() {
+
+ // given
+ String input = "7";
+
+ // when
+ int bonus = InputValidator.parseBonusNumber(input);
+
+ // then
+ assertThat(bonus).isEqualTo(7);
+ }
+
+ @ParameterizedTest
+ @NullSource
+ @ValueSource(strings = {"", " ", " "})
+ void ๋ณด๋์ค_๋ฒํธ_์
๋ ฅ์ด_null_๋๋_๊ณต๋ฐฑ์ด๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค(String input) {
+
+ // when & then
+ assertThatThrownBy(() -> InputValidator.parseBonusNumber(input))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ์
๋ ฅ์ ๋น์ด ์์ ์ ์์ต๋๋ค.");
+ }
+
+ @Test
+ void ๋ณด๋์ค_๋ฒํธ์_์ซ์๊ฐ_์๋_๊ฐ์ด_๋ค์ด์ค๋ฉด_์์ธ๊ฐ_๋ฐ์ํ๋ค() {
+ // given
+ String input = "๋ณด๋์ค";
+
+ // when & then
+ assertThatThrownBy(() -> InputValidator.parseBonusNumber(input))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("[ERROR] ๋ณด๋์ค ๋ฒํธ๋ ์ซ์์ฌ์ผ ํฉ๋๋ค.");
+ }
+}