From e68f567b926f852360378c26df1d65eec04d26ca Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 22:24:45 +0900 Subject: [PATCH 01/18] =?UTF-8?q?docs:=20=EB=A1=9C=EB=98=90=204=EB=8B=A8?= =?UTF-8?q?=EA=B3=84(=EC=88=98=EB=8F=99)=20=EB=AC=B8=EC=84=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20README=20=EB=A7=81=ED=81=AC=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 README.md - 4단계 문서 링크를 `./docs/04-lotto-manual.md`로 수정하여 잘못된 링크를 정상화 04-lotto-manual.md - 로또 4단계(수동) 요구사항 문서 신규 추가 - 기능 요구사항, 프로그래밍 규칙, 체크리스트, 구현 기능 목록 상세 기술 --- README.md | 2 +- docs/04-lotto-manual.md | 118 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 docs/04-lotto-manual.md diff --git a/README.md b/README.md index cb44fbb4e3..905d38c7bd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ - **[1단계 - 문자열 계산기](./docs/01-string-calculator.md)** - **[2단계 - 로또(자동)](./docs/02-lotto-auto.md)** - **[3단계 - 로또(2등)](./docs/03-lotto-rank-second.md)** -- **[4단계 - 로또(수동)]()** +- **[4단계 - 로또(수동)](./docs/04-lotto-manual.md)** ## 진행 방법 * 로또 요구사항을 파악한다. * 요구사항에 대한 구현을 완료한 후 자신의 github 아이디에 해당하는 브랜치에 Pull Request(이하 PR)를 통해 코드 리뷰 요청을 한다. diff --git a/docs/04-lotto-manual.md b/docs/04-lotto-manual.md new file mode 100644 index 0000000000..1222ceff03 --- /dev/null +++ b/docs/04-lotto-manual.md @@ -0,0 +1,118 @@ +# 4단계 - 로또(수동) +*** +## 코드 리뷰 +> PR 링크: +> +## 나의 학습 목표 +- TDD 사이클로 구현 +- 객체지향 생활 체조 원칙 준수 +- 테스트 작성하기 쉬운 구조로 설계 +- 체크리스트 및 피드백 준수 +- 기능 추가 시 컴파일 에러를 최소화하며 점진적인 리팩터링에 도전 +## 기능 요구 사항 +- 현재 로또 생성기는 자동 생성 기능만 제공한다. 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다. +- 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다. +## 힌트 +- 규칙 3: 모든 원시값과 문자열을 포장한다. + - 로또 숫자 하나는 int 타입이다. 이 숫자 하나를 추상화한 LottoNo 객체를 추가해 구현한다. +- 예외 처리를 통해 프로그램이 중단되지 않고 다시 입력할 수 있도록 구현한다. + - 사용자가 잘못된 값을 입력했을 때 java exception으로 에러 처리를 한다. + - java8에 추가된 Optional을 적용해 NullPointerException이 발생하지 않도록 한다. +## 프로그래밍 요구사항 +- 규칙 3: 모든 원시값과 문자열을 포장한다. +- 규칙 5: 줄여쓰지 않는다(축약 금지). +- 예외 처리를 통해 에러가 발생하지 않도록 한다. +- 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 +- java enum을 적용해 프로그래밍을 구현한다. +- 규칙 8: 일급 콜렉션을 쓴다. +- indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다. +- 함수(또는 메소드)의 길이가 15라인을 넘어가지 않도록 구현한다. +- 자바 코드 컨벤션을 지키면서 프로그래밍한다. +- else 예약어를 쓰지 않는다. +## PR 전 점검 +**[체크리스트 확인하기](checklist.md)** +## 구현 기능 목록 +### 공통 +- [x] 금액 (Money) + - [x] 0원 이상의 정수로 생성 + - [x] 금액 비교 (미만 여부) + - [x] 나눗셈 가능 여부 판단 + - [x] 나눗셈 연산 + - [x] 비율 계산 + - [x] 곱셈 연산 + - [x] 덧셈 연산 + - [x] 원화 문자열 변환 + +### 로또 번호 +- [x] 로또 번호 (LottoNumber) + - [x] 1~45 범위의 정수로 생성 + - [x] 문자열 입력으로 생성 + - [x] 범위 벗어날 시 예외 발생 + - [x] 동일 값은 캐싱된 인스턴스 반환 + +### 로또 +- [x] 로또 (Lotto) + - [x] 6개의 로또 번호로 생성 + - [x] 중복된 번호 존재 시 예외 발생 + - [x] 번호 누락 시 예외 발생 + - [x] 6개가 아닐 시 예외 발생 + - [x] 다른 로또와 일치하는 번호 개수 계산 + - [x] 특정 번호 포함 여부 판단 + - [x] 정렬된 출력용 문자열 반환 + +### 로또 구매 +- [x] 로또 구매 개수 (LottoCount) + - [x] 1개 이상의 정수로 생성 + - [x] 1개 미만일 시 예외 발생 + +- [x] 로또 구매 금액 (LottoPurchaseAmount) + - [x] 금액으로 생성 + - [x] 문자열 입력으로 생성 + - [x] 정수 입력으로 생성 + - [x] 최소 금액(1,000원) 미달 시 예외 발생 + - [x] 1,000원 단위가 아닐 시 예외 발생 + - [x] 구매 가능한 로또 개수 계산 + +- [x] 구매한 로또 목록 (PurchasedLottos) + - [x] 로또 목록으로 생성 + - [x] 빈 목록일 시 예외 발생 + - [x] 당첨 로또와 비교하여 결과 생성 + - [x] 구매 개수 출력용 문자열 반환 + +### 당첨 판정 +- [x] 당첨 로또 (WinningLotto) + - [x] 로또 번호 목록과 보너스 번호로 생성 + - [x] 보너스 번호가 당첨 번호와 중복 시 예외 발생 + - [x] 구매 로또와 비교하여 등수 판정 + +- [x] 등수 (LottoRank) + - [x] 일치 개수와 보너스 일치 여부로 등수 결정 + - [x] 3개 미만 일치 시 MISS 반환 + - [x] 3개 일치 시 FIFTH (5,000원) + - [x] 4개 일치 시 FOURTH (50,000원) + - [x] 5개 일치 시 THIRD (1,500,000원) + - [x] 5개 일치 + 보너스 일치 시 SECOND (30,000,000원) + - [x] 6개 일치 시 FIRST (2,000,000,000원) + - [x] 등수별 총 상금 계산 + - [x] 일치 개수 출력용 문자열 반환 + - [x] 상금 출력용 문자열 반환 + +- [x] 로또 결과 (LottoResult) + - [x] 빈 상태로 생성 + - [x] 등수 추가 + - [x] 총 상금 계산 + - [x] 등수별 결과 출력용 문자열 반환 + +- [x] 수익률 (ReturnRate) + - [x] 구매금액과 총 상금으로 생성 + - [x] 수익률 0 미만일 시 예외 발생 + - [x] 수익률 출력용 문자열 반환 + +### 유틸리티 +- [x] 자동 로또 생성기 (AutoBasedLottoGenerator) + - [x] 1~45 범위의 번호 풀 보유 + - [x] 지정 개수만큼 로또 생성 + - [x] 셔플 방식으로 6개 번호 선택 + +- [x] 로또 번호 파서 (LottoNumberParser) + - [x] 쉼표 구분 문자열을 로또 번호 목록으로 변환 \ No newline at end of file From dee80ed7fb6048fe02846e4c08abfa8acea0adab Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 22:47:06 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EC=83=9D=EC=84=B1=EA=B8=B0=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ManualBasedLottoGenerator.java - 문자열 입력을 받아 로또 번호 파싱 후 Lotto 생성하도록 수동 로또 생성기 구현 - 기본 생성자에서 LottoNumberParser 주입 방식 제공 ManualBasedLottoGeneratorTest.java - 문자열 목록 입력 시 올바른 Lotto 리스트가 생성되는지 검증 테스트 추가 --- .../utils/ManualBasedLottoGenerator.java | 21 +++++++++++++++++++ .../utils/ManualBasedLottoGeneratorTest.java | 19 +++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/main/java/lotto/utils/ManualBasedLottoGenerator.java create mode 100644 src/test/java/lotto/utils/ManualBasedLottoGeneratorTest.java diff --git a/src/main/java/lotto/utils/ManualBasedLottoGenerator.java b/src/main/java/lotto/utils/ManualBasedLottoGenerator.java new file mode 100644 index 0000000000..11ecf89620 --- /dev/null +++ b/src/main/java/lotto/utils/ManualBasedLottoGenerator.java @@ -0,0 +1,21 @@ +package lotto.utils; + +import java.util.List; +import lotto.domain.Lotto; + +public class ManualBasedLottoGenerator { + + private final LottoNumberParser parser; + + public ManualBasedLottoGenerator() { + this(new LottoNumberParser()); + } + + private ManualBasedLottoGenerator(LottoNumberParser parser) { + this.parser = parser; + } + + public List generate(List inputs) { + return inputs.stream().map(parser::parse).map(Lotto::new).toList(); + } +} diff --git a/src/test/java/lotto/utils/ManualBasedLottoGeneratorTest.java b/src/test/java/lotto/utils/ManualBasedLottoGeneratorTest.java new file mode 100644 index 0000000000..f60e8e2719 --- /dev/null +++ b/src/test/java/lotto/utils/ManualBasedLottoGeneratorTest.java @@ -0,0 +1,19 @@ +package lotto.utils; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; +import lotto.domain.Lotto; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class ManualBasedLottoGeneratorTest { + + @Test + void generate_문자열목록_생성() { + assertThat(new ManualBasedLottoGenerator().generate(List.of("1, 2, 3, 4, 5, 6", "7, 8, 9, 10, 11, 12"))) + .isEqualTo(List.of(new Lotto(1, 2, 3, 4, 5, 6), new Lotto(7, 8, 9, 10, 11, 12))); + } +} From 05ceaac05584a58b88ea24629a1288236a12fd1c Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 22:47:49 +0900 Subject: [PATCH 03/18] =?UTF-8?q?docs:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EC=83=9D=EC=84=B1=EA=B8=B0=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=AC=B8=EC=84=9C=EC=97=90=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 04-lotto-manual.md - 수동 로또 생성기(ManualBasedLottoGenerator) 기능 요구사항 항목 추가 - 문자열 목록을 기반으로 로또 목록을 생성하는 기능 명시 --- docs/04-lotto-manual.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/04-lotto-manual.md b/docs/04-lotto-manual.md index 1222ceff03..ddcff68fbc 100644 --- a/docs/04-lotto-manual.md +++ b/docs/04-lotto-manual.md @@ -113,6 +113,8 @@ - [x] 1~45 범위의 번호 풀 보유 - [x] 지정 개수만큼 로또 생성 - [x] 셔플 방식으로 6개 번호 선택 +- [x] 수동 로또 생성기 (ManualBasedLottoGenerator) + - [x] 문자열 목록을 입력받아 로또 목록 생성 - [x] 로또 번호 파서 (LottoNumberParser) - [x] 쉼표 구분 문자열을 로또 번호 목록으로 변환 \ No newline at end of file From eda4de9fbd2831df14add1a4ef7ac5a397fdb296 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 23:13:38 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EA=B5=AC=EB=A7=A4=20=EA=B0=9C=EC=88=98=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ManualLottoCount.java - 문자열 입력을 정수로 변환하여 수동 구매 개수 생성 - null·빈값 입력 시 예외 발생 처리 ManualLottoCountTest.java - 정상 숫자 입력 시 생성 성공 테스트 - null·빈 문자열 입력 시 예외 발생 여부 검증 --- .../java/lotto/domain/ManualLottoCount.java | 16 +++++++++++ .../lotto/domain/ManualLottoCountTest.java | 28 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/java/lotto/domain/ManualLottoCount.java create mode 100644 src/test/java/lotto/domain/ManualLottoCountTest.java diff --git a/src/main/java/lotto/domain/ManualLottoCount.java b/src/main/java/lotto/domain/ManualLottoCount.java new file mode 100644 index 0000000000..ec901cfdf8 --- /dev/null +++ b/src/main/java/lotto/domain/ManualLottoCount.java @@ -0,0 +1,16 @@ +package lotto.domain; + +public record ManualLottoCount(int value) { + + public ManualLottoCount(String input) { + this(toInt(input)); + } + + private static int toInt(String input) { + if (input == null || input.isBlank()) { + throw new IllegalArgumentException("수동 구매 개수는 필수입니다."); + } + + return Integer.parseInt(input); + } +} diff --git a/src/test/java/lotto/domain/ManualLottoCountTest.java b/src/test/java/lotto/domain/ManualLottoCountTest.java new file mode 100644 index 0000000000..16492fce95 --- /dev/null +++ b/src/test/java/lotto/domain/ManualLottoCountTest.java @@ -0,0 +1,28 @@ +package lotto.domain; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) +class ManualLottoCountTest { + + @ParameterizedTest(name = "입력값:{0}") + @ValueSource(ints = {0, 5}) + void 생성자_문자열_생성성공(int input) { + assertThatCode(() -> new ManualLottoCount(input)).doesNotThrowAnyException(); + } + + @ParameterizedTest(name = "빈값:{0}") + @NullAndEmptySource + @ValueSource(strings = " ") + void 생성자_빈문자열_예외발생(String input) { + assertThatThrownBy(() -> new ManualLottoCount(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("수동 구매 개수는 필수입니다."); + } +} From 7b4716c4d78621a8b455744ff7815581d7051dc5 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 23:14:58 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EA=B0=9C=EC=88=98=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=95=ED=99=94=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ManualLottoCount.java - 생성자 블록에서 음수 값 검증 로직 추가 - 음수 입력 시 "수동 구매 개수는 0 이상이어야 합니다." 예외 발생 ManualLottoCountTest.java - 음수 입력 시 예외 발생 여부 검증 테스트 추가 --- src/main/java/lotto/domain/ManualLottoCount.java | 10 ++++++++++ src/test/java/lotto/domain/ManualLottoCountTest.java | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/lotto/domain/ManualLottoCount.java b/src/main/java/lotto/domain/ManualLottoCount.java index ec901cfdf8..1f30f9c765 100644 --- a/src/main/java/lotto/domain/ManualLottoCount.java +++ b/src/main/java/lotto/domain/ManualLottoCount.java @@ -13,4 +13,14 @@ private static int toInt(String input) { return Integer.parseInt(input); } + + public ManualLottoCount { + validateNonNegative(value); + } + + private static void validateNonNegative(int input) { + if (input < 0) { + throw new IllegalArgumentException("수동 구매 개수는 0 이상이어야 합니다."); + } + } } diff --git a/src/test/java/lotto/domain/ManualLottoCountTest.java b/src/test/java/lotto/domain/ManualLottoCountTest.java index 16492fce95..1b9e7fb728 100644 --- a/src/test/java/lotto/domain/ManualLottoCountTest.java +++ b/src/test/java/lotto/domain/ManualLottoCountTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; @@ -25,4 +26,11 @@ class ManualLottoCountTest { .isInstanceOf(IllegalArgumentException.class) .hasMessage("수동 구매 개수는 필수입니다."); } + + @Test + void 생성자_음수문자열_예외발생() { + assertThatThrownBy(() -> new ManualLottoCount(-1)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("수동 구매 개수는 0 이상이어야 합니다."); + } } From 07c96730f3c00233b94c465eded69d4e358bd179 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 23:17:24 +0900 Subject: [PATCH 06/18] =?UTF-8?q?docs:=20=EC=88=98=EB=8F=99=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4=20=EA=B0=9C=EC=88=98=20=EA=B8=B0=EB=8A=A5=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 04-lotto-manual.md - ManualLottoCount 요구사항 항목 추가 - 0 이상 정수 생성 조건 명시 - 문자열 생성 지원 명시 - 빈 문자열 및 음수 입력 시 예외 발생 명시 --- docs/04-lotto-manual.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/04-lotto-manual.md b/docs/04-lotto-manual.md index ddcff68fbc..c2a8d72c31 100644 --- a/docs/04-lotto-manual.md +++ b/docs/04-lotto-manual.md @@ -65,6 +65,12 @@ - [x] 1개 이상의 정수로 생성 - [x] 1개 미만일 시 예외 발생 +- [x] 수동 구매 개수 (ManualLottoCount) + - [x] 0 이상의 정수로 생성 + - [x] 문자열 입력으로 생성 + - [x] 빈 문자열일 시 예외 발생 + - [x] 음수일 시 예외 발생 + - [x] 로또 구매 금액 (LottoPurchaseAmount) - [x] 금액으로 생성 - [x] 문자열 입력으로 생성 From 5000c32a19072c0c8a78965f7f98db1e3f2fde81 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Wed, 3 Dec 2025 23:58:57 +0900 Subject: [PATCH 07/18] =?UTF-8?q?refactor:=20LottoCount=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EA=B2=80=EC=A6=9D=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F?= =?UTF-8?q?=20=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LottoCount.java - 문자열 기반 생성자 추가 및 빈 문자열 검증 로직 도입 - 최소값 기준을 1 → 0 이상으로 변경 - 음수 입력 시 예외 메시지 "구매 개수는 0이상이어야 합니다."로 통일 LottoCountTest.java - 정상 생성 가능 범위를 0 이상으로 확장한 테스트 추가 - 문자열 입력 시 빈값 예외 발생 테스트 추가 - 음수 입력 시 예외 검증으로 테스트 수정 --- src/main/java/lotto/domain/LottoCount.java | 16 +++++++++--- .../java/lotto/domain/LottoCountTest.java | 25 ++++++++++++++----- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/main/java/lotto/domain/LottoCount.java b/src/main/java/lotto/domain/LottoCount.java index cd6c2b444e..91eac0e3fd 100644 --- a/src/main/java/lotto/domain/LottoCount.java +++ b/src/main/java/lotto/domain/LottoCount.java @@ -1,11 +1,21 @@ package lotto.domain; public record LottoCount(int value) { - private static final int MIN_COUNT = 1; + + public LottoCount(String input) { + this(toInt(input)); + } + + private static int toInt(String input) { + if (input == null || input.isBlank()) { + throw new IllegalArgumentException("구매 개수는 필수입니다."); + } + return Integer.parseInt(input); + } public LottoCount { - if (value < MIN_COUNT) { - throw new IllegalArgumentException("로또 구매 개수는 %d개 이상이어야 합니다.".formatted(MIN_COUNT)); + if (value < 0) { + throw new IllegalArgumentException("구매 개수는 0이상이어야 합니다."); } } } diff --git a/src/test/java/lotto/domain/LottoCountTest.java b/src/test/java/lotto/domain/LottoCountTest.java index 3454ca0b29..114efec06e 100644 --- a/src/test/java/lotto/domain/LottoCountTest.java +++ b/src/test/java/lotto/domain/LottoCountTest.java @@ -5,19 +5,32 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) class LottoCountTest { - @Test - void 생성자_정상값_생성성공() { - assertThatCode(() -> new LottoCount(1)).doesNotThrowAnyException(); + @ParameterizedTest(name = "입력값:{0}") + @ValueSource(ints = {0, 5}) + void 생성자_정상값_생성성공(int input) { + assertThatCode(() -> new LottoCount(input)).doesNotThrowAnyException(); + } + + @ParameterizedTest(name = "빈값:{0}") + @NullAndEmptySource + @ValueSource(strings = {" "}) + void 생성자_빈문자열_예외발생(String input) { + assertThatThrownBy(() -> new LottoCount(input)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("구매 개수는 필수입니다."); } @Test - void 생성자_1미만입력_예외발생() { - assertThatThrownBy(() -> new LottoCount(0)) + void 생성자_음수입력_예외발생() { + assertThatThrownBy(() -> new LottoCount(-1)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("로또 구매 개수는 1개 이상이어야 합니다."); + .hasMessage("구매 개수는 0이상이어야 합니다."); } } From 0e54ee821064103cf14053508efb9d6099038cd6 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 00:02:30 +0900 Subject: [PATCH 08/18] =?UTF-8?q?refactor:=20LottoCount=20=EC=B0=A8?= =?UTF-8?q?=EA=B0=90=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EB=B3=B4=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LottoCount.java - subtract 메서드 추가하여 다른 LottoCount와의 차감 기능 제공 LottoCountTest.java - subtract 호출 시 정상적으로 값이 감소하는지 검증 테스트 추가 --- src/main/java/lotto/domain/LottoCount.java | 4 ++++ src/test/java/lotto/domain/LottoCountTest.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/lotto/domain/LottoCount.java b/src/main/java/lotto/domain/LottoCount.java index 91eac0e3fd..6a0c471a98 100644 --- a/src/main/java/lotto/domain/LottoCount.java +++ b/src/main/java/lotto/domain/LottoCount.java @@ -18,4 +18,8 @@ private static int toInt(String input) { throw new IllegalArgumentException("구매 개수는 0이상이어야 합니다."); } } + + public LottoCount subtract(LottoCount lottoCount) { + return new LottoCount(this.value - lottoCount.value); + } } diff --git a/src/test/java/lotto/domain/LottoCountTest.java b/src/test/java/lotto/domain/LottoCountTest.java index 114efec06e..0d20435a06 100644 --- a/src/test/java/lotto/domain/LottoCountTest.java +++ b/src/test/java/lotto/domain/LottoCountTest.java @@ -33,4 +33,9 @@ class LottoCountTest { .isInstanceOf(IllegalArgumentException.class) .hasMessage("구매 개수는 0이상이어야 합니다."); } + + @Test + void subtract_정상값_차감() { + assertThat(new LottoCount(10).subtract(new LottoCount(3))).isEqualTo(new LottoCount(7)); + } } From 055de052ce44bf9cf938560a61c4ff0ce97fb313 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 00:06:12 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20ManualLottoCount=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ManualLottoCount.java - 수동 구매 개수 도메인 클래스 삭제 ManualLottoCountTest.java - 관련 테스트 코드 전체 삭제 --- .../java/lotto/domain/ManualLottoCount.java | 26 -------------- .../lotto/domain/ManualLottoCountTest.java | 36 ------------------- 2 files changed, 62 deletions(-) delete mode 100644 src/main/java/lotto/domain/ManualLottoCount.java delete mode 100644 src/test/java/lotto/domain/ManualLottoCountTest.java diff --git a/src/main/java/lotto/domain/ManualLottoCount.java b/src/main/java/lotto/domain/ManualLottoCount.java deleted file mode 100644 index 1f30f9c765..0000000000 --- a/src/main/java/lotto/domain/ManualLottoCount.java +++ /dev/null @@ -1,26 +0,0 @@ -package lotto.domain; - -public record ManualLottoCount(int value) { - - public ManualLottoCount(String input) { - this(toInt(input)); - } - - private static int toInt(String input) { - if (input == null || input.isBlank()) { - throw new IllegalArgumentException("수동 구매 개수는 필수입니다."); - } - - return Integer.parseInt(input); - } - - public ManualLottoCount { - validateNonNegative(value); - } - - private static void validateNonNegative(int input) { - if (input < 0) { - throw new IllegalArgumentException("수동 구매 개수는 0 이상이어야 합니다."); - } - } -} diff --git a/src/test/java/lotto/domain/ManualLottoCountTest.java b/src/test/java/lotto/domain/ManualLottoCountTest.java deleted file mode 100644 index 1b9e7fb728..0000000000 --- a/src/test/java/lotto/domain/ManualLottoCountTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package lotto.domain; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.ValueSource; - -@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) -class ManualLottoCountTest { - - @ParameterizedTest(name = "입력값:{0}") - @ValueSource(ints = {0, 5}) - void 생성자_문자열_생성성공(int input) { - assertThatCode(() -> new ManualLottoCount(input)).doesNotThrowAnyException(); - } - - @ParameterizedTest(name = "빈값:{0}") - @NullAndEmptySource - @ValueSource(strings = " ") - void 생성자_빈문자열_예외발생(String input) { - assertThatThrownBy(() -> new ManualLottoCount(input)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("수동 구매 개수는 필수입니다."); - } - - @Test - void 생성자_음수문자열_예외발생() { - assertThatThrownBy(() -> new ManualLottoCount(-1)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("수동 구매 개수는 0 이상이어야 합니다."); - } -} From 2bd391de83a6f68c72fc960c81a12625c462f313 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 00:12:10 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EA=B5=AC=EB=A7=A4=20=ED=9D=90=EB=A6=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=9E=85=EC=B6=9C=EB=A0=A5=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=ED=99=95=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Application.java - 전체 구매 수에서 수동/자동 개수 분리 로직 추가 - 수동 번호 입력 및 로또 생성 기능 연동 - 자동/수동 로또 병합 처리 메서드 추가 - 구매 개수 출력 방식 변경(수동/자동 구분 출력) InputView.java - 수동 구매 개수 입력 메서드 추가 - 수동 번호 리스트 입력 메서드 추가 ResultView.java - 구매 개수 출력 형식을 변경하여 수동/자동 개수 명시 --- src/main/java/lotto/Application.java | 35 +++++++++++++++++++++--- src/main/java/lotto/view/InputView.java | 14 ++++++++++ src/main/java/lotto/view/ResultView.java | 4 +-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index da7c70e75a..ac67c3983f 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,18 +1,19 @@ package lotto; +import java.util.ArrayList; import java.util.List; import lotto.domain.*; import lotto.utils.AutoBasedLottoGenerator; import lotto.utils.LottoNumberParser; +import lotto.utils.ManualBasedLottoGenerator; import lotto.view.InputView; import lotto.view.ResultView; public class Application { public static void main(String[] args) { LottoPurchaseAmount purchaseAmount = getPurchaseAmount(); - PurchasedLottos purchased = new PurchasedLottos(generateLottos(purchaseAmount)); + PurchasedLottos purchased = purchaseLottos(purchaseAmount); - ResultView.printPurchaseCount(purchased); ResultView.printPurchasedLottos(purchased); LottoResult result = purchased.result(getWinningLotto()); @@ -24,8 +25,34 @@ private static LottoPurchaseAmount getPurchaseAmount() { return new LottoPurchaseAmount(InputView.readPurchaseAmount()); } - private static List generateLottos(LottoPurchaseAmount purchaseAmount) { - return new AutoBasedLottoGenerator().generate(purchaseAmount.lottoCount()); + private static PurchasedLottos purchaseLottos(LottoPurchaseAmount purchaseAmount) { + LottoCount totalCount = purchaseAmount.lottoCount(); + + LottoCount manualCount = new LottoCount(InputView.readManualLottoCount()); + List manualLottos = generateManualLottos(manualCount); + + LottoCount autoCount = totalCount.subtract(manualCount); + List autoLottos = generateAutoLottos(autoCount); + + ResultView.printPurchaseCount(manualCount, autoCount); + + return mergeLottos(manualLottos, autoLottos); + } + + private static List generateManualLottos(LottoCount manualCount) { + List manualInputs = InputView.readManualLottoNumbers(manualCount.value()); + List manualLottos = new ManualBasedLottoGenerator().generate(manualInputs); + return manualLottos; + } + + private static List generateAutoLottos(LottoCount autoCount) { + return new AutoBasedLottoGenerator().generate(autoCount); + } + + private static PurchasedLottos mergeLottos(List manual, List auto) { + List all = new ArrayList<>(manual); + all.addAll(auto); + return new PurchasedLottos(all); } private static WinningLotto getWinningLotto() { diff --git a/src/main/java/lotto/view/InputView.java b/src/main/java/lotto/view/InputView.java index 6a1b581352..2af7bdc8bb 100644 --- a/src/main/java/lotto/view/InputView.java +++ b/src/main/java/lotto/view/InputView.java @@ -1,6 +1,8 @@ package lotto.view; +import java.util.List; import java.util.Scanner; +import java.util.stream.IntStream; public class InputView { private static final Scanner scanner = new Scanner(System.in); @@ -11,6 +13,18 @@ public static String readPurchaseAmount() { return scanner.nextLine(); } + public static String readManualLottoCount() { + System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); + + return scanner.nextLine(); + } + + public static List readManualLottoNumbers(int count) { + System.out.println("수동으로 구매할 번호를 입력해 주세요."); + + return IntStream.range(0, count).mapToObj(i -> scanner.nextLine()).toList(); + } + public static String readWinningNumbers() { System.out.println("지난 주 당첨 번호를 입력해 주세요."); diff --git a/src/main/java/lotto/view/ResultView.java b/src/main/java/lotto/view/ResultView.java index bd6c06e391..c843c82414 100644 --- a/src/main/java/lotto/view/ResultView.java +++ b/src/main/java/lotto/view/ResultView.java @@ -4,8 +4,8 @@ public class ResultView { - public static void printPurchaseCount(PurchasedLottos purchased) { - System.out.println(purchased.purchaseCountForDisplay()); + public static void printPurchaseCount(LottoCount manualCount, LottoCount auto) { + System.out.printf("수동으로 %d장, 자동으로 %d개를 구매했습니다.%n", manualCount.value(), auto.value()); } public static void printPurchasedLottos(PurchasedLottos purchased) { From d9c80e730c7b519c62c8b9e509b6836572ecfa2d Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 00:13:28 +0900 Subject: [PATCH 11/18] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B5=AC=EB=A7=A4=20=EA=B0=9C=EC=88=98=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PurchasedLottos.java - purchaseCountForDisplay 및 size 관련 메서드 제거 (구매 수 출력 책임을 ResultView로 완전 이관) PurchasedLottosTest.java - purchaseCountForDisplay 검증 테스트 삭제 --- src/main/java/lotto/domain/PurchasedLottos.java | 8 -------- src/test/java/lotto/domain/PurchasedLottosTest.java | 7 ------- 2 files changed, 15 deletions(-) diff --git a/src/main/java/lotto/domain/PurchasedLottos.java b/src/main/java/lotto/domain/PurchasedLottos.java index 5c6a176950..250247c2b2 100644 --- a/src/main/java/lotto/domain/PurchasedLottos.java +++ b/src/main/java/lotto/domain/PurchasedLottos.java @@ -27,12 +27,4 @@ public LottoResult result(WinningLotto winningLotto) { return result; } - - public String purchaseCountForDisplay() { - return "%d개를 구매했습니다.".formatted(size()); - } - - private int size() { - return values.size(); - } } diff --git a/src/test/java/lotto/domain/PurchasedLottosTest.java b/src/test/java/lotto/domain/PurchasedLottosTest.java index 688a67a292..a1f7b9ec8b 100644 --- a/src/test/java/lotto/domain/PurchasedLottosTest.java +++ b/src/test/java/lotto/domain/PurchasedLottosTest.java @@ -24,11 +24,4 @@ class PurchasedLottosTest { .isInstanceOf(IllegalArgumentException.class) .hasMessage("구매한 로또는 1개 이상이어야 합니다."); } - - @Test - void purchaseCountForDisplay_구매개수_표시() { - PurchasedLottos purchased = new PurchasedLottos(new Lotto(1, 2, 3, 4, 5, 6)); - - assertThat(purchased.purchaseCountForDisplay()).isEqualTo("1개를 구매했습니다."); - } } From a67115dac1f520c9489529501a8efdf4ac5b1fc4 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 07:07:26 +0900 Subject: [PATCH 12/18] =?UTF-8?q?refactor:=20LottoCount=20=EC=B0=A8?= =?UTF-8?q?=EA=B0=90=20=EA=B2=80=EC=A6=9D=20=EA=B0=95=ED=99=94=20=EB=B0=8F?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=99=95=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LottoCount.java - subtract 호출 시 차감 가능 여부 검증 추가 - isLessThan 기반의 validateSubtractable 메서드 도입 - 차감 불가 시 "차감할 수 없습니다." 예외 발생하도록 수정 LottoCountTest.java - subtract 정상/동일 값 케이스에 대한 CSV 기반 테스트 추가 - subtract 시 초과 차감 예외 테스트 추가 - validateSubtractable 정상/예외 케이스 테스트 추가 --- src/main/java/lotto/domain/LottoCount.java | 15 +++++++++-- .../java/lotto/domain/LottoCountTest.java | 27 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/lotto/domain/LottoCount.java b/src/main/java/lotto/domain/LottoCount.java index 6a0c471a98..18e9e03d6a 100644 --- a/src/main/java/lotto/domain/LottoCount.java +++ b/src/main/java/lotto/domain/LottoCount.java @@ -19,7 +19,18 @@ private static int toInt(String input) { } } - public LottoCount subtract(LottoCount lottoCount) { - return new LottoCount(this.value - lottoCount.value); + public LottoCount subtract(LottoCount other) { + validateSubtractable(other); + return new LottoCount(this.value - other.value); + } + + public void validateSubtractable(LottoCount other) { + if (isLessThan(other)) { + throw new IllegalArgumentException("차감할 수 없습니다."); + } + } + + private boolean isLessThan(LottoCount other) { + return this.value < other.value; } } diff --git a/src/test/java/lotto/domain/LottoCountTest.java b/src/test/java/lotto/domain/LottoCountTest.java index 0d20435a06..da97fd7e85 100644 --- a/src/test/java/lotto/domain/LottoCountTest.java +++ b/src/test/java/lotto/domain/LottoCountTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; @@ -34,8 +35,30 @@ class LottoCountTest { .hasMessage("구매 개수는 0이상이어야 합니다."); } + @ParameterizedTest(name = "원본:{0} - 차감:{1} = 결과:{2}") + @CsvSource({"10, 3, 7", "5, 5, 0"}) + void subtract_정상값_차감(int original, int subtrahend, int expected) { + assertThat(new LottoCount(original).subtract(new LottoCount(subtrahend))) + .isEqualTo(new LottoCount(expected)); + } + + @Test + void subtract_초과값_예외발생() { + assertThatThrownBy(() -> new LottoCount(3).subtract(new LottoCount(5))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("차감할 수 없습니다."); + } + @Test - void subtract_정상값_차감() { - assertThat(new LottoCount(10).subtract(new LottoCount(3))).isEqualTo(new LottoCount(7)); + void validateSubtractable_정상값_예외없음() { + assertThatCode(() -> new LottoCount(10).validateSubtractable(new LottoCount(5))) + .doesNotThrowAnyException(); + } + + @Test + void validateSubtractable_초과값_예외발생() { + assertThatThrownBy(() -> new LottoCount(3).validateSubtractable(new LottoCount(5))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("차감할 수 없습니다."); } } From 75c9e324ef40cb854280bc16ad88ab82a254b864 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 07:10:52 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat:=20=EC=98=88=EC=99=B8=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EC=8B=9C=20=EC=9E=AC=EC=9E=85=EB=A0=A5=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=9E=AC=EC=8B=9C?= =?UTF-8?q?=EB=8F=84=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Application.java - 구매 금액 입력을 `retryUntilSuccess`로 감싸 예외 발생 시 재입력 가능하도록 개선 - 수동 구매 개수 입력 시 총 구매 수 초과 여부 검증 및 재입력 처리 추가 - 수동 로또 생성 단계에도 재시도 로직 적용 - 입력 람다 처리용 `retryUntilSuccess` 유틸 메서드 추가 - manual/auto 입력 분리 메서드(`getManualLottoCount`, `getManualLottoNumbers`) 추가 --- src/main/java/lotto/Application.java | 32 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index ac67c3983f..ba207846ce 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; import lotto.domain.*; import lotto.utils.AutoBasedLottoGenerator; import lotto.utils.LottoNumberParser; @@ -22,13 +23,12 @@ public static void main(String[] args) { } private static LottoPurchaseAmount getPurchaseAmount() { - return new LottoPurchaseAmount(InputView.readPurchaseAmount()); + return retryUntilSuccess(() -> new LottoPurchaseAmount(InputView.readPurchaseAmount())); } private static PurchasedLottos purchaseLottos(LottoPurchaseAmount purchaseAmount) { LottoCount totalCount = purchaseAmount.lottoCount(); - - LottoCount manualCount = new LottoCount(InputView.readManualLottoCount()); + LottoCount manualCount = getManualLottoCount(totalCount); List manualLottos = generateManualLottos(manualCount); LottoCount autoCount = totalCount.subtract(manualCount); @@ -39,10 +39,20 @@ private static PurchasedLottos purchaseLottos(LottoPurchaseAmount purchaseAmount return mergeLottos(manualLottos, autoLottos); } + private static LottoCount getManualLottoCount(LottoCount totalCount) { + return retryUntilSuccess(() -> { + LottoCount manualCount = new LottoCount(InputView.readManualLottoCount()); + totalCount.validateSubtractable(manualCount); + return manualCount; + }); + } + private static List generateManualLottos(LottoCount manualCount) { - List manualInputs = InputView.readManualLottoNumbers(manualCount.value()); - List manualLottos = new ManualBasedLottoGenerator().generate(manualInputs); - return manualLottos; + return retryUntilSuccess(() -> new ManualBasedLottoGenerator().generate(getManualLottoNumbers(manualCount))); + } + + private static List getManualLottoNumbers(LottoCount manualCount) { + return InputView.readManualLottoNumbers(manualCount.value()); } private static List generateAutoLottos(LottoCount autoCount) { @@ -62,4 +72,14 @@ private static WinningLotto getWinningLotto() { List numbers = new LottoNumberParser().parse(winningNumbers); return new WinningLotto(numbers, bonusNumber); } + + private static T retryUntilSuccess(Supplier action) { + while (true) { + try { + return action.get(); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + } + } } From 2b66fdc8d652232ddae8e59e86a0999094ad1c5e Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 07:25:07 +0900 Subject: [PATCH 14/18] =?UTF-8?q?refactor:=20=EC=9E=85=EB=A0=A5/=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5=20static=20import=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B3=B4=EB=84=88=EC=8A=A4=20=EB=B2=88=ED=98=B8=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Application.java - InputView, ResultView 메서드들을 static import로 교체하여 코드 가독성 개선 - 구매 금액/수동 개수/수동 번호/당첨 번호/보너스 번호 입력에 재시도(retry) 로직 적용 - 수동 로또 번호 입력 흐름 간소화 (`getManualLottoNumbers` 제거) - 당첨 번호 파싱 및 보너스 번호 중복 검증 로직(`validateBonusNotDuplicated`) 분리 - WinningLotto 생성 시 LottoNumber를 직접 전달하도록 수정 WinningLotto.java - 생성자 시그니처 변경: 보너스 번호를 String → LottoNumber로 받도록 일원화 --- src/main/java/lotto/Application.java | 46 +++++++++++++------- src/main/java/lotto/domain/WinningLotto.java | 4 +- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index ba207846ce..fa589b71bd 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -1,5 +1,8 @@ package lotto; +import static lotto.view.InputView.*; +import static lotto.view.ResultView.*; + import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; @@ -7,23 +10,21 @@ import lotto.utils.AutoBasedLottoGenerator; import lotto.utils.LottoNumberParser; import lotto.utils.ManualBasedLottoGenerator; -import lotto.view.InputView; -import lotto.view.ResultView; public class Application { public static void main(String[] args) { LottoPurchaseAmount purchaseAmount = getPurchaseAmount(); PurchasedLottos purchased = purchaseLottos(purchaseAmount); - ResultView.printPurchasedLottos(purchased); + printPurchasedLottos(purchased); LottoResult result = purchased.result(getWinningLotto()); - ResultView.printResult(result, purchaseAmount); + printResult(result, purchaseAmount); } private static LottoPurchaseAmount getPurchaseAmount() { - return retryUntilSuccess(() -> new LottoPurchaseAmount(InputView.readPurchaseAmount())); + return retryUntilSuccess(() -> new LottoPurchaseAmount(readPurchaseAmount())); } private static PurchasedLottos purchaseLottos(LottoPurchaseAmount purchaseAmount) { @@ -34,25 +35,22 @@ private static PurchasedLottos purchaseLottos(LottoPurchaseAmount purchaseAmount LottoCount autoCount = totalCount.subtract(manualCount); List autoLottos = generateAutoLottos(autoCount); - ResultView.printPurchaseCount(manualCount, autoCount); + printPurchaseCount(manualCount, autoCount); return mergeLottos(manualLottos, autoLottos); } private static LottoCount getManualLottoCount(LottoCount totalCount) { return retryUntilSuccess(() -> { - LottoCount manualCount = new LottoCount(InputView.readManualLottoCount()); + LottoCount manualCount = new LottoCount(readManualLottoCount()); totalCount.validateSubtractable(manualCount); return manualCount; }); } private static List generateManualLottos(LottoCount manualCount) { - return retryUntilSuccess(() -> new ManualBasedLottoGenerator().generate(getManualLottoNumbers(manualCount))); - } - - private static List getManualLottoNumbers(LottoCount manualCount) { - return InputView.readManualLottoNumbers(manualCount.value()); + return retryUntilSuccess( + () -> new ManualBasedLottoGenerator().generate(readManualLottoNumbers(manualCount.value()))); } private static List generateAutoLottos(LottoCount autoCount) { @@ -66,11 +64,27 @@ private static PurchasedLottos mergeLottos(List manual, List auto) } private static WinningLotto getWinningLotto() { - String winningNumbers = InputView.readWinningNumbers(); - String bonusNumber = InputView.readBonusNumber(); + List numbers = getWinningNumbers(); + LottoNumber bonus = getBonusNumber(numbers); + return new WinningLotto(numbers, bonus); + } + + private static List getWinningNumbers() { + return retryUntilSuccess(() -> new LottoNumberParser().parse(readWinningNumbers())); + } - List numbers = new LottoNumberParser().parse(winningNumbers); - return new WinningLotto(numbers, bonusNumber); + private static LottoNumber getBonusNumber(List winningNumbers) { + return retryUntilSuccess(() -> { + LottoNumber bonus = LottoNumber.of(readBonusNumber()); + validateBonusNotDuplicated(winningNumbers, bonus); + return bonus; + }); + } + + private static void validateBonusNotDuplicated(List numbers, LottoNumber bonus) { + if (numbers.contains(bonus)) { + throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); + } } private static T retryUntilSuccess(Supplier action) { diff --git a/src/main/java/lotto/domain/WinningLotto.java b/src/main/java/lotto/domain/WinningLotto.java index 475b815747..ebd3ac8e55 100644 --- a/src/main/java/lotto/domain/WinningLotto.java +++ b/src/main/java/lotto/domain/WinningLotto.java @@ -4,8 +4,8 @@ public record WinningLotto(Lotto lotto, LottoNumber bonus) { - public WinningLotto(List numbers, String bonus) { - this(new Lotto(numbers), LottoNumber.of(bonus)); + public WinningLotto(List numbers, LottoNumber bonus) { + this(new Lotto(numbers), bonus); } public WinningLotto(Lotto lotto, int bonus) { From acfae395fb763c5ca57303b18c801a752b352c69 Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 07:39:21 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor:=20=EB=8B=B9=EC=B2=A8=20?= =?UTF-8?q?=EB=A1=9C=EB=98=90=20=EC=83=9D=EC=84=B1=20=ED=9D=90=EB=A6=84=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=A4=91=EB=B3=B5=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EB=8B=A8=EC=88=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Application.java - WinningLotto 생성 과정 분리: `generateWinningLotto` 메서드 추가 - 당첨 번호 파싱 후 바로 Lotto 객체로 생성 - 보너스 번호 검증 시 `Lotto`의 contains 사용하도록 개선 - 기존 List 기반 검증 로직을 Lotto 기반으로 단순화 --- src/main/java/lotto/Application.java | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index fa589b71bd..9c5b76f2aa 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -64,25 +64,29 @@ private static PurchasedLottos mergeLottos(List manual, List auto) } private static WinningLotto getWinningLotto() { - List numbers = getWinningNumbers(); - LottoNumber bonus = getBonusNumber(numbers); - return new WinningLotto(numbers, bonus); + Lotto lotto = generateWinningLotto(); + LottoNumber bonus = getBonusNumber(lotto); + return new WinningLotto(lotto, bonus); + } + + private static Lotto generateWinningLotto() { + return retryUntilSuccess(() -> new Lotto(getWinningNumbers())); } private static List getWinningNumbers() { - return retryUntilSuccess(() -> new LottoNumberParser().parse(readWinningNumbers())); + return new LottoNumberParser().parse(readWinningNumbers()); } - private static LottoNumber getBonusNumber(List winningNumbers) { + private static LottoNumber getBonusNumber(Lotto lotto) { return retryUntilSuccess(() -> { LottoNumber bonus = LottoNumber.of(readBonusNumber()); - validateBonusNotDuplicated(winningNumbers, bonus); + validateBonusNotDuplicated(lotto, bonus); return bonus; }); } - private static void validateBonusNotDuplicated(List numbers, LottoNumber bonus) { - if (numbers.contains(bonus)) { + private static void validateBonusNotDuplicated(Lotto lotto, LottoNumber bonus) { + if (lotto.contains(bonus)) { throw new IllegalArgumentException("보너스 번호는 당첨 번호와 중복될 수 없습니다."); } } From dee1310ca40552a91b02b084adc18eb30f689f6d Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 07:49:21 +0900 Subject: [PATCH 16/18] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F?= =?UTF-8?q?=20=EB=8B=B9=EC=B2=A8=20=EB=A1=9C=EB=98=90=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=ED=9D=90=EB=A6=84=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Application.java - 입력/생성 메서드 네이밍을 create* 스타일로 통일하여 책임 명확화 - 구매 로또 생성 흐름을 createManualLottos, generateAutoLottos로 단순화 - 당첨 번호 생성 흐름을 createLottoForWinning, createBonusNumber로 개선 - 전체적으로 불필요한 임시 변수 제거 및 호출 구조 간결화 --- src/main/java/lotto/Application.java | 31 +++++++++++++--------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/main/java/lotto/Application.java b/src/main/java/lotto/Application.java index 9c5b76f2aa..bcc923e8a4 100644 --- a/src/main/java/lotto/Application.java +++ b/src/main/java/lotto/Application.java @@ -13,34 +13,31 @@ public class Application { public static void main(String[] args) { - LottoPurchaseAmount purchaseAmount = getPurchaseAmount(); + LottoPurchaseAmount purchaseAmount = createPurchaseAmount(); PurchasedLottos purchased = purchaseLottos(purchaseAmount); printPurchasedLottos(purchased); - LottoResult result = purchased.result(getWinningLotto()); + LottoResult result = purchased.result(createWinningLotto()); printResult(result, purchaseAmount); } - private static LottoPurchaseAmount getPurchaseAmount() { + private static LottoPurchaseAmount createPurchaseAmount() { return retryUntilSuccess(() -> new LottoPurchaseAmount(readPurchaseAmount())); } private static PurchasedLottos purchaseLottos(LottoPurchaseAmount purchaseAmount) { LottoCount totalCount = purchaseAmount.lottoCount(); - LottoCount manualCount = getManualLottoCount(totalCount); - List manualLottos = generateManualLottos(manualCount); - + LottoCount manualCount = createManualLottoCount(totalCount); LottoCount autoCount = totalCount.subtract(manualCount); - List autoLottos = generateAutoLottos(autoCount); printPurchaseCount(manualCount, autoCount); - return mergeLottos(manualLottos, autoLottos); + return mergeLottos(createManualLottos(manualCount), generateAutoLottos(autoCount)); } - private static LottoCount getManualLottoCount(LottoCount totalCount) { + private static LottoCount createManualLottoCount(LottoCount totalCount) { return retryUntilSuccess(() -> { LottoCount manualCount = new LottoCount(readManualLottoCount()); totalCount.validateSubtractable(manualCount); @@ -48,7 +45,7 @@ private static LottoCount getManualLottoCount(LottoCount totalCount) { }); } - private static List generateManualLottos(LottoCount manualCount) { + private static List createManualLottos(LottoCount manualCount) { return retryUntilSuccess( () -> new ManualBasedLottoGenerator().generate(readManualLottoNumbers(manualCount.value()))); } @@ -63,21 +60,21 @@ private static PurchasedLottos mergeLottos(List manual, List auto) return new PurchasedLottos(all); } - private static WinningLotto getWinningLotto() { - Lotto lotto = generateWinningLotto(); - LottoNumber bonus = getBonusNumber(lotto); + private static WinningLotto createWinningLotto() { + Lotto lotto = createLottoForWinning(); + LottoNumber bonus = createBonusNumber(lotto); return new WinningLotto(lotto, bonus); } - private static Lotto generateWinningLotto() { - return retryUntilSuccess(() -> new Lotto(getWinningNumbers())); + private static Lotto createLottoForWinning() { + return retryUntilSuccess(() -> new Lotto(createWinningNumbers())); } - private static List getWinningNumbers() { + private static List createWinningNumbers() { return new LottoNumberParser().parse(readWinningNumbers()); } - private static LottoNumber getBonusNumber(Lotto lotto) { + private static LottoNumber createBonusNumber(Lotto lotto) { return retryUntilSuccess(() -> { LottoNumber bonus = LottoNumber.of(readBonusNumber()); validateBonusNotDuplicated(lotto, bonus); From f73cd13f803f44b87a333e9fc874c118b6349bea Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 07:51:52 +0900 Subject: [PATCH 17/18] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WinningLotto.java - List를 입력받는 생성자 제거하여 Lotto 기반 생성 방식으로 일원화 - 기존 int 보너스 번호 생성자는 그대로 유지 --- src/main/java/lotto/domain/WinningLotto.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/lotto/domain/WinningLotto.java b/src/main/java/lotto/domain/WinningLotto.java index ebd3ac8e55..581a273eb3 100644 --- a/src/main/java/lotto/domain/WinningLotto.java +++ b/src/main/java/lotto/domain/WinningLotto.java @@ -1,13 +1,7 @@ package lotto.domain; -import java.util.List; - public record WinningLotto(Lotto lotto, LottoNumber bonus) { - public WinningLotto(List numbers, LottoNumber bonus) { - this(new Lotto(numbers), bonus); - } - public WinningLotto(Lotto lotto, int bonus) { this(lotto, LottoNumber.of(bonus)); } From be9ef0a6ef3eeb50e6dfd4e925afc8dd422df00e Mon Sep 17 00:00:00 2001 From: ghtjr410 Date: Thu, 4 Dec 2025 08:09:20 +0900 Subject: [PATCH 18/18] =?UTF-8?q?docs:=20=EB=A1=9C=EB=98=90=20=EC=88=98?= =?UTF-8?q?=EB=8F=99=20=EA=B5=AC=EB=A7=A4=20=EB=B0=8F=20WinningLotto=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 04-lotto-manual.md - PR 링크 추가 - LottoCount 요구사항을 0 이상 생성 및 차감 검증 중심으로 업데이트 - ManualLottoCount 제거에 따른 관련 항목 삭제 - PurchasedLottos의 구매 개수 출력 기능 제거 반영 - WinningLotto 생성 방식 변경(로또 + 보너스 번호)으로 요구사항 수정 --- docs/04-lotto-manual.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/docs/04-lotto-manual.md b/docs/04-lotto-manual.md index c2a8d72c31..a1c3403938 100644 --- a/docs/04-lotto-manual.md +++ b/docs/04-lotto-manual.md @@ -2,7 +2,7 @@ *** ## 코드 리뷰 > PR 링크: -> +> **[https://github.com/next-step/java-lotto/pull/4232](https://github.com/next-step/java-lotto/pull/4232)** ## 나의 학습 목표 - TDD 사이클로 구현 - 객체지향 생활 체조 원칙 준수 @@ -62,14 +62,12 @@ ### 로또 구매 - [x] 로또 구매 개수 (LottoCount) - - [x] 1개 이상의 정수로 생성 - - [x] 1개 미만일 시 예외 발생 - -- [x] 수동 구매 개수 (ManualLottoCount) - - [x] 0 이상의 정수로 생성 - - [x] 문자열 입력으로 생성 - - [x] 빈 문자열일 시 예외 발생 - - [x] 음수일 시 예외 발생 + - [x] 0 이상의 정수로 생성 + - [x] 문자열 입력으로 생성 + - [x] 빈 문자열일 시 예외 발생 + - [x] 음수일 시 예외 발생 + - [x] 다른 개수와 차감 연산 + - [x] 차감 결과가 음수일 시 예외 발생 - [x] 로또 구매 금액 (LottoPurchaseAmount) - [x] 금액으로 생성 @@ -83,11 +81,10 @@ - [x] 로또 목록으로 생성 - [x] 빈 목록일 시 예외 발생 - [x] 당첨 로또와 비교하여 결과 생성 - - [x] 구매 개수 출력용 문자열 반환 ### 당첨 판정 - [x] 당첨 로또 (WinningLotto) - - [x] 로또 번호 목록과 보너스 번호로 생성 + - [x] 로또와 보너스 번호로 생성 - [x] 보너스 번호가 당첨 번호와 중복 시 예외 발생 - [x] 구매 로또와 비교하여 등수 판정