From 84a584fc86c6bdd51270c093cc4e1dbfa3bb0a00 Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 14:45:12 +0900 Subject: [PATCH 01/51] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 5 +++++ src/main/java/controller/LottoController.java | 4 ++++ src/main/java/domain/Lotto.java | 5 +++++ src/main/java/domain/LottoShop.java | 6 ++++++ src/main/java/domain/NumberGenerator.java | 5 +++++ src/main/java/domain/RandomNumberGenerator.java | 10 ++++++++++ src/main/java/view/InputView.java | 4 ++++ src/main/java/view/OutputView.java | 4 ++++ 8 files changed, 43 insertions(+) create mode 100644 src/main/java/Application.java create mode 100644 src/main/java/controller/LottoController.java create mode 100644 src/main/java/domain/Lotto.java create mode 100644 src/main/java/domain/LottoShop.java create mode 100644 src/main/java/domain/NumberGenerator.java create mode 100644 src/main/java/domain/RandomNumberGenerator.java create mode 100644 src/main/java/view/InputView.java create mode 100644 src/main/java/view/OutputView.java diff --git a/src/main/java/Application.java b/src/main/java/Application.java new file mode 100644 index 000000000..bdbffd531 --- /dev/null +++ b/src/main/java/Application.java @@ -0,0 +1,5 @@ +public class Application { + public static void main(String[] args) { + + } +} diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java new file mode 100644 index 000000000..7ca1c2e53 --- /dev/null +++ b/src/main/java/controller/LottoController.java @@ -0,0 +1,4 @@ +package controller; + +public class LottoController { +} diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java new file mode 100644 index 000000000..0a776568c --- /dev/null +++ b/src/main/java/domain/Lotto.java @@ -0,0 +1,5 @@ +package domain; + +public class Lotto { + +} diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java new file mode 100644 index 000000000..fe76cdb60 --- /dev/null +++ b/src/main/java/domain/LottoShop.java @@ -0,0 +1,6 @@ +package domain; + +import java.util.List; + +public class LottoShop { +} diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java new file mode 100644 index 000000000..03d271e6b --- /dev/null +++ b/src/main/java/domain/NumberGenerator.java @@ -0,0 +1,5 @@ +package domain; + +public interface NumberGenerator { + int generator(); +} diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java new file mode 100644 index 000000000..9e086558a --- /dev/null +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -0,0 +1,10 @@ +package domain; + +public class RandomNumberGenerator implements NumberGenerator { + + + @Override + public int generator() { + return 0; + } +} diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java new file mode 100644 index 000000000..ae2791fb0 --- /dev/null +++ b/src/main/java/view/InputView.java @@ -0,0 +1,4 @@ +package view; + +public class InputView { +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java new file mode 100644 index 000000000..d8f9743cc --- /dev/null +++ b/src/main/java/view/OutputView.java @@ -0,0 +1,4 @@ +package view; + +public class OutputView { +} From 57a69966c9a65a95b7f46e33bafa07b8ed60ad8c Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 14:46:24 +0900 Subject: [PATCH 02/51] =?UTF-8?q?feat:=20Lotto=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 0a776568c..1e3d8d855 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -1,5 +1,36 @@ package domain; +import java.util.Collections; +import java.util.List; + public class Lotto { + private final List numbers; + private final int LOTTO_SIZE = 6; + + public Lotto(List numbers) { + validate(numbers); + this.numbers = Collections.unmodifiableList(numbers); + } + + public List getNumbers() { + return numbers; + } + + private void validate(List numbers) { + validateLottoSize(numbers); + validateDuplicate(numbers); + } + + private void validateLottoSize(List numbers) { + if (numbers.size() != LOTTO_SIZE) { + throw new IllegalArgumentException("로또 숫자의 갯수는 6개입니다."); + } + } + private void validateDuplicate(List numbers) { + long distinctCount = numbers.stream().distinct().count(); + if(distinctCount != LOTTO_SIZE){ + throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); + } + } } From 65c7547bd211100e60a019136ab58ffc8f3e87cd Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 15:06:28 +0900 Subject: [PATCH 03/51] =?UTF-8?q?feat:=20RandomNumberGenerator=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 2 +- src/main/java/domain/LottoShop.java | 2 -- src/main/java/domain/NumberGenerator.java | 4 +++- .../java/domain/RandomNumberGenerator.java | 23 ++++++++++++++++--- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 1e3d8d855..4a83263d4 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -29,7 +29,7 @@ private void validateLottoSize(List numbers) { private void validateDuplicate(List numbers) { long distinctCount = numbers.stream().distinct().count(); - if(distinctCount != LOTTO_SIZE){ + if (distinctCount != LOTTO_SIZE) { throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); } } diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java index fe76cdb60..77aa4e160 100644 --- a/src/main/java/domain/LottoShop.java +++ b/src/main/java/domain/LottoShop.java @@ -1,6 +1,4 @@ package domain; -import java.util.List; - public class LottoShop { } diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java index 03d271e6b..678f6126c 100644 --- a/src/main/java/domain/NumberGenerator.java +++ b/src/main/java/domain/NumberGenerator.java @@ -1,5 +1,7 @@ package domain; +import java.util.List; + public interface NumberGenerator { - int generator(); + List generator(); } diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java index 9e086558a..88613754e 100644 --- a/src/main/java/domain/RandomNumberGenerator.java +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -1,10 +1,27 @@ package domain; -public class RandomNumberGenerator implements NumberGenerator { +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +public class RandomNumberGenerator implements NumberGenerator { + private static final int LOTTO_START_NUMBER = 1; + private static final int LOTTO_END_NUMBER = 45; + private static final int LOTTO_SIZE = 6; @Override - public int generator() { - return 0; + public List generator() { + List lottoNumbers = new ArrayList<>(); + + for (int i = LOTTO_START_NUMBER; i <= LOTTO_END_NUMBER; i++) { + lottoNumbers.add(i); + } + + Collections.shuffle(lottoNumbers); + + return lottoNumbers.subList(0, LOTTO_SIZE) + .stream() + .sorted() + .toList(); } } From 0fcc57b6a39f64088ce7f68a974719c656ee7a0e Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 17:32:06 +0900 Subject: [PATCH 04/51] =?UTF-8?q?feat:=20LottoShop=20=EB=B0=8F=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20Lottos=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/LottoShop.java | 35 +++++++++++++++++++++++++++++ src/main/java/domain/Lottos.java | 16 +++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/domain/Lottos.java diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java index 77aa4e160..4ec3c4788 100644 --- a/src/main/java/domain/LottoShop.java +++ b/src/main/java/domain/LottoShop.java @@ -1,4 +1,39 @@ package domain; +import java.util.ArrayList; +import java.util.List; + public class LottoShop { + private static final int LOTTO_PRICE = 1000; + private final NumberGenerator numberGenerator; + + public LottoShop(NumberGenerator numberGenerator) { + this.numberGenerator = numberGenerator; + } + + public Lottos purchase(int amount) { + validateAmount(amount); + return new Lottos(createLottos(amount)); + } + + private List createLottos(int amount) { + List lottos = new ArrayList<>(); + for (int i = 0; i < calculateCount(amount); i++) { + lottos.add(new Lotto(numberGenerator.generator())); + } + return lottos; + } + + private int calculateCount(int amount) { + return amount / LOTTO_PRICE; + } + + private void validateAmount(int amount) { + if (amount < LOTTO_PRICE) { + throw new IllegalArgumentException("구입 금액은 1000원 이상이어야 합니다."); + } + if (amount % LOTTO_PRICE != 0) { + throw new IllegalArgumentException("구입 금액은 1000원 단위여야 합니다."); + } + } } diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java new file mode 100644 index 000000000..3c5a17c36 --- /dev/null +++ b/src/main/java/domain/Lottos.java @@ -0,0 +1,16 @@ +package domain; + +import java.util.Collections; +import java.util.List; + +public class Lottos { + private final List lottos; + + public Lottos(List values) { + this.lottos = values; + } + + public List getLottos() { + return Collections.unmodifiableList(lottos); + } +} From 21cbf6895b8d9fd921a4c815bc064d781bf333a7 Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 18:11:36 +0900 Subject: [PATCH 05/51] =?UTF-8?q?feat:=20InputView=EC=99=80=20OutputView?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lottos.java | 8 ++++++-- src/main/java/view/InputView.java | 9 +++++++++ src/main/java/view/OutputView.java | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 3c5a17c36..4ec6c5641 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -6,11 +6,15 @@ public class Lottos { private final List lottos; - public Lottos(List values) { - this.lottos = values; + public Lottos(List lottos) { + this.lottos = lottos; } public List getLottos() { return Collections.unmodifiableList(lottos); } + + public int size() { + return lottos.size(); + } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index ae2791fb0..edd1ab71e 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,4 +1,13 @@ package view; +import java.util.Scanner; + public class InputView { + private final Scanner scanner = new Scanner(System.in); + + public int readAmount() { + System.out.println("구입금액을 입력해 주세요."); + + return scanner.nextInt(); + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index d8f9743cc..1b0ef42e2 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,4 +1,19 @@ package view; +import domain.Lotto; +import domain.Lottos; + public class OutputView { + + public void printLottos(Lottos lottos) { + printResultHeader(lottos.size()); + + for (Lotto lotto : lottos.getLottos()) { + System.out.println(lotto.getNumbers()); + } + } + + private void printResultHeader(int count) { + System.out.println(count + "개를 구매했습니다."); + } } From c22fb34d7cfab2bde71f3e227b6d98bf28c2c722 Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 18:25:45 +0900 Subject: [PATCH 06/51] =?UTF-8?q?feat:=20LottoController=20=EC=99=80=20App?= =?UTF-8?q?lication=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 5 ++++- src/main/java/controller/LottoController.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index bdbffd531..1f2fe1baf 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,5 +1,8 @@ +import controller.LottoController; + public class Application { public static void main(String[] args) { - + LottoController lottoController = new LottoController(); + lottoController.run(); } } diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 7ca1c2e53..79b0e68c4 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,4 +1,19 @@ package controller; +import domain.LottoShop; +import domain.Lottos; +import domain.RandomNumberGenerator; +import view.InputView; +import view.OutputView; + public class LottoController { + private final InputView inputView = new InputView(); + private final OutputView outputView = new OutputView(); + private final LottoShop lottoShop = new LottoShop(new RandomNumberGenerator()); + + public void run() { + int amount = inputView.readAmount(); + Lottos lottos = lottoShop.purchase(amount); + outputView.printLottos(lottos); + } } From 00d965e0656d2f4f79429caddf3ece7f56bec8b3 Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 18:41:54 +0900 Subject: [PATCH 07/51] =?UTF-8?q?fix:=20Lottos=20=EB=B0=A9=EC=96=B4?= =?UTF-8?q?=EC=A0=81=20=EB=B3=B5=EC=82=AC=20=EB=B0=8F=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EB=AA=85=EC=B9=AD=20=EB=B3=80=EA=B2=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/LottoShop.java | 2 +- src/main/java/domain/Lottos.java | 3 ++- src/main/java/domain/NumberGenerator.java | 2 +- src/main/java/domain/RandomNumberGenerator.java | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java index 4ec3c4788..27997544e 100644 --- a/src/main/java/domain/LottoShop.java +++ b/src/main/java/domain/LottoShop.java @@ -19,7 +19,7 @@ public Lottos purchase(int amount) { private List createLottos(int amount) { List lottos = new ArrayList<>(); for (int i = 0; i < calculateCount(amount); i++) { - lottos.add(new Lotto(numberGenerator.generator())); + lottos.add(new Lotto(numberGenerator.generate())); } return lottos; } diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 4ec6c5641..301a4b425 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -1,5 +1,6 @@ package domain; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -7,7 +8,7 @@ public class Lottos { private final List lottos; public Lottos(List lottos) { - this.lottos = lottos; + this.lottos = new ArrayList<>(lottos); } public List getLottos() { diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java index 678f6126c..9b7e9dcba 100644 --- a/src/main/java/domain/NumberGenerator.java +++ b/src/main/java/domain/NumberGenerator.java @@ -3,5 +3,5 @@ import java.util.List; public interface NumberGenerator { - List generator(); + List generate(); } diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java index 88613754e..ddfd320bc 100644 --- a/src/main/java/domain/RandomNumberGenerator.java +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -10,7 +10,7 @@ public class RandomNumberGenerator implements NumberGenerator { private static final int LOTTO_SIZE = 6; @Override - public List generator() { + public List generate() { List lottoNumbers = new ArrayList<>(); for (int i = LOTTO_START_NUMBER; i <= LOTTO_END_NUMBER; i++) { From c785cf3810715af2110dde5c0932bcf9e83f7fcc Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 18:55:10 +0900 Subject: [PATCH 08/51] =?UTF-8?q?refactor:=20OutputView=EA=B0=80=20Lottos?= =?UTF-8?q?=EC=97=90=20=EC=9D=98=EC=A1=B4=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 3 ++- src/main/java/domain/Lottos.java | 6 ++++++ src/main/java/view/OutputView.java | 16 ++++++++-------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 79b0e68c4..4ed7dc76b 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -14,6 +14,7 @@ public class LottoController { public void run() { int amount = inputView.readAmount(); Lottos lottos = lottoShop.purchase(amount); - outputView.printLottos(lottos); + outputView.printResultHeader(lottos.size()); + outputView.printLottos(lottos.toNumberLists()); } } diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 301a4b425..f3655e3d6 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -18,4 +18,10 @@ public List getLottos() { public int size() { return lottos.size(); } + + public List> toNumberLists() { + return lottos.stream() + .map(Lotto::getNumbers) + .toList(); + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 1b0ef42e2..466a120cb 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -3,17 +3,17 @@ import domain.Lotto; import domain.Lottos; -public class OutputView { +import java.util.List; - public void printLottos(Lottos lottos) { - printResultHeader(lottos.size()); +public class OutputView { - for (Lotto lotto : lottos.getLottos()) { - System.out.println(lotto.getNumbers()); - } + public void printResultHeader(int count) { + System.out.println(count + "개를 구매했습니다."); } - private void printResultHeader(int count) { - System.out.println(count + "개를 구매했습니다."); + public void printLottos(List> lottoNumbers) { + for (List numbers : lottoNumbers) { + System.out.println(numbers); + } } } From 2f266989695d55bbacaccd25b00affabf84c0748 Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 19:17:27 +0900 Subject: [PATCH 09/51] =?UTF-8?q?test:=20LottoTest=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/view/OutputView.java | 3 --- src/test/java/domain/LottoTest.java | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/test/java/domain/LottoTest.java diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 466a120cb..4fd22d88f 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,8 +1,5 @@ package view; -import domain.Lotto; -import domain.Lottos; - import java.util.List; public class OutputView { diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java new file mode 100644 index 000000000..4576d595c --- /dev/null +++ b/src/test/java/domain/LottoTest.java @@ -0,0 +1,24 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class LottoTest { + + @Test + void 로또_숫자의_갯수는_6개가_아니라면_예외가_발생한다() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("로또 숫자의 갯수는 6개입니다."); + } + + @Test + void 로또_번호가_중복되면_예외가_발생한다() { + assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 3, 5, 6))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("로또 번호는 중복될 수 없습니다."); + } +} From de6d26edc7e31a60e6ce043eaaeb9aacc2f15652 Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 29 Mar 2026 19:28:13 +0900 Subject: [PATCH 10/51] =?UTF-8?q?test:=20LottosTest,=20LottoShopTest,=20Te?= =?UTF-8?q?stNumberGenerator=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoShopTest.java | 45 +++++++++++++++++++ src/test/java/domain/LottosTest.java | 25 +++++++++++ src/test/java/domain/TestNumberGenerator.java | 16 +++++++ 3 files changed, 86 insertions(+) create mode 100644 src/test/java/domain/LottoShopTest.java create mode 100644 src/test/java/domain/LottosTest.java create mode 100644 src/test/java/domain/TestNumberGenerator.java diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java new file mode 100644 index 000000000..fc5de079e --- /dev/null +++ b/src/test/java/domain/LottoShopTest.java @@ -0,0 +1,45 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LottoShopTest { + + @Test + void 구입금액만큼_로또를_구매한다() { + NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); + LottoShop lottoShop = new LottoShop(numberGenerator); + + Lottos lottos = lottoShop.purchase(3000); + + assertThat(lottos.toNumberLists()).containsExactly( + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 6) + ); + } + + @Test + void 구입금액이_1000원_미만이면_예외가_발생한다() { + NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); + LottoShop lottoShop = new LottoShop(numberGenerator); + + assertThatThrownBy(() -> lottoShop.purchase(500)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("구입 금액은 1000원 이상이어야 합니다."); + } + + @Test + void 구입금액이_1000원_단위가_아니면_예외가_발생한다() { + NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); + LottoShop lottoShop = new LottoShop(numberGenerator); + + assertThatThrownBy(() -> lottoShop.purchase(1500)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("구입 금액은 1000원 단위여야 합니다."); + } +} diff --git a/src/test/java/domain/LottosTest.java b/src/test/java/domain/LottosTest.java new file mode 100644 index 000000000..1e9add135 --- /dev/null +++ b/src/test/java/domain/LottosTest.java @@ -0,0 +1,25 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class LottosTest { + + @Test + void 로또_목록을_번호_리스트로_변환한다() { + Lottos lottos = new Lottos(List.of( + new Lotto(List.of(1, 2, 3, 4, 5, 6)), + new Lotto(List.of(7, 8, 9, 10, 11, 12)) + )); + + List> numberLists = lottos.toNumberLists(); + + assertThat(numberLists).isEqualTo(List.of( + List.of(1, 2, 3, 4, 5, 6), + List.of(7, 8, 9, 10, 11, 12) + )); + } +} diff --git a/src/test/java/domain/TestNumberGenerator.java b/src/test/java/domain/TestNumberGenerator.java new file mode 100644 index 000000000..f85f4daf9 --- /dev/null +++ b/src/test/java/domain/TestNumberGenerator.java @@ -0,0 +1,16 @@ +package domain; + +import java.util.List; + +public class TestNumberGenerator implements NumberGenerator { + private final List numbers; + + public TestNumberGenerator(List numbers) { + this.numbers = List.copyOf(numbers); + } + + @Override + public List generate() { + return numbers; + } +} From ddc142ab503440fd5d7a8997353fb7e7b334e469 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 12:47:33 +0900 Subject: [PATCH 11/51] =?UTF-8?q?feat:=20=EC=A7=80=EB=82=9C=20=EC=A3=BC=20?= =?UTF-8?q?=EB=8B=B9=EC=B2=A8=20=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 2 ++ src/main/java/view/InputView.java | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 4ed7dc76b..e8e122d65 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,5 +1,6 @@ package controller; +import domain.Lotto; import domain.LottoShop; import domain.Lottos; import domain.RandomNumberGenerator; @@ -16,5 +17,6 @@ public void run() { Lottos lottos = lottoShop.purchase(amount); outputView.printResultHeader(lottos.size()); outputView.printLottos(lottos.toNumberLists()); + Lotto winningLotto = new Lotto(inputView.readWinningNumbers()); } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index edd1ab71e..14993762e 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,5 +1,7 @@ package view; +import java.util.Arrays; +import java.util.List; import java.util.Scanner; public class InputView { @@ -7,7 +9,20 @@ public class InputView { public int readAmount() { System.out.println("구입금액을 입력해 주세요."); + return Integer.parseInt(scanner.nextLine()); + } + + public List readWinningNumbers() { + System.out.println("지난 주 당첨 번호를 입력해 주세요."); + String input = scanner.nextLine(); + return parseWinningNumbers(input); + } - return scanner.nextInt(); + private List parseWinningNumbers(String input) { + return Arrays.stream(input.split(",")) + .map(String::trim) + .map(Integer::parseInt) + .sorted() + .toList(); } } From d3f7ba7e810fe334ef823fb622da4a3db1c233b1 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 13:50:04 +0900 Subject: [PATCH 12/51] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=20=ED=86=B5=EA=B3=84=20=EC=A7=91=EA=B3=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 6 +-- src/main/java/domain/Lotto.java | 10 +++++ src/main/java/domain/Lottos.java | 17 +++++--- src/main/java/domain/Rank.java | 41 +++++++++++++++++++ src/main/java/domain/WinningStatistics.java | 29 +++++++++++++ 5 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/main/java/domain/Rank.java create mode 100644 src/main/java/domain/WinningStatistics.java diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index e8e122d65..e77ed6c7d 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,9 +1,6 @@ package controller; -import domain.Lotto; -import domain.LottoShop; -import domain.Lottos; -import domain.RandomNumberGenerator; +import domain.*; import view.InputView; import view.OutputView; @@ -18,5 +15,6 @@ public void run() { outputView.printResultHeader(lottos.size()); outputView.printLottos(lottos.toNumberLists()); Lotto winningLotto = new Lotto(inputView.readWinningNumbers()); + WinningStatistics winningStatistics = lottos.createWinningStatistics(winningLotto); } } diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 4a83263d4..7a4ac4f48 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -16,6 +16,16 @@ public List getNumbers() { return numbers; } + public int countMatch(Lotto winningLotto) { + return (int) numbers.stream() + .filter(winningLotto::contains) + .count(); + } + + private boolean contains(int number) { + return numbers.contains(number); + } + private void validate(List numbers) { validateLottoSize(numbers); validateDuplicate(numbers); diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index f3655e3d6..4074a32c5 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -1,7 +1,6 @@ package domain; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class Lottos { @@ -11,10 +10,6 @@ public Lottos(List lottos) { this.lottos = new ArrayList<>(lottos); } - public List getLottos() { - return Collections.unmodifiableList(lottos); - } - public int size() { return lottos.size(); } @@ -24,4 +19,16 @@ public List> toNumberLists() { .map(Lotto::getNumbers) .toList(); } + + public WinningStatistics createWinningStatistics(Lotto winningLotto) { + WinningStatistics winningStatistics = new WinningStatistics(); + for (Lotto lotto : lottos) { + winningStatistics.add(findRank(lotto, winningLotto)); + } + return winningStatistics; + } + + private Rank findRank(Lotto lotto, Lotto winningLotto) { + return Rank.from(lotto.countMatch(winningLotto)); + } } diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java new file mode 100644 index 000000000..6e0cf8ff5 --- /dev/null +++ b/src/main/java/domain/Rank.java @@ -0,0 +1,41 @@ +package domain; + +public enum Rank { + THREE_MATCH(3, 5000), + FOUR_MATCH(4, 50000), + FIVE_MATCH(5, 1500000), + SIX_MATCH(6, 2000000000), + MISS(0, 0); + + private final int matchCount; + private final int prizeMoney; + + Rank(int matchCount, int prizeMoney) { + this.matchCount = matchCount; + this.prizeMoney = prizeMoney; + } + + public static Rank from(int matchCount) { + if (matchCount == 6) { + return SIX_MATCH; + } + if (matchCount == 5) { + return FIVE_MATCH; + } + if (matchCount == 4) { + return FOUR_MATCH; + } + if (matchCount == 3) { + return THREE_MATCH; + } + return MISS; + } + + public int getPrizeMoney() { + return prizeMoney; + } + + public int getMatchCount() { + return matchCount; + } +} diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java new file mode 100644 index 000000000..1842fed71 --- /dev/null +++ b/src/main/java/domain/WinningStatistics.java @@ -0,0 +1,29 @@ +package domain; + +import java.util.HashMap; +import java.util.Map; + +public class WinningStatistics { + private final Map statistics; + + public WinningStatistics() { + statistics = new HashMap<>(); + initialize(); + } + + private void initialize() { + for (Rank rank : Rank.values()) { + statistics.put(rank, 0); + } + } + + public void add(Rank rank) { + int rankCount = statistics.get(rank); + rankCount++; + statistics.put(rank, rankCount); + } + + public int countOf(Rank rank) { + return statistics.get(rank); + } +} From 4811aef28d19c409c38733cdd967c9c55f41d144 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 15:24:42 +0900 Subject: [PATCH 13/51] =?UTF-8?q?feat:=20=EB=A1=9C=EB=98=90=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=20=ED=86=B5=EA=B3=84=20=EB=B0=8F=20=EC=88=98=EC=9D=B5?= =?UTF-8?q?=EB=A5=A0=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 18 +++++++++++++++ src/main/java/domain/WinningStatistics.java | 10 ++++++++ src/main/java/dto/WinningResult.java | 4 ++++ src/main/java/view/OutputView.java | 23 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 src/main/java/dto/WinningResult.java diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index e77ed6c7d..826bf6a94 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,9 +1,12 @@ package controller; import domain.*; +import dto.WinningResult; import view.InputView; import view.OutputView; +import java.util.List; + public class LottoController { private final InputView inputView = new InputView(); private final OutputView outputView = new OutputView(); @@ -16,5 +19,20 @@ public void run() { outputView.printLottos(lottos.toNumberLists()); Lotto winningLotto = new Lotto(inputView.readWinningNumbers()); WinningStatistics winningStatistics = lottos.createWinningStatistics(winningLotto); + outputView.printWinningStatistics(createWinningResults(winningStatistics)); + outputView.printProfitRate(winningStatistics.calculateProfitRate(amount)); + } + + private List createWinningResults(WinningStatistics winningStatistics) { + return List.of( + createWinningResult(winningStatistics, Rank.THREE_MATCH), + createWinningResult(winningStatistics, Rank.FOUR_MATCH), + createWinningResult(winningStatistics, Rank.FIVE_MATCH), + createWinningResult(winningStatistics, Rank.SIX_MATCH) + ); + } + + private WinningResult createWinningResult(WinningStatistics winningStatistics, Rank rank) { + return new WinningResult(rank.getMatchCount(), rank.getPrizeMoney(), winningStatistics.countOf(rank)); } } diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 1842fed71..b3e82d1e5 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -26,4 +26,14 @@ public void add(Rank rank) { public int countOf(Rank rank) { return statistics.get(rank); } + + public long calculateTotalPrize() { + return statistics.entrySet().stream() + .mapToLong(entry -> (long) entry.getKey().getPrizeMoney() * entry.getValue()) + .sum(); + } + + public double calculateProfitRate(int amount) { + return (double) calculateTotalPrize() / amount; + } } diff --git a/src/main/java/dto/WinningResult.java b/src/main/java/dto/WinningResult.java new file mode 100644 index 000000000..f6ace10a3 --- /dev/null +++ b/src/main/java/dto/WinningResult.java @@ -0,0 +1,4 @@ +package dto; + +public record WinningResult(int matchCount, int prizeMoney, int count) { +} diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 4fd22d88f..ee487d0f4 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -1,5 +1,7 @@ package view; +import dto.WinningResult; + import java.util.List; public class OutputView { @@ -13,4 +15,25 @@ public void printLottos(List> lottoNumbers) { System.out.println(numbers); } } + + public void printWinningStatistics(List winningResults) { + printStatisticsHeader(); + printRankCount(winningResults); + } + + private void printStatisticsHeader() { + System.out.println(); + System.out.println("당첨 통계"); + System.out.println("---------"); + } + + private void printRankCount(List winningResults) { + for (WinningResult winningResult : winningResults) { + System.out.println(winningResult.matchCount() + "개 일치" + " (" + winningResult.prizeMoney() + "원)-" + winningResult.count() + "개"); + } + } + + public void printProfitRate(double profitRate) { + System.out.println("총 수익률은 " + String.format("%.2f", profitRate) + "입니다."); + } } From e2d7f58b2018ee789a555ad8832db3944d9ca4a6 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 16:02:53 +0900 Subject: [PATCH 14/51] =?UTF-8?q?refactor:=20=EA=B5=AC=EC=9E=85=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=EC=9D=84=20PurchaseAmount=EB=A1=9C=20?= =?UTF-8?q?=ED=8F=AC=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 6 ++-- src/main/java/domain/LottoShop.java | 23 +++------------ src/main/java/domain/PurchaseAmount.java | 28 +++++++++++++++++++ src/main/java/domain/WinningStatistics.java | 4 +-- src/test/java/domain/LottoShopTest.java | 23 +-------------- 5 files changed, 38 insertions(+), 46 deletions(-) create mode 100644 src/main/java/domain/PurchaseAmount.java diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 826bf6a94..52d586dbc 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -13,14 +13,14 @@ public class LottoController { private final LottoShop lottoShop = new LottoShop(new RandomNumberGenerator()); public void run() { - int amount = inputView.readAmount(); - Lottos lottos = lottoShop.purchase(amount); + PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); + Lottos lottos = lottoShop.purchase(purchaseAmount); outputView.printResultHeader(lottos.size()); outputView.printLottos(lottos.toNumberLists()); Lotto winningLotto = new Lotto(inputView.readWinningNumbers()); WinningStatistics winningStatistics = lottos.createWinningStatistics(winningLotto); outputView.printWinningStatistics(createWinningResults(winningStatistics)); - outputView.printProfitRate(winningStatistics.calculateProfitRate(amount)); + outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } private List createWinningResults(WinningStatistics winningStatistics) { diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java index 27997544e..84f586d45 100644 --- a/src/main/java/domain/LottoShop.java +++ b/src/main/java/domain/LottoShop.java @@ -4,36 +4,21 @@ import java.util.List; public class LottoShop { - private static final int LOTTO_PRICE = 1000; private final NumberGenerator numberGenerator; public LottoShop(NumberGenerator numberGenerator) { this.numberGenerator = numberGenerator; } - public Lottos purchase(int amount) { - validateAmount(amount); - return new Lottos(createLottos(amount)); + public Lottos purchase(PurchaseAmount purchaseAmount) { + return new Lottos(createLottos(purchaseAmount)); } - private List createLottos(int amount) { + private List createLottos(PurchaseAmount purchaseAmount) { List lottos = new ArrayList<>(); - for (int i = 0; i < calculateCount(amount); i++) { + for (int i = 0; i < purchaseAmount.calculateLottoCount(); i++) { lottos.add(new Lotto(numberGenerator.generate())); } return lottos; } - - private int calculateCount(int amount) { - return amount / LOTTO_PRICE; - } - - private void validateAmount(int amount) { - if (amount < LOTTO_PRICE) { - throw new IllegalArgumentException("구입 금액은 1000원 이상이어야 합니다."); - } - if (amount % LOTTO_PRICE != 0) { - throw new IllegalArgumentException("구입 금액은 1000원 단위여야 합니다."); - } - } } diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java new file mode 100644 index 000000000..f5c226593 --- /dev/null +++ b/src/main/java/domain/PurchaseAmount.java @@ -0,0 +1,28 @@ +package domain; + +public class PurchaseAmount { + private static final int LOTTO_PRICE = 1000; + private final int amount; + + public PurchaseAmount(int amount) { + validateAmount(amount); + this.amount = amount; + } + + private void validateAmount(int amount) { + if (amount < LOTTO_PRICE) { + throw new IllegalArgumentException("구입 금액은 1000원 이상이어야 합니다."); + } + if (amount % LOTTO_PRICE != 0) { + throw new IllegalArgumentException("구입 금액은 1000원 단위여야 합니다."); + } + } + + public int calculateLottoCount() { + return amount / LOTTO_PRICE; + } + + public int getAmount() { + return amount; + } +} diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index b3e82d1e5..bcc368955 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -33,7 +33,7 @@ public long calculateTotalPrize() { .sum(); } - public double calculateProfitRate(int amount) { - return (double) calculateTotalPrize() / amount; + public double calculateProfitRate(PurchaseAmount purchaseAmount) { + return (double) calculateTotalPrize() / purchaseAmount.getAmount(); } } diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java index fc5de079e..b609b90da 100644 --- a/src/test/java/domain/LottoShopTest.java +++ b/src/test/java/domain/LottoShopTest.java @@ -5,7 +5,6 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; class LottoShopTest { @@ -14,7 +13,7 @@ class LottoShopTest { NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); LottoShop lottoShop = new LottoShop(numberGenerator); - Lottos lottos = lottoShop.purchase(3000); + Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000)); assertThat(lottos.toNumberLists()).containsExactly( List.of(1, 2, 3, 4, 5, 6), @@ -22,24 +21,4 @@ class LottoShopTest { List.of(1, 2, 3, 4, 5, 6) ); } - - @Test - void 구입금액이_1000원_미만이면_예외가_발생한다() { - NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); - LottoShop lottoShop = new LottoShop(numberGenerator); - - assertThatThrownBy(() -> lottoShop.purchase(500)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("구입 금액은 1000원 이상이어야 합니다."); - } - - @Test - void 구입금액이_1000원_단위가_아니면_예외가_발생한다() { - NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); - LottoShop lottoShop = new LottoShop(numberGenerator); - - assertThatThrownBy(() -> lottoShop.purchase(1500)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("구입 금액은 1000원 단위여야 합니다."); - } } From fffdd214ea49327a59f8c100cc79b963d97056de Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 16:37:59 +0900 Subject: [PATCH 15/51] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=EB=A5=BC=EC=9D=84=20LottoNumber=EB=A1=9C=20?= =?UTF-8?q?=ED=8F=AC=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 11 +++++++++- src/main/java/domain/Lotto.java | 16 +++++++-------- src/main/java/domain/LottoNumber.java | 20 +++++++++++++++++++ src/main/java/domain/Lottos.java | 9 ++++++++- src/main/java/domain/NumberGenerator.java | 2 +- src/main/java/domain/PurchaseAmount.java | 10 ++-------- .../java/domain/RandomNumberGenerator.java | 9 +++++---- src/main/java/domain/WinningStatistics.java | 2 +- src/test/java/domain/TestNumberGenerator.java | 2 +- 9 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 src/main/java/domain/LottoNumber.java diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 52d586dbc..91e974be7 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -17,12 +17,21 @@ public void run() { Lottos lottos = lottoShop.purchase(purchaseAmount); outputView.printResultHeader(lottos.size()); outputView.printLottos(lottos.toNumberLists()); - Lotto winningLotto = new Lotto(inputView.readWinningNumbers()); + + List winningNumbers = inputView.readWinningNumbers(); + Lotto winningLotto = new Lotto(toLottoNumbers(winningNumbers)); + WinningStatistics winningStatistics = lottos.createWinningStatistics(winningLotto); outputView.printWinningStatistics(createWinningResults(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } + private List toLottoNumbers(List numbers) { + return numbers.stream() + .map(LottoNumber::new) + .toList(); + } + private List createWinningResults(WinningStatistics winningStatistics) { return List.of( createWinningResult(winningStatistics, Rank.THREE_MATCH), diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 7a4ac4f48..15169d1f7 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -4,15 +4,15 @@ import java.util.List; public class Lotto { - private final List numbers; + private final List numbers; private final int LOTTO_SIZE = 6; - public Lotto(List numbers) { + public Lotto(List numbers) { validate(numbers); this.numbers = Collections.unmodifiableList(numbers); } - public List getNumbers() { + public List getNumbers() { return numbers; } @@ -22,22 +22,22 @@ public int countMatch(Lotto winningLotto) { .count(); } - private boolean contains(int number) { - return numbers.contains(number); + private boolean contains(LottoNumber lottoNumber) { + return numbers.contains(lottoNumber); } - private void validate(List numbers) { + private void validate(List numbers) { validateLottoSize(numbers); validateDuplicate(numbers); } - private void validateLottoSize(List numbers) { + private void validateLottoSize(List numbers) { if (numbers.size() != LOTTO_SIZE) { throw new IllegalArgumentException("로또 숫자의 갯수는 6개입니다."); } } - private void validateDuplicate(List numbers) { + private void validateDuplicate(List numbers) { long distinctCount = numbers.stream().distinct().count(); if (distinctCount != LOTTO_SIZE) { throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); diff --git a/src/main/java/domain/LottoNumber.java b/src/main/java/domain/LottoNumber.java new file mode 100644 index 000000000..8d3943ae2 --- /dev/null +++ b/src/main/java/domain/LottoNumber.java @@ -0,0 +1,20 @@ +package domain; + +public record LottoNumber(int number) { + private static final int MIN_NUMBER = 1; + private static final int MAX_NUMBER = 45; + + public LottoNumber { + validate(number); + } + + private void validate(int number) { + validateRange(number); + } + + private void validateRange(int number) { + if (number < MIN_NUMBER || number > MAX_NUMBER) { + throw new IllegalArgumentException("로또 번호는 1부터 45 사이여야 합니다."); + } + } +} diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 4074a32c5..1c0318a2f 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -16,7 +16,7 @@ public int size() { public List> toNumberLists() { return lottos.stream() - .map(Lotto::getNumbers) + .map(this::toNumbers) .toList(); } @@ -28,6 +28,13 @@ public WinningStatistics createWinningStatistics(Lotto winningLotto) { return winningStatistics; } + private List toNumbers(Lotto lotto) { + return lotto.getNumbers().stream() + .map(LottoNumber::number) + .toList(); + } + + private Rank findRank(Lotto lotto, Lotto winningLotto) { return Rank.from(lotto.countMatch(winningLotto)); } diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/domain/NumberGenerator.java index 9b7e9dcba..9a28ba8e4 100644 --- a/src/main/java/domain/NumberGenerator.java +++ b/src/main/java/domain/NumberGenerator.java @@ -3,5 +3,5 @@ import java.util.List; public interface NumberGenerator { - List generate(); + List generate(); } diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index f5c226593..f88362af6 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -1,12 +1,10 @@ package domain; -public class PurchaseAmount { +public record PurchaseAmount(int amount) { private static final int LOTTO_PRICE = 1000; - private final int amount; - public PurchaseAmount(int amount) { + public PurchaseAmount { validateAmount(amount); - this.amount = amount; } private void validateAmount(int amount) { @@ -21,8 +19,4 @@ private void validateAmount(int amount) { public int calculateLottoCount() { return amount / LOTTO_PRICE; } - - public int getAmount() { - return amount; - } } diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/domain/RandomNumberGenerator.java index ddfd320bc..f394f3fcc 100644 --- a/src/main/java/domain/RandomNumberGenerator.java +++ b/src/main/java/domain/RandomNumberGenerator.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; public class RandomNumberGenerator implements NumberGenerator { @@ -10,18 +11,18 @@ public class RandomNumberGenerator implements NumberGenerator { private static final int LOTTO_SIZE = 6; @Override - public List generate() { - List lottoNumbers = new ArrayList<>(); + public List generate() { + List lottoNumbers = new ArrayList<>(); for (int i = LOTTO_START_NUMBER; i <= LOTTO_END_NUMBER; i++) { - lottoNumbers.add(i); + lottoNumbers.add(new LottoNumber(i)); } Collections.shuffle(lottoNumbers); return lottoNumbers.subList(0, LOTTO_SIZE) .stream() - .sorted() + .sorted(Comparator.comparingInt(LottoNumber::number)) .toList(); } } diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index bcc368955..578968121 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -34,6 +34,6 @@ public long calculateTotalPrize() { } public double calculateProfitRate(PurchaseAmount purchaseAmount) { - return (double) calculateTotalPrize() / purchaseAmount.getAmount(); + return (double) calculateTotalPrize() / purchaseAmount.amount(); } } diff --git a/src/test/java/domain/TestNumberGenerator.java b/src/test/java/domain/TestNumberGenerator.java index f85f4daf9..1c6c5d52e 100644 --- a/src/test/java/domain/TestNumberGenerator.java +++ b/src/test/java/domain/TestNumberGenerator.java @@ -10,7 +10,7 @@ public TestNumberGenerator(List numbers) { } @Override - public List generate() { + public List generate() { return numbers; } } From 47405fe7037cc98927c59c9931b5bacfcd1dabc4 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 16:50:23 +0900 Subject: [PATCH 16/51] =?UTF-8?q?test:=20=EC=A0=84=EB=B0=98=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoNumberTest.java | 22 ++++++++++ src/test/java/domain/LottoShopTest.java | 9 +++- src/test/java/domain/LottoTest.java | 17 +++++++- src/test/java/domain/LottosTest.java | 18 +++++++- src/test/java/domain/PurchaseAmountTest.java | 30 +++++++++++++ src/test/java/domain/TestNumberGenerator.java | 4 +- .../java/domain/WinningStatisticsTest.java | 42 +++++++++++++++++++ 7 files changed, 135 insertions(+), 7 deletions(-) create mode 100644 src/test/java/domain/LottoNumberTest.java create mode 100644 src/test/java/domain/PurchaseAmountTest.java create mode 100644 src/test/java/domain/WinningStatisticsTest.java diff --git a/src/test/java/domain/LottoNumberTest.java b/src/test/java/domain/LottoNumberTest.java new file mode 100644 index 000000000..0ff61b63d --- /dev/null +++ b/src/test/java/domain/LottoNumberTest.java @@ -0,0 +1,22 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class LottoNumberTest { + + @Test + void 로또_번호가_1보다_작으면_예외가_발생한다() { + assertThatThrownBy(() -> new LottoNumber(0)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("로또 번호는 1부터 45 사이여야 합니다."); + } + + @Test + void 로또_번호가_45보다_크면_예외가_발생한다() { + assertThatThrownBy(() -> new LottoNumber(46)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("로또 번호는 1부터 45 사이여야 합니다."); + } +} diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java index b609b90da..0f3ea4c0e 100644 --- a/src/test/java/domain/LottoShopTest.java +++ b/src/test/java/domain/LottoShopTest.java @@ -10,7 +10,14 @@ class LottoShopTest { @Test void 구입금액만큼_로또를_구매한다() { - NumberGenerator numberGenerator = new TestNumberGenerator(List.of(1, 2, 3, 4, 5, 6)); + NumberGenerator numberGenerator = new TestNumberGenerator(List.of( + new LottoNumber(1), + new LottoNumber(2), + new LottoNumber(3), + new LottoNumber(4), + new LottoNumber(5), + new LottoNumber(6) + )); LottoShop lottoShop = new LottoShop(numberGenerator); Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000)); diff --git a/src/test/java/domain/LottoTest.java b/src/test/java/domain/LottoTest.java index 4576d595c..9f2c2d023 100644 --- a/src/test/java/domain/LottoTest.java +++ b/src/test/java/domain/LottoTest.java @@ -10,14 +10,27 @@ public class LottoTest { @Test void 로또_숫자의_갯수는_6개가_아니라면_예외가_발생한다() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 4, 5))) + assertThatThrownBy(() -> new Lotto(List.of( + new LottoNumber(1), + new LottoNumber(2), + new LottoNumber(3), + new LottoNumber(4), + new LottoNumber(5) + ))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("로또 숫자의 갯수는 6개입니다."); } @Test void 로또_번호가_중복되면_예외가_발생한다() { - assertThatThrownBy(() -> new Lotto(List.of(1, 2, 3, 3, 5, 6))) + assertThatThrownBy(() -> new Lotto(List.of( + new LottoNumber(1), + new LottoNumber(2), + new LottoNumber(3), + new LottoNumber(3), + new LottoNumber(5), + new LottoNumber(6) + ))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("로또 번호는 중복될 수 없습니다."); } diff --git a/src/test/java/domain/LottosTest.java b/src/test/java/domain/LottosTest.java index 1e9add135..1399c02d0 100644 --- a/src/test/java/domain/LottosTest.java +++ b/src/test/java/domain/LottosTest.java @@ -11,8 +11,22 @@ class LottosTest { @Test void 로또_목록을_번호_리스트로_변환한다() { Lottos lottos = new Lottos(List.of( - new Lotto(List.of(1, 2, 3, 4, 5, 6)), - new Lotto(List.of(7, 8, 9, 10, 11, 12)) + new Lotto(List.of( + new LottoNumber(1), + new LottoNumber(2), + new LottoNumber(3), + new LottoNumber(4), + new LottoNumber(5), + new LottoNumber(6) + )), + new Lotto(List.of( + new LottoNumber(7), + new LottoNumber(8), + new LottoNumber(9), + new LottoNumber(10), + new LottoNumber(11), + new LottoNumber(12) + )) )); List> numberLists = lottos.toNumberLists(); diff --git a/src/test/java/domain/PurchaseAmountTest.java b/src/test/java/domain/PurchaseAmountTest.java new file mode 100644 index 000000000..459d3891d --- /dev/null +++ b/src/test/java/domain/PurchaseAmountTest.java @@ -0,0 +1,30 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class PurchaseAmountTest { + + @Test + void 구입금액이_1000원_미만이면_예외가_발생한다() { + assertThatThrownBy(() -> new PurchaseAmount(999)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("구입 금액은 1000원 이상이어야 합니다."); + } + + @Test + void 구입금액이_1000원_단위가_아니면_예외가_발생한다() { + assertThatThrownBy(() -> new PurchaseAmount(1500)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("구입 금액은 1000원 단위여야 합니다."); + } + + @Test + void 구입금액으로_구매할_로또_개수를_계산한다() { + PurchaseAmount purchaseAmount = new PurchaseAmount(5000); + + assertThat(purchaseAmount.calculateLottoCount()).isEqualTo(5); + } +} diff --git a/src/test/java/domain/TestNumberGenerator.java b/src/test/java/domain/TestNumberGenerator.java index 1c6c5d52e..4998d9455 100644 --- a/src/test/java/domain/TestNumberGenerator.java +++ b/src/test/java/domain/TestNumberGenerator.java @@ -3,9 +3,9 @@ import java.util.List; public class TestNumberGenerator implements NumberGenerator { - private final List numbers; + private final List numbers; - public TestNumberGenerator(List numbers) { + public TestNumberGenerator(List numbers) { this.numbers = List.copyOf(numbers); } diff --git a/src/test/java/domain/WinningStatisticsTest.java b/src/test/java/domain/WinningStatisticsTest.java new file mode 100644 index 000000000..8793e5a5a --- /dev/null +++ b/src/test/java/domain/WinningStatisticsTest.java @@ -0,0 +1,42 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class WinningStatisticsTest { + + @Test + void 등수를_추가하면_개수가_증가한다() { + WinningStatistics winningStatistics = new WinningStatistics(); + + winningStatistics.add(Rank.THREE_MATCH); + winningStatistics.add(Rank.THREE_MATCH); + winningStatistics.add(Rank.FOUR_MATCH); + + assertThat(winningStatistics.countOf(Rank.THREE_MATCH)).isEqualTo(2); + assertThat(winningStatistics.countOf(Rank.FOUR_MATCH)).isEqualTo(1); + } + + @Test + void 총_당첨금을_계산한다() { + WinningStatistics winningStatistics = new WinningStatistics(); + + winningStatistics.add(Rank.THREE_MATCH); + winningStatistics.add(Rank.THREE_MATCH); + winningStatistics.add(Rank.FOUR_MATCH); + + assertThat(winningStatistics.calculateTotalPrize()).isEqualTo(60000); + } + + @Test + void 수익률을_계산한다() { + WinningStatistics winningStatistics = new WinningStatistics(); + PurchaseAmount purchaseAmount = new PurchaseAmount(10000); + + winningStatistics.add(Rank.THREE_MATCH); + winningStatistics.add(Rank.FOUR_MATCH); + + assertThat(winningStatistics.calculateProfitRate(purchaseAmount)).isEqualTo(5.5); + } +} From 7017a06eccc947a5deeb4a284746b5cb196e3321 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 17:10:27 +0900 Subject: [PATCH 17/51] =?UTF-8?q?docs:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/README.md | 137 ++++++++++++++++++ src/main/java/controller/LottoController.java | 2 + 2 files changed, 139 insertions(+) create mode 100644 src/README.md diff --git a/src/README.md b/src/README.md new file mode 100644 index 000000000..7d13d7032 --- /dev/null +++ b/src/README.md @@ -0,0 +1,137 @@ +# 로또 게임 + +## 프로젝트 개요 + +로또 구매 및 당첨 통계 계산 프로그램을 구현했습니다. +사용자는 구입 금액과 지난 주 당첨 번호를 입력할 수 있으며, 구입 금액에 해당하는 수만큼 로또를 자동으로 발급받습니다. +발급된 로또와 당첨 번호를 비교하여 당첨 통계를 계산하고, 총 수익률을 출력합니다. + +## 기술 스택 + +- Java +- Gradle +- JUnit5 +- AssertJ + +## 구현 기능 + +### LottoNumber + +- 로또 번호 하나를 관리한다. +- 로또 번호가 1보다 작거나 45보다 크면 예외를 발생시킨다. + +### Lotto + +- 로또 한 장의 번호 6개를 관리한다. +- 로또 번호가 6개가 아니면 예외를 발생시킨다. +- 로또 번호가 중복되면 예외를 발생시킨다. +- 당첨 번호와 비교하여 일치 개수를 계산한다. + +### Lottos + +- 여러 장의 로또를 관리한다. +- 구매한 로또 개수를 반환한다. +- 로또 목록을 출력용 번호 리스트로 변환한다. +- 당첨 번호를 기준으로 당첨 통계를 생성한다. + +### PurchaseAmount + +- 구입 금액을 관리한다. +- 구입 금액이 1000원 미만이면 예외를 발생시킨다. +- 구입 금액이 1000원 단위가 아니면 예외를 발생시킨다. +- 구입 금액으로 구매 가능한 로또 개수를 계산한다. + +### LottoShop + +- 로또 구매를 담당한다. +- 구입 금액에 해당하는 개수만큼 로또를 생성한다. + +### NumberGenerator + +- 로또 번호 생성 역할을 분리하기 위해 `NumberGenerator` 인터페이스를 사용했다. +- `RandomNumberGenerator`는 1부터 45 사이의 숫자 중 6개를 무작위로 생성한다. +- `TestNumberGenerator`는 테스트에서 원하는 번호를 고정으로 생성한다. + +### Rank + +- 당첨 등수를 관리한다. +- 일치 개수에 따라 3등, 4등, 5등, 6등을 판별한다. +- 각 등수에 해당하는 당첨 금액을 관리한다. + +### WinningStatistics + +- 등수별 당첨 개수를 관리한다. +- 등수별 당첨 개수를 증가시킨다. +- 총 당첨금을 계산한다. +- 구입 금액을 기준으로 수익률을 계산한다. + +### WinningResult + +- 당첨 통계 출력에 필요한 데이터를 관리한다. +- 일치 개수, 당첨 금액, 당첨 개수를 담아 출력 계층으로 전달한다. + +### InputView + +- 구입 금액을 입력받는다. +- 지난 주 당첨 번호를 쉼표(,)를 기준으로 구분하여 입력받는다. + +### OutputView + +- 구매 결과 헤더를 출력한다. +- 발급된 로또 번호를 출력한다. +- 당첨 통계를 출력한다. +- 총 수익률을 출력한다. + +### LottoController + +- 로또 게임의 전체 진행을 담당한다. +- 구입 금액 입력, 로또 구매, 당첨 번호 입력, 당첨 통계 계산, 결과 출력을 순서대로 연결한다. + +## 테스트 + +### LottoTest + +- 로또 번호의 개수가 6개가 아니면 예외가 발생하는지 테스트한다. +- 로또 번호가 중복되면 예외가 발생하는지 테스트한다. + +### LottosTest + +- 로또 목록을 출력용 번호 리스트로 변환하는지 테스트한다. + +### LottoShopTest + +- 구입 금액만큼 로또를 구매하는지 테스트한다. + +### LottoNumberTest + +- 로또 번호가 1보다 작으면 예외가 발생하는지 테스트한다. +- 로또 번호가 45보다 크면 예외가 발생하는지 테스트한다. + +### PurchaseAmountTest + +- 구입 금액이 1000원 미만이면 예외가 발생하는지 테스트한다. +- 구입 금액이 1000원 단위가 아니면 예외가 발생하는지 테스트한다. +- 구입 금액으로 구매 가능한 로또 개수를 계산하는지 테스트한다. + +### RankTest + +- 일치 개수에 따라 올바른 등수를 반환하는지 테스트한다. + +### WinningStatisticsTest + +- 등수를 추가하면 당첨 개수가 증가하는지 테스트한다. +- 총 당첨금을 계산하는지 테스트한다. +- 수익률을 계산하는지 테스트한다. + +## 설계 의도 + +로또 번호와 구입 금액을 각각 `LottoNumber`, `PurchaseAmount` 로 포장했습니다. +이를 통해 로또 번호 범위 검증과 구입 금액 검증 책임을 객체가 관리하도록 했습니다. + +또한 `List`를 `Lottos`로 감싸는 방식으로 로또 목록에 대한 책임을 분리했습니다. + +당첨 결과는 `Rank`와 `WinningStatistics`를 통해 계산하도록 했습니다. +`Rank`는 일치 개수에 따른 등수와 당첨 금액을 관리하고, `WinningStatistics`는 등수별 개수와 총 당첨금, 수익률 계산을 담당하도록 구성했습니다. + +입력과 출력은 `InputView`, `OutputView`로 분리했습니다. +또한 출력에 필요한 데이터는 `WinningResult`로 별도 전달하여 뷰가 도메인 객체를 직접 해석하지 않도록 구성했습니다. diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 91e974be7..5785f2edd 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -15,6 +15,7 @@ public class LottoController { public void run() { PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); Lottos lottos = lottoShop.purchase(purchaseAmount); + outputView.printResultHeader(lottos.size()); outputView.printLottos(lottos.toNumberLists()); @@ -22,6 +23,7 @@ public void run() { Lotto winningLotto = new Lotto(toLottoNumbers(winningNumbers)); WinningStatistics winningStatistics = lottos.createWinningStatistics(winningLotto); + outputView.printWinningStatistics(createWinningResults(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } From 135ce7b4f0a4f4af426c228f288c3fcaa231119a Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 17:58:00 +0900 Subject: [PATCH 18/51] =?UTF-8?q?refactor:=20generator=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 1 + src/main/java/domain/LottoShop.java | 2 ++ src/main/java/{domain => generator}/NumberGenerator.java | 4 +++- .../java/{domain => generator}/RandomNumberGenerator.java | 4 +++- src/test/java/domain/LottoShopTest.java | 2 ++ src/test/java/{domain => generator}/TestNumberGenerator.java | 4 +++- 6 files changed, 14 insertions(+), 3 deletions(-) rename src/main/java/{domain => generator}/NumberGenerator.java (67%) rename src/main/java/{domain => generator}/RandomNumberGenerator.java (94%) rename src/test/java/{domain => generator}/TestNumberGenerator.java (87%) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 5785f2edd..a121282c3 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -2,6 +2,7 @@ import domain.*; import dto.WinningResult; +import generator.RandomNumberGenerator; import view.InputView; import view.OutputView; diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java index 84f586d45..5c24e299e 100644 --- a/src/main/java/domain/LottoShop.java +++ b/src/main/java/domain/LottoShop.java @@ -1,5 +1,7 @@ package domain; +import generator.NumberGenerator; + import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/domain/NumberGenerator.java b/src/main/java/generator/NumberGenerator.java similarity index 67% rename from src/main/java/domain/NumberGenerator.java rename to src/main/java/generator/NumberGenerator.java index 9a28ba8e4..a45772a25 100644 --- a/src/main/java/domain/NumberGenerator.java +++ b/src/main/java/generator/NumberGenerator.java @@ -1,4 +1,6 @@ -package domain; +package generator; + +import domain.LottoNumber; import java.util.List; diff --git a/src/main/java/domain/RandomNumberGenerator.java b/src/main/java/generator/RandomNumberGenerator.java similarity index 94% rename from src/main/java/domain/RandomNumberGenerator.java rename to src/main/java/generator/RandomNumberGenerator.java index f394f3fcc..ed111a29a 100644 --- a/src/main/java/domain/RandomNumberGenerator.java +++ b/src/main/java/generator/RandomNumberGenerator.java @@ -1,4 +1,6 @@ -package domain; +package generator; + +import domain.LottoNumber; import java.util.ArrayList; import java.util.Collections; diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java index 0f3ea4c0e..1e6132e6b 100644 --- a/src/test/java/domain/LottoShopTest.java +++ b/src/test/java/domain/LottoShopTest.java @@ -1,5 +1,7 @@ package domain; +import generator.NumberGenerator; +import generator.TestNumberGenerator; import org.junit.jupiter.api.Test; import java.util.List; diff --git a/src/test/java/domain/TestNumberGenerator.java b/src/test/java/generator/TestNumberGenerator.java similarity index 87% rename from src/test/java/domain/TestNumberGenerator.java rename to src/test/java/generator/TestNumberGenerator.java index 4998d9455..4746f653a 100644 --- a/src/test/java/domain/TestNumberGenerator.java +++ b/src/test/java/generator/TestNumberGenerator.java @@ -1,4 +1,6 @@ -package domain; +package generator; + +import domain.LottoNumber; import java.util.List; From 743f4ce46b6c832025c110e45882456fd4eb17fb Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 30 Mar 2026 19:17:06 +0900 Subject: [PATCH 19/51] =?UTF-8?q?fix:=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/README.md b/src/README.md index 7d13d7032..84b3d4a2c 100644 --- a/src/README.md +++ b/src/README.md @@ -113,10 +113,6 @@ - 구입 금액이 1000원 단위가 아니면 예외가 발생하는지 테스트한다. - 구입 금액으로 구매 가능한 로또 개수를 계산하는지 테스트한다. -### RankTest - -- 일치 개수에 따라 올바른 등수를 반환하는지 테스트한다. - ### WinningStatisticsTest - 등수를 추가하면 당첨 개수가 증가하는지 테스트한다. From 7bbb4df3860c0f43947316fb41aad77bd3ca7079 Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 14:39:03 +0900 Subject: [PATCH 20/51] =?UTF-8?q?refactor:=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=B1=85=EC=9E=84=EC=9D=84=20Application?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/Application.java | 10 +++++++++- src/main/java/controller/LottoController.java | 13 +++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/Application.java b/src/main/java/Application.java index 1f2fe1baf..78579ad09 100644 --- a/src/main/java/Application.java +++ b/src/main/java/Application.java @@ -1,8 +1,16 @@ import controller.LottoController; +import domain.LottoShop; +import generator.RandomNumberGenerator; +import view.InputView; +import view.OutputView; public class Application { public static void main(String[] args) { - LottoController lottoController = new LottoController(); + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + LottoShop lottoShop = new LottoShop(new RandomNumberGenerator()); + + LottoController lottoController = new LottoController(inputView, outputView, lottoShop); lottoController.run(); } } diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index a121282c3..8671f2ff1 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -2,16 +2,21 @@ import domain.*; import dto.WinningResult; -import generator.RandomNumberGenerator; import view.InputView; import view.OutputView; import java.util.List; public class LottoController { - private final InputView inputView = new InputView(); - private final OutputView outputView = new OutputView(); - private final LottoShop lottoShop = new LottoShop(new RandomNumberGenerator()); + private final InputView inputView; + private final OutputView outputView; + private final LottoShop lottoShop; + + public LottoController(InputView inputView, OutputView outputView, LottoShop lottoShop) { + this.inputView = inputView; + this.outputView = outputView; + this.lottoShop = lottoShop; + } public void run() { PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); From 2f4bb82a823b739ef06137157efe8a8220ac9b60 Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 15:35:46 +0900 Subject: [PATCH 21/51] =?UTF-8?q?refactor:=20=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=B0=A9=EC=96=B4=EC=A0=81=20=EB=B3=B5=EC=82=AC=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 15169d1f7..68e37f47d 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -1,6 +1,6 @@ package domain; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; public class Lotto { @@ -9,11 +9,11 @@ public class Lotto { public Lotto(List numbers) { validate(numbers); - this.numbers = Collections.unmodifiableList(numbers); + this.numbers = new ArrayList<>(numbers); } public List getNumbers() { - return numbers; + return List.copyOf(numbers); } public int countMatch(Lotto winningLotto) { From dc5f94949a37b4ebe9c0f52afe2d71d36e20e18f Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 15:59:07 +0900 Subject: [PATCH 22/51] =?UTF-8?q?refactor:=20Rank=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EA=B0=80=20enum=20=ED=95=84=EB=93=9C=EB=A5=BC=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Rank.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index 6e0cf8ff5..27a2ca99a 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,5 +1,7 @@ package domain; +import java.util.Arrays; + public enum Rank { THREE_MATCH(3, 5000), FOUR_MATCH(4, 50000), @@ -16,19 +18,10 @@ public enum Rank { } public static Rank from(int matchCount) { - if (matchCount == 6) { - return SIX_MATCH; - } - if (matchCount == 5) { - return FIVE_MATCH; - } - if (matchCount == 4) { - return FOUR_MATCH; - } - if (matchCount == 3) { - return THREE_MATCH; - } - return MISS; + return Arrays.stream(values()) + .filter(rank -> rank.matchCount == matchCount) + .findFirst() + .orElse(MISS); } public int getPrizeMoney() { From cc52c55c456910da83900061c28e289efbf15387 Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 16:01:47 +0900 Subject: [PATCH 23/51] =?UTF-8?q?refactor:=20LOTTE=5FSIZE=EB=A5=BC=20stati?= =?UTF-8?q?c=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 68e37f47d..f1f04fb7d 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -5,7 +5,7 @@ public class Lotto { private final List numbers; - private final int LOTTO_SIZE = 6; + private static final int LOTTO_SIZE = 6; public Lotto(List numbers) { validate(numbers); From 43547a30f90c561d789fb9e18c5186b04df4033d Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 16:12:55 +0900 Subject: [PATCH 24/51] =?UTF-8?q?test:=20LottoNumber=20=EA=B2=BD=EA=B3=84?= =?UTF-8?q?=EA=B0=92=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoNumberTest.java | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/test/java/domain/LottoNumberTest.java b/src/test/java/domain/LottoNumberTest.java index 0ff61b63d..c97486321 100644 --- a/src/test/java/domain/LottoNumberTest.java +++ b/src/test/java/domain/LottoNumberTest.java @@ -1,22 +1,16 @@ package domain; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThatThrownBy; class LottoNumberTest { - @Test - void 로또_번호가_1보다_작으면_예외가_발생한다() { - assertThatThrownBy(() -> new LottoNumber(0)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("로또 번호는 1부터 45 사이여야 합니다."); - } - - @Test - void 로또_번호가_45보다_크면_예외가_발생한다() { - assertThatThrownBy(() -> new LottoNumber(46)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("로또 번호는 1부터 45 사이여야 합니다."); + @ParameterizedTest + @ValueSource(ints = {0, 46}) + void 로또_번호가_범위를_벗어나면_예외가_발생한다(int number) { + assertThatThrownBy(() -> new LottoNumber(number)) + .isInstanceOf(IllegalArgumentException.class); } } From 166ff458e56a55f347470887a11de49c29890f95 Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 16:34:08 +0900 Subject: [PATCH 25/51] =?UTF-8?q?refactor:=20generator=EB=A5=BC=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=ED=95=A0=EB=95=8C=20LottoNumber=EB=A5=BC=20?= =?UTF-8?q?=EC=9E=AC=EC=82=AC=EC=9A=A9=20=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/generator/RandomNumberGenerator.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/generator/RandomNumberGenerator.java b/src/main/java/generator/RandomNumberGenerator.java index ed111a29a..832d1ce62 100644 --- a/src/main/java/generator/RandomNumberGenerator.java +++ b/src/main/java/generator/RandomNumberGenerator.java @@ -6,20 +6,17 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.stream.IntStream; public class RandomNumberGenerator implements NumberGenerator { private static final int LOTTO_START_NUMBER = 1; private static final int LOTTO_END_NUMBER = 45; private static final int LOTTO_SIZE = 6; + private static final List LOTTO_NUMBERS = createLottoNumbers(); @Override public List generate() { - List lottoNumbers = new ArrayList<>(); - - for (int i = LOTTO_START_NUMBER; i <= LOTTO_END_NUMBER; i++) { - lottoNumbers.add(new LottoNumber(i)); - } - + List lottoNumbers = new ArrayList<>(LOTTO_NUMBERS); Collections.shuffle(lottoNumbers); return lottoNumbers.subList(0, LOTTO_SIZE) @@ -27,4 +24,10 @@ public List generate() { .sorted(Comparator.comparingInt(LottoNumber::number)) .toList(); } + + private static List createLottoNumbers() { + return IntStream.rangeClosed(LOTTO_START_NUMBER, LOTTO_END_NUMBER) + .mapToObj(LottoNumber::new) + .toList(); + } } From 4cc8f1e64c41e7d6850723b2096630bb20951646 Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 16:51:56 +0900 Subject: [PATCH 26/51] =?UTF-8?q?refactor:=20=EB=A1=9C=EB=98=90=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=A0=95=EB=A0=AC=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20OutputView=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/generator/RandomNumberGenerator.java | 7 ++----- src/main/java/view/OutputView.java | 8 +++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/generator/RandomNumberGenerator.java b/src/main/java/generator/RandomNumberGenerator.java index 832d1ce62..65f981f2b 100644 --- a/src/main/java/generator/RandomNumberGenerator.java +++ b/src/main/java/generator/RandomNumberGenerator.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.stream.IntStream; @@ -17,12 +16,10 @@ public class RandomNumberGenerator implements NumberGenerator { @Override public List generate() { List lottoNumbers = new ArrayList<>(LOTTO_NUMBERS); + Collections.shuffle(lottoNumbers); - return lottoNumbers.subList(0, LOTTO_SIZE) - .stream() - .sorted(Comparator.comparingInt(LottoNumber::number)) - .toList(); + return new ArrayList<>(lottoNumbers.subList(0, LOTTO_SIZE)); } private static List createLottoNumbers() { diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index ee487d0f4..f57d71ee0 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -12,7 +12,7 @@ public void printResultHeader(int count) { public void printLottos(List> lottoNumbers) { for (List numbers : lottoNumbers) { - System.out.println(numbers); + System.out.println(sortLotto(numbers)); } } @@ -21,6 +21,12 @@ public void printWinningStatistics(List winningResults) { printRankCount(winningResults); } + private List sortLotto(List lotto) { + return lotto.stream() + .sorted() + .toList(); + } + private void printStatisticsHeader() { System.out.println(); System.out.println("당첨 통계"); From f4cdd4789f9c31e5f59eaf6cbbca74fccd292fce Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 18:24:11 +0900 Subject: [PATCH 27/51] =?UTF-8?q?refactor:=20WinningStatistics=EB=A1=9C=20?= =?UTF-8?q?=EB=8B=B9=EC=B2=A8=20=ED=86=B5=EA=B3=84=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=20=EC=9D=B4=EB=8F=99=20test:=20WinningStatis?= =?UTF-8?q?tics=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 2 +- src/main/java/domain/Lottos.java | 18 ++------ src/main/java/domain/WinningStatistics.java | 12 ++++- .../java/domain/WinningStatisticsTest.java | 46 +++++++++++++------ 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 8671f2ff1..3f574d308 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -28,7 +28,7 @@ public void run() { List winningNumbers = inputView.readWinningNumbers(); Lotto winningLotto = new Lotto(toLottoNumbers(winningNumbers)); - WinningStatistics winningStatistics = lottos.createWinningStatistics(winningLotto); + WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); outputView.printWinningStatistics(createWinningResults(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 1c0318a2f..8a8d0a7d9 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; public class Lottos { private final List lottos; @@ -10,6 +11,10 @@ public Lottos(List lottos) { this.lottos = new ArrayList<>(lottos); } + public Stream stream() { + return lottos.stream(); + } + public int size() { return lottos.size(); } @@ -20,22 +25,9 @@ public List> toNumberLists() { .toList(); } - public WinningStatistics createWinningStatistics(Lotto winningLotto) { - WinningStatistics winningStatistics = new WinningStatistics(); - for (Lotto lotto : lottos) { - winningStatistics.add(findRank(lotto, winningLotto)); - } - return winningStatistics; - } - private List toNumbers(Lotto lotto) { return lotto.getNumbers().stream() .map(LottoNumber::number) .toList(); } - - - private Rank findRank(Lotto lotto, Lotto winningLotto) { - return Rank.from(lotto.countMatch(winningLotto)); - } } diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 578968121..3f5ef13a7 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -11,13 +11,23 @@ public WinningStatistics() { initialize(); } + public static WinningStatistics from(Lottos lottos, Lotto winningLotto) { + WinningStatistics statistics = new WinningStatistics(); + + lottos.stream() + .map(lotto -> Rank.from(lotto.countMatch(winningLotto))) + .forEach(statistics::add); + + return statistics; + } + private void initialize() { for (Rank rank : Rank.values()) { statistics.put(rank, 0); } } - public void add(Rank rank) { + private void add(Rank rank) { int rankCount = statistics.get(rank); rankCount++; statistics.put(rank, rankCount); diff --git a/src/test/java/domain/WinningStatisticsTest.java b/src/test/java/domain/WinningStatisticsTest.java index 8793e5a5a..ca6c0a4cc 100644 --- a/src/test/java/domain/WinningStatisticsTest.java +++ b/src/test/java/domain/WinningStatisticsTest.java @@ -2,41 +2,61 @@ import org.junit.jupiter.api.Test; +import java.util.Arrays; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; class WinningStatisticsTest { + private static final Lotto WINNING_LOTTO = createLotto(1, 2, 3, 4, 5, 6); + private static final Lotto THREE_MATCH_LOTTO = createLotto(1, 2, 3, 7, 8, 9); + private static final Lotto FOUR_MATCH_LOTTO = createLotto(1, 2, 3, 4, 10, 11); + private static final Lotto FIVE_MATCH_LOTTO = createLotto(1, 2, 3, 4, 5, 12); @Test - void 등수를_추가하면_개수가_증가한다() { - WinningStatistics winningStatistics = new WinningStatistics(); + void 당첨_결과별_개수를_집계한다() { + Lottos lottos = new Lottos(List.of( + THREE_MATCH_LOTTO, + FOUR_MATCH_LOTTO, + FIVE_MATCH_LOTTO + )); - winningStatistics.add(Rank.THREE_MATCH); - winningStatistics.add(Rank.THREE_MATCH); - winningStatistics.add(Rank.FOUR_MATCH); + WinningStatistics winningStatistics = WinningStatistics.from(lottos, WINNING_LOTTO); - assertThat(winningStatistics.countOf(Rank.THREE_MATCH)).isEqualTo(2); + assertThat(winningStatistics.countOf(Rank.THREE_MATCH)).isEqualTo(1); assertThat(winningStatistics.countOf(Rank.FOUR_MATCH)).isEqualTo(1); + assertThat(winningStatistics.countOf(Rank.FIVE_MATCH)).isEqualTo(1); } @Test void 총_당첨금을_계산한다() { - WinningStatistics winningStatistics = new WinningStatistics(); + Lottos lottos = new Lottos(List.of( + THREE_MATCH_LOTTO, + THREE_MATCH_LOTTO, + FOUR_MATCH_LOTTO + )); - winningStatistics.add(Rank.THREE_MATCH); - winningStatistics.add(Rank.THREE_MATCH); - winningStatistics.add(Rank.FOUR_MATCH); + WinningStatistics winningStatistics = WinningStatistics.from(lottos, WINNING_LOTTO); assertThat(winningStatistics.calculateTotalPrize()).isEqualTo(60000); } @Test void 수익률을_계산한다() { - WinningStatistics winningStatistics = new WinningStatistics(); + Lottos lottos = new Lottos(List.of( + THREE_MATCH_LOTTO, + FOUR_MATCH_LOTTO + )); PurchaseAmount purchaseAmount = new PurchaseAmount(10000); - winningStatistics.add(Rank.THREE_MATCH); - winningStatistics.add(Rank.FOUR_MATCH); + WinningStatistics winningStatistics = WinningStatistics.from(lottos, WINNING_LOTTO); assertThat(winningStatistics.calculateProfitRate(purchaseAmount)).isEqualTo(5.5); } + + private static Lotto createLotto(int... numbers) { + return new Lotto(Arrays.stream(numbers) + .mapToObj(LottoNumber::new) + .toList()); + } } From a96522272d054569029a4c0711f99cfb1debf96a Mon Sep 17 00:00:00 2001 From: dahyn Date: Tue, 31 Mar 2026 23:14:55 +0900 Subject: [PATCH 28/51] =?UTF-8?q?refactor:=20WinningResult=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=B1=85=EC=9E=84=EC=9D=84=20WinningStatistics?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 16 +--------------- src/main/java/domain/Rank.java | 4 ++++ src/main/java/domain/WinningStatistics.java | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 3f574d308..a3271aa7e 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,7 +1,6 @@ package controller; import domain.*; -import dto.WinningResult; import view.InputView; import view.OutputView; @@ -30,7 +29,7 @@ public void run() { WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); - outputView.printWinningStatistics(createWinningResults(winningStatistics)); + outputView.printWinningStatistics(winningStatistics.winningResults()); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } @@ -39,17 +38,4 @@ private List toLottoNumbers(List numbers) { .map(LottoNumber::new) .toList(); } - - private List createWinningResults(WinningStatistics winningStatistics) { - return List.of( - createWinningResult(winningStatistics, Rank.THREE_MATCH), - createWinningResult(winningStatistics, Rank.FOUR_MATCH), - createWinningResult(winningStatistics, Rank.FIVE_MATCH), - createWinningResult(winningStatistics, Rank.SIX_MATCH) - ); - } - - private WinningResult createWinningResult(WinningStatistics winningStatistics, Rank rank) { - return new WinningResult(rank.getMatchCount(), rank.getPrizeMoney(), winningStatistics.countOf(rank)); - } } diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index 27a2ca99a..44f2d0cd1 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -24,6 +24,10 @@ public static Rank from(int matchCount) { .orElse(MISS); } + public boolean isWinning() { + return this != MISS; + } + public int getPrizeMoney() { return prizeMoney; } diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 3f5ef13a7..37f1bc54c 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -1,6 +1,10 @@ package domain; +import dto.WinningResult; + +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; public class WinningStatistics { @@ -33,6 +37,21 @@ private void add(Rank rank) { statistics.put(rank, rankCount); } + public List winningResults() { + return Arrays.stream(Rank.values()) + .filter(Rank::isWinning) + .map(this::toWinningResult) + .toList(); + } + + private WinningResult toWinningResult(Rank rank) { + return new WinningResult( + rank.getMatchCount(), + rank.getPrizeMoney(), + statistics.get(rank) + ); + } + public int countOf(Rank rank) { return statistics.get(rank); } From 1136f01c679e1d7c44d30d9aff1befc05c87c115 Mon Sep 17 00:00:00 2001 From: dahyn Date: Fri, 3 Apr 2026 00:33:40 +0900 Subject: [PATCH 29/51] =?UTF-8?q?refactor:=20Lottos=20=EA=B0=92=EC=9D=84?= =?UTF-8?q?=20List=EB=A1=9C=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lottos.java | 9 ++++----- src/main/java/domain/WinningStatistics.java | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 8a8d0a7d9..3412e3926 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; public class Lottos { private final List lottos; @@ -11,10 +10,6 @@ public Lottos(List lottos) { this.lottos = new ArrayList<>(lottos); } - public Stream stream() { - return lottos.stream(); - } - public int size() { return lottos.size(); } @@ -25,6 +20,10 @@ public List> toNumberLists() { .toList(); } + public List lottoToList() { + return new ArrayList<>(lottos); + } + private List toNumbers(Lotto lotto) { return lotto.getNumbers().stream() .map(LottoNumber::number) diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 37f1bc54c..69e593a08 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -18,7 +18,7 @@ public WinningStatistics() { public static WinningStatistics from(Lottos lottos, Lotto winningLotto) { WinningStatistics statistics = new WinningStatistics(); - lottos.stream() + lottos.lottoToList().stream() .map(lotto -> Rank.from(lotto.countMatch(winningLotto))) .forEach(statistics::add); From 6585e9202adffa549e408f889d0bee56cbca874f Mon Sep 17 00:00:00 2001 From: dahyn Date: Fri, 3 Apr 2026 00:59:44 +0900 Subject: [PATCH 30/51] =?UTF-8?q?refactor:=20getter=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index f1f04fb7d..c143b43bd 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -12,16 +12,16 @@ public Lotto(List numbers) { this.numbers = new ArrayList<>(numbers); } - public List getNumbers() { - return List.copyOf(numbers); - } - public int countMatch(Lotto winningLotto) { return (int) numbers.stream() .filter(winningLotto::contains) .count(); } + public List getNumbers() { + return List.copyOf(numbers); + } + private boolean contains(LottoNumber lottoNumber) { return numbers.contains(lottoNumber); } From 93c931b06cfccfff6d6d397f278dd59754a985ce Mon Sep 17 00:00:00 2001 From: dahyn Date: Fri, 3 Apr 2026 01:03:12 +0900 Subject: [PATCH 31/51] =?UTF-8?q?fix:=20=EC=9E=85=EB=A0=A5=20=EC=8B=9C=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=20=EC=A0=95=EB=A0=AC=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 --- src/main/java/view/InputView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 14993762e..f0e6eb60d 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -22,7 +22,6 @@ private List parseWinningNumbers(String input) { return Arrays.stream(input.split(",")) .map(String::trim) .map(Integer::parseInt) - .sorted() .toList(); } } From a98be00e072adf3440511d56aab91ed28403752f Mon Sep 17 00:00:00 2001 From: dahyn Date: Fri, 3 Apr 2026 01:14:46 +0900 Subject: [PATCH 32/51] =?UTF-8?q?test:=20LottoShopTest=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoShopTest.java | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java index 1e6132e6b..d3ebedb1a 100644 --- a/src/test/java/domain/LottoShopTest.java +++ b/src/test/java/domain/LottoShopTest.java @@ -11,14 +11,25 @@ class LottoShopTest { @Test - void 구입금액만큼_로또를_구매한다() { + void 구입금액만큼_로또를_생성한다() { NumberGenerator numberGenerator = new TestNumberGenerator(List.of( - new LottoNumber(1), - new LottoNumber(2), - new LottoNumber(3), - new LottoNumber(4), - new LottoNumber(5), - new LottoNumber(6) + new LottoNumber(1), new LottoNumber(2), + new LottoNumber(3), new LottoNumber(4), + new LottoNumber(5), new LottoNumber(6) + )); + LottoShop lottoShop = new LottoShop(numberGenerator); + + Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000)); + + assertThat(lottos.toNumberLists()).hasSize(3); + } + + @Test + void 생성된_번호로_로또가_구성된다() { + NumberGenerator numberGenerator = new TestNumberGenerator(List.of( + new LottoNumber(1), new LottoNumber(2), + new LottoNumber(3), new LottoNumber(4), + new LottoNumber(5), new LottoNumber(6) )); LottoShop lottoShop = new LottoShop(numberGenerator); From ec60bb64cb39570ad03c6cd9c79d252ef33f9a1a Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 5 Apr 2026 17:01:08 +0900 Subject: [PATCH 33/51] =?UTF-8?q?refactor:=20controller=EC=97=90=EC=84=9C?= =?UTF-8?q?=20DTO=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 16 +++++++++++++++- src/main/java/domain/WinningStatistics.java | 19 ------------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index a3271aa7e..3f574d308 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,6 +1,7 @@ package controller; import domain.*; +import dto.WinningResult; import view.InputView; import view.OutputView; @@ -29,7 +30,7 @@ public void run() { WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); - outputView.printWinningStatistics(winningStatistics.winningResults()); + outputView.printWinningStatistics(createWinningResults(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } @@ -38,4 +39,17 @@ private List toLottoNumbers(List numbers) { .map(LottoNumber::new) .toList(); } + + private List createWinningResults(WinningStatistics winningStatistics) { + return List.of( + createWinningResult(winningStatistics, Rank.THREE_MATCH), + createWinningResult(winningStatistics, Rank.FOUR_MATCH), + createWinningResult(winningStatistics, Rank.FIVE_MATCH), + createWinningResult(winningStatistics, Rank.SIX_MATCH) + ); + } + + private WinningResult createWinningResult(WinningStatistics winningStatistics, Rank rank) { + return new WinningResult(rank.getMatchCount(), rank.getPrizeMoney(), winningStatistics.countOf(rank)); + } } diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 69e593a08..74b05c6a6 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -1,10 +1,6 @@ package domain; -import dto.WinningResult; - -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; public class WinningStatistics { @@ -37,21 +33,6 @@ private void add(Rank rank) { statistics.put(rank, rankCount); } - public List winningResults() { - return Arrays.stream(Rank.values()) - .filter(Rank::isWinning) - .map(this::toWinningResult) - .toList(); - } - - private WinningResult toWinningResult(Rank rank) { - return new WinningResult( - rank.getMatchCount(), - rank.getPrizeMoney(), - statistics.get(rank) - ); - } - public int countOf(Rank rank) { return statistics.get(rank); } From 2257a3086be3daf71e54544abafed3aa31e54cdb Mon Sep 17 00:00:00 2001 From: dahyn Date: Sun, 5 Apr 2026 18:32:45 +0900 Subject: [PATCH 34/51] =?UTF-8?q?feat:=20BonusBall=20=EB=8F=84=EC=9E=85=20?= =?UTF-8?q?=EB=B0=8F=202=EB=93=B1=20=EB=8B=B9=EC=B2=A8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 17 +++++++------ src/main/java/domain/BonusBall.java | 13 ++++++++++ src/main/java/domain/Lotto.java | 2 +- src/main/java/domain/Rank.java | 25 +++++++++++++------ src/main/java/domain/WinningLotto.java | 25 +++++++++++++++++++ src/main/java/domain/WinningStatistics.java | 4 +-- src/main/java/dto/WinningResult.java | 10 +++++++- src/main/java/view/InputView.java | 5 ++++ src/main/java/view/OutputView.java | 2 +- .../java/domain/WinningStatisticsTest.java | 7 ++++-- 10 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 src/main/java/domain/BonusBall.java create mode 100644 src/main/java/domain/WinningLotto.java diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 3f574d308..a80b08011 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -5,6 +5,7 @@ import view.InputView; import view.OutputView; +import java.util.Arrays; import java.util.List; public class LottoController { @@ -26,7 +27,8 @@ public void run() { outputView.printLottos(lottos.toNumberLists()); List winningNumbers = inputView.readWinningNumbers(); - Lotto winningLotto = new Lotto(toLottoNumbers(winningNumbers)); + BonusBall bonusBall = new BonusBall(new LottoNumber(inputView.readBonusBall())); + WinningLotto winningLotto = new WinningLotto(new Lotto(toLottoNumbers(winningNumbers)), bonusBall); WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); @@ -41,15 +43,14 @@ private List toLottoNumbers(List numbers) { } private List createWinningResults(WinningStatistics winningStatistics) { - return List.of( - createWinningResult(winningStatistics, Rank.THREE_MATCH), - createWinningResult(winningStatistics, Rank.FOUR_MATCH), - createWinningResult(winningStatistics, Rank.FIVE_MATCH), - createWinningResult(winningStatistics, Rank.SIX_MATCH) - ); + return Arrays.stream(Rank.values()) + .filter(Rank::isWinning) + .map(rank -> createWinningResult(winningStatistics, rank)) + .toList(); } + private WinningResult createWinningResult(WinningStatistics winningStatistics, Rank rank) { - return new WinningResult(rank.getMatchCount(), rank.getPrizeMoney(), winningStatistics.countOf(rank)); + return WinningResult.from(rank, winningStatistics.countOf(rank)); } } diff --git a/src/main/java/domain/BonusBall.java b/src/main/java/domain/BonusBall.java new file mode 100644 index 000000000..5f7e78b78 --- /dev/null +++ b/src/main/java/domain/BonusBall.java @@ -0,0 +1,13 @@ +package domain; + +public record BonusBall(LottoNumber number) { + public BonusBall { + validate(number); + } + + private void validate(LottoNumber number) { + if (number == null) { + throw new IllegalArgumentException("보너스 볼은 필수입니다."); + } + } +} diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index c143b43bd..72888a43d 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -22,7 +22,7 @@ public List getNumbers() { return List.copyOf(numbers); } - private boolean contains(LottoNumber lottoNumber) { + public boolean contains(LottoNumber lottoNumber) { return numbers.contains(lottoNumber); } diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index 44f2d0cd1..002da03bc 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,11 +1,10 @@ package domain; -import java.util.Arrays; - public enum Rank { THREE_MATCH(3, 5000), FOUR_MATCH(4, 50000), FIVE_MATCH(5, 1500000), + FIVE_BONUS_MATCH(5, 30000000), SIX_MATCH(6, 2000000000), MISS(0, 0); @@ -17,11 +16,23 @@ public enum Rank { this.prizeMoney = prizeMoney; } - public static Rank from(int matchCount) { - return Arrays.stream(values()) - .filter(rank -> rank.matchCount == matchCount) - .findFirst() - .orElse(MISS); + public static Rank from(int matchCount, boolean bonusBallMatched) { + if (matchCount == 6) { + return SIX_MATCH; + } + if (matchCount == 5 && bonusBallMatched) { + return FIVE_BONUS_MATCH; + } + if (matchCount == 5) { + return FIVE_MATCH; + } + if (matchCount == 4) { + return FOUR_MATCH; + } + if (matchCount == 3) { + return THREE_MATCH; + } + return MISS; } public boolean isWinning() { diff --git a/src/main/java/domain/WinningLotto.java b/src/main/java/domain/WinningLotto.java new file mode 100644 index 000000000..c5a3ff958 --- /dev/null +++ b/src/main/java/domain/WinningLotto.java @@ -0,0 +1,25 @@ +package domain; + +public class WinningLotto { + + private final Lotto winningLotto; + private final BonusBall bonusBall; + + public WinningLotto(Lotto winningLotto, BonusBall bonusBall) { + validateWinningLotto(winningLotto, bonusBall); + this.winningLotto = winningLotto; + this.bonusBall = bonusBall; + } + + public Rank match(Lotto lotto) { + int matchCount = lotto.countMatch(this.winningLotto); + boolean bonusBallMatch = lotto.contains(bonusBall.number()); + return Rank.from(matchCount, bonusBallMatch); + } + + private void validateWinningLotto(Lotto winningLotto, BonusBall bonusBall) { + if (winningLotto.contains(bonusBall.number())) { + throw new IllegalArgumentException("보너스 볼은 당첨 번호와 중복될 수 없습니다."); + } + } +} diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 74b05c6a6..60440faa3 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -11,11 +11,11 @@ public WinningStatistics() { initialize(); } - public static WinningStatistics from(Lottos lottos, Lotto winningLotto) { + public static WinningStatistics from(Lottos lottos, WinningLotto winningLotto) { WinningStatistics statistics = new WinningStatistics(); lottos.lottoToList().stream() - .map(lotto -> Rank.from(lotto.countMatch(winningLotto))) + .map(winningLotto::match) .forEach(statistics::add); return statistics; diff --git a/src/main/java/dto/WinningResult.java b/src/main/java/dto/WinningResult.java index f6ace10a3..6540fd488 100644 --- a/src/main/java/dto/WinningResult.java +++ b/src/main/java/dto/WinningResult.java @@ -1,4 +1,12 @@ package dto; -public record WinningResult(int matchCount, int prizeMoney, int count) { +import domain.Rank; + +public record WinningResult(String message, int count) { + public static WinningResult from(Rank rank, int count) { + if (rank == Rank.FIVE_BONUS_MATCH) { + return new WinningResult("5개 일치, 보너스 볼 일치(" + rank.getPrizeMoney() + "원)", count); + } + return new WinningResult(rank.getMatchCount() + "개 일치 (" + rank.getPrizeMoney() + "원)", count); + } } diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index f0e6eb60d..32c37e84b 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -18,6 +18,11 @@ public List readWinningNumbers() { return parseWinningNumbers(input); } + public int readBonusBall() { + System.out.println("보너스 볼을 입력해 주세요."); + return Integer.parseInt(scanner.nextLine()); + } + private List parseWinningNumbers(String input) { return Arrays.stream(input.split(",")) .map(String::trim) diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index f57d71ee0..c95fd447a 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -35,7 +35,7 @@ private void printStatisticsHeader() { private void printRankCount(List winningResults) { for (WinningResult winningResult : winningResults) { - System.out.println(winningResult.matchCount() + "개 일치" + " (" + winningResult.prizeMoney() + "원)-" + winningResult.count() + "개"); + System.out.println(winningResult.message() + " - " + winningResult.count() + "개"); } } diff --git a/src/test/java/domain/WinningStatisticsTest.java b/src/test/java/domain/WinningStatisticsTest.java index ca6c0a4cc..78987d4cc 100644 --- a/src/test/java/domain/WinningStatisticsTest.java +++ b/src/test/java/domain/WinningStatisticsTest.java @@ -8,17 +8,19 @@ import static org.assertj.core.api.Assertions.assertThat; class WinningStatisticsTest { - private static final Lotto WINNING_LOTTO = createLotto(1, 2, 3, 4, 5, 6); + private static final WinningLotto WINNING_LOTTO = new WinningLotto(createLotto(1, 2, 3, 4, 5, 6), new BonusBall(new LottoNumber(7))); private static final Lotto THREE_MATCH_LOTTO = createLotto(1, 2, 3, 7, 8, 9); private static final Lotto FOUR_MATCH_LOTTO = createLotto(1, 2, 3, 4, 10, 11); private static final Lotto FIVE_MATCH_LOTTO = createLotto(1, 2, 3, 4, 5, 12); + private static final Lotto FIVE_BONUS_MATCH_LOTTO = createLotto(1, 2, 3, 4, 5, 7); @Test void 당첨_결과별_개수를_집계한다() { Lottos lottos = new Lottos(List.of( THREE_MATCH_LOTTO, FOUR_MATCH_LOTTO, - FIVE_MATCH_LOTTO + FIVE_MATCH_LOTTO, + FIVE_BONUS_MATCH_LOTTO )); WinningStatistics winningStatistics = WinningStatistics.from(lottos, WINNING_LOTTO); @@ -26,6 +28,7 @@ class WinningStatisticsTest { assertThat(winningStatistics.countOf(Rank.THREE_MATCH)).isEqualTo(1); assertThat(winningStatistics.countOf(Rank.FOUR_MATCH)).isEqualTo(1); assertThat(winningStatistics.countOf(Rank.FIVE_MATCH)).isEqualTo(1); + assertThat(winningStatistics.countOf(Rank.FIVE_BONUS_MATCH)).isEqualTo(1); } @Test From 50f8cbfaa0f40943fe62b076067717500056a134 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 13:16:05 +0900 Subject: [PATCH 35/51] =?UTF-8?q?feat:=20=EC=88=98=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EB=98=90=20=EC=9E=85=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 16 ++++++++++++---- src/main/java/view/InputView.java | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index a80b08011..363d7fb13 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -21,6 +21,8 @@ public LottoController(InputView inputView, OutputView outputView, LottoShop lot public void run() { PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); + int manualCount = inputView.readManualCount(); + Lottos manualLottos = toLottos(inputView.readManualNumbers(manualCount)); Lottos lottos = lottoShop.purchase(purchaseAmount); outputView.printResultHeader(lottos.size()); @@ -28,7 +30,7 @@ public void run() { List winningNumbers = inputView.readWinningNumbers(); BonusBall bonusBall = new BonusBall(new LottoNumber(inputView.readBonusBall())); - WinningLotto winningLotto = new WinningLotto(new Lotto(toLottoNumbers(winningNumbers)), bonusBall); + WinningLotto winningLotto = new WinningLotto(toLotto(winningNumbers), bonusBall); WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); @@ -36,10 +38,16 @@ public void run() { outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } - private List toLottoNumbers(List numbers) { - return numbers.stream() + private Lottos toLottos(List> numbers) { + return new Lottos(numbers.stream() + .map(this::toLotto) + .toList()); + } + + private Lotto toLotto(List numbers) { + return new Lotto(numbers.stream() .map(LottoNumber::new) - .toList(); + .toList()); } private List createWinningResults(WinningStatistics winningStatistics) { diff --git a/src/main/java/view/InputView.java b/src/main/java/view/InputView.java index 32c37e84b..cac7c8d49 100644 --- a/src/main/java/view/InputView.java +++ b/src/main/java/view/InputView.java @@ -1,5 +1,6 @@ package view; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner; @@ -12,10 +13,24 @@ public int readAmount() { return Integer.parseInt(scanner.nextLine()); } + public int readManualCount() { + System.out.println("수동으로 구매할 로또 수를 입력해 주세요."); + return Integer.parseInt(scanner.nextLine()); + } + + public List> readManualNumbers(int count) { + System.out.println("수동으로 구매할 번호를 입력해 주세요."); + List> manualNumbers = new ArrayList<>(); + for (int i = 0; i < count; i++) { + manualNumbers.add(parseLottoNumbers(scanner.nextLine())); + } + return manualNumbers; + } + public List readWinningNumbers() { System.out.println("지난 주 당첨 번호를 입력해 주세요."); String input = scanner.nextLine(); - return parseWinningNumbers(input); + return parseLottoNumbers(input); } public int readBonusBall() { @@ -23,7 +38,7 @@ public int readBonusBall() { return Integer.parseInt(scanner.nextLine()); } - private List parseWinningNumbers(String input) { + private List parseLottoNumbers(String input) { return Arrays.stream(input.split(",")) .map(String::trim) .map(Integer::parseInt) From 5eee051432656a57abec744735671aedf8989e5f Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 13:59:41 +0900 Subject: [PATCH 36/51] =?UTF-8?q?feat:=20LottoShop=20=EC=88=98=EB=8F=99/?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EB=A1=9C=EB=98=90=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 2 +- src/main/java/domain/LottoShop.java | 16 ++++++++++++---- src/main/java/domain/PurchaseAmount.java | 16 +++++++++++++++- src/test/java/domain/LottoShopTest.java | 4 ++-- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 363d7fb13..fcad407a5 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -23,7 +23,7 @@ public void run() { PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); int manualCount = inputView.readManualCount(); Lottos manualLottos = toLottos(inputView.readManualNumbers(manualCount)); - Lottos lottos = lottoShop.purchase(purchaseAmount); + Lottos lottos = lottoShop.purchase(purchaseAmount, manualLottos); outputView.printResultHeader(lottos.size()); outputView.printLottos(lottos.toNumberLists()); diff --git a/src/main/java/domain/LottoShop.java b/src/main/java/domain/LottoShop.java index 5c24e299e..5bc975c56 100644 --- a/src/main/java/domain/LottoShop.java +++ b/src/main/java/domain/LottoShop.java @@ -12,15 +12,23 @@ public LottoShop(NumberGenerator numberGenerator) { this.numberGenerator = numberGenerator; } - public Lottos purchase(PurchaseAmount purchaseAmount) { - return new Lottos(createLottos(purchaseAmount)); + public Lottos purchase(PurchaseAmount purchaseAmount, Lottos manualLottos) { + int autoLottoCount = purchaseAmount.calculateAutoLottoCount(manualLottos.size()); + List autoLottos = createAutoLottos(autoLottoCount); + return new Lottos(mergeLottos(manualLottos.lottoToList(), autoLottos)); } - private List createLottos(PurchaseAmount purchaseAmount) { + private List createAutoLottos(int autoLottoCount) { List lottos = new ArrayList<>(); - for (int i = 0; i < purchaseAmount.calculateLottoCount(); i++) { + for (int i = 0; i < autoLottoCount; i++) { lottos.add(new Lotto(numberGenerator.generate())); } return lottos; } + + private List mergeLottos(List manualLottos, List autoLottos) { + List mergedLottos = new ArrayList<>(manualLottos); + mergedLottos.addAll(autoLottos); + return mergedLottos; + } } diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index f88362af6..4b2c6c22d 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -16,7 +16,21 @@ private void validateAmount(int amount) { } } - public int calculateLottoCount() { + public int calculateAutoLottoCount(int manualCount) { + validateManualCount(manualCount); + return calculateLottoCount() - manualCount; + } + + private void validateManualCount(int manualCount) { + if (manualCount < 0) { + throw new IllegalArgumentException("수동 구매 수는 0 이상이어야 합니다."); + } + if (manualCount > calculateLottoCount()) { + throw new IllegalArgumentException("수동 로또 수는 총 로또 수를 넘을 수 없습니다."); + } + } + + private int calculateLottoCount() { return amount / LOTTO_PRICE; } } diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java index d3ebedb1a..6fd4ddd6d 100644 --- a/src/test/java/domain/LottoShopTest.java +++ b/src/test/java/domain/LottoShopTest.java @@ -19,7 +19,7 @@ class LottoShopTest { )); LottoShop lottoShop = new LottoShop(numberGenerator); - Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000)); + Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000), manualLottos); assertThat(lottos.toNumberLists()).hasSize(3); } @@ -33,7 +33,7 @@ class LottoShopTest { )); LottoShop lottoShop = new LottoShop(numberGenerator); - Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000)); + Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000), manualLottos); assertThat(lottos.toNumberLists()).containsExactly( List.of(1, 2, 3, 4, 5, 6), From 6371f0251e23a5f01a299c4b6fc1a0099c08aaf6 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 14:24:12 +0900 Subject: [PATCH 37/51] =?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=EC=9E=85=20=EC=88=98=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 3 ++- src/main/java/view/OutputView.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index fcad407a5..04e0b4149 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -25,7 +25,8 @@ public void run() { Lottos manualLottos = toLottos(inputView.readManualNumbers(manualCount)); Lottos lottos = lottoShop.purchase(purchaseAmount, manualLottos); - outputView.printResultHeader(lottos.size()); + int autoCount = lottos.size() - manualCount; + outputView.printResultHeader(manualCount, autoCount); outputView.printLottos(lottos.toNumberLists()); List winningNumbers = inputView.readWinningNumbers(); diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index c95fd447a..86b6585ea 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -6,8 +6,8 @@ public class OutputView { - public void printResultHeader(int count) { - System.out.println(count + "개를 구매했습니다."); + public void printResultHeader(int manualCount, int autoCount) { + System.out.println("수동으로 " + manualCount + "장, 자동으로 " + autoCount + "개를 구매했습니다."); } public void printLottos(List> lottoNumbers) { From 0ce58f83c22ecef63d2dc7c0cc5b63e154674cce Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 14:52:40 +0900 Subject: [PATCH 38/51] =?UTF-8?q?refactor:=20WinningResult=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=B1=85=EC=9E=84=EC=9D=84=20Controller=EC=97=90?= =?UTF-8?q?=EC=84=9C=20WinningResult=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 15 +-------------- src/main/java/dto/WinningResult.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 04e0b4149..341b095ee 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -5,7 +5,6 @@ import view.InputView; import view.OutputView; -import java.util.Arrays; import java.util.List; public class LottoController { @@ -35,7 +34,7 @@ public void run() { WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); - outputView.printWinningStatistics(createWinningResults(winningStatistics)); + outputView.printWinningStatistics(WinningResult.from(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } @@ -50,16 +49,4 @@ private Lotto toLotto(List numbers) { .map(LottoNumber::new) .toList()); } - - private List createWinningResults(WinningStatistics winningStatistics) { - return Arrays.stream(Rank.values()) - .filter(Rank::isWinning) - .map(rank -> createWinningResult(winningStatistics, rank)) - .toList(); - } - - - private WinningResult createWinningResult(WinningStatistics winningStatistics, Rank rank) { - return WinningResult.from(rank, winningStatistics.countOf(rank)); - } } diff --git a/src/main/java/dto/WinningResult.java b/src/main/java/dto/WinningResult.java index 6540fd488..f29d8f7e4 100644 --- a/src/main/java/dto/WinningResult.java +++ b/src/main/java/dto/WinningResult.java @@ -1,6 +1,10 @@ package dto; import domain.Rank; +import domain.WinningStatistics; + +import java.util.Arrays; +import java.util.List; public record WinningResult(String message, int count) { public static WinningResult from(Rank rank, int count) { @@ -9,4 +13,11 @@ public static WinningResult from(Rank rank, int count) { } return new WinningResult(rank.getMatchCount() + "개 일치 (" + rank.getPrizeMoney() + "원)", count); } + + public static List from(WinningStatistics winningStatistics) { + return Arrays.stream(Rank.values()) + .filter(Rank::isWinning) + .map(rank -> from(rank, winningStatistics.countOf(rank))) + .toList(); + } } From 6270d1282dea9dc12a3f2e0b67ed5496b25d3296 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 15:07:25 +0900 Subject: [PATCH 39/51] =?UTF-8?q?refactor:=20WinningResult=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/dto/WinningResult.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/dto/WinningResult.java b/src/main/java/dto/WinningResult.java index f29d8f7e4..4ec6c8c77 100644 --- a/src/main/java/dto/WinningResult.java +++ b/src/main/java/dto/WinningResult.java @@ -7,17 +7,17 @@ import java.util.List; public record WinningResult(String message, int count) { - public static WinningResult from(Rank rank, int count) { - if (rank == Rank.FIVE_BONUS_MATCH) { - return new WinningResult("5개 일치, 보너스 볼 일치(" + rank.getPrizeMoney() + "원)", count); - } - return new WinningResult(rank.getMatchCount() + "개 일치 (" + rank.getPrizeMoney() + "원)", count); - } - public static List from(WinningStatistics winningStatistics) { return Arrays.stream(Rank.values()) .filter(Rank::isWinning) - .map(rank -> from(rank, winningStatistics.countOf(rank))) + .map(rank -> createMessage(rank, winningStatistics.countOf(rank))) .toList(); } + + private static WinningResult createMessage(Rank rank, int count) { + if (rank == Rank.FIVE_BONUS_MATCH) { + return new WinningResult("5개 일치, 보너스 볼 일치(" + rank.getPrizeMoney() + "원)", count); + } + return new WinningResult(rank.getMatchCount() + "개 일치 (" + rank.getPrizeMoney() + "원)", count); + } } From 97c03ab8b8f1fe67cb89d98618b21fadf52eba12 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 15:47:25 +0900 Subject: [PATCH 40/51] =?UTF-8?q?feat:=20manualCount=EB=A5=BC=20ManualLott?= =?UTF-8?q?oCount=EB=A1=9C=20=EC=9B=90=EC=8B=9C=EA=B0=92=20=ED=8F=AC?= =?UTF-8?q?=EC=9E=A5=20refactor:=20run=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 31 ++++++++++++++----- src/main/java/domain/ManualLottoCount.java | 23 ++++++++++++++ src/main/java/domain/PurchaseAmount.java | 18 +++-------- 3 files changed, 50 insertions(+), 22 deletions(-) create mode 100644 src/main/java/domain/ManualLottoCount.java diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 341b095ee..77561c442 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -20,20 +20,35 @@ public LottoController(InputView inputView, OutputView outputView, LottoShop lot public void run() { PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); - int manualCount = inputView.readManualCount(); - Lottos manualLottos = toLottos(inputView.readManualNumbers(manualCount)); - Lottos lottos = lottoShop.purchase(purchaseAmount, manualLottos); + ManualLottoCount manualLottoCount = new ManualLottoCount(inputView.readManualCount(), purchaseAmount); + Lottos lottos = purchaseLottos(purchaseAmount, manualLottoCount); - int autoCount = lottos.size() - manualCount; - outputView.printResultHeader(manualCount, autoCount); + printPurchaseResult(lottos, manualLottoCount); + + WinningLotto winningLotto = readWinningLotto(); + WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); + + printWinningResult(winningStatistics, purchaseAmount); + } + + private Lottos purchaseLottos(PurchaseAmount purchaseAmount, ManualLottoCount manualLottoCount) { + Lottos manualLottos = toLottos(inputView.readManualNumbers(manualLottoCount.count())); + return lottoShop.purchase(purchaseAmount, manualLottos); + } + + private void printPurchaseResult(Lottos lottos, ManualLottoCount manualLottoCount) { + int autoCount = lottos.size() - manualLottoCount.count(); + outputView.printResultHeader(manualLottoCount.count(), autoCount); outputView.printLottos(lottos.toNumberLists()); + } + private WinningLotto readWinningLotto() { List winningNumbers = inputView.readWinningNumbers(); BonusBall bonusBall = new BonusBall(new LottoNumber(inputView.readBonusBall())); - WinningLotto winningLotto = new WinningLotto(toLotto(winningNumbers), bonusBall); - - WinningStatistics winningStatistics = WinningStatistics.from(lottos, winningLotto); + return new WinningLotto(toLotto(winningNumbers), bonusBall); + } + private void printWinningResult(WinningStatistics winningStatistics, PurchaseAmount purchaseAmount) { outputView.printWinningStatistics(WinningResult.from(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } diff --git a/src/main/java/domain/ManualLottoCount.java b/src/main/java/domain/ManualLottoCount.java new file mode 100644 index 000000000..025da317d --- /dev/null +++ b/src/main/java/domain/ManualLottoCount.java @@ -0,0 +1,23 @@ +package domain; + +public class ManualLottoCount { + private final int count; + + public ManualLottoCount(int count, PurchaseAmount purchaseAmount) { + validate(count, purchaseAmount); + this.count = count; + } + + private void validate(int count, PurchaseAmount purchaseAmount) { + if (count < 0) { + throw new IllegalArgumentException("수동 구매 수는 0 이상이어야 합니다."); + } + if (count > purchaseAmount.calculateLottoCount()) { + throw new IllegalArgumentException("수동 로또 수는 총 로또 수를 넘을 수 없습니다."); + } + } + + public int count() { + return count; + } +} diff --git a/src/main/java/domain/PurchaseAmount.java b/src/main/java/domain/PurchaseAmount.java index 4b2c6c22d..60d89cd39 100644 --- a/src/main/java/domain/PurchaseAmount.java +++ b/src/main/java/domain/PurchaseAmount.java @@ -16,21 +16,11 @@ private void validateAmount(int amount) { } } - public int calculateAutoLottoCount(int manualCount) { - validateManualCount(manualCount); - return calculateLottoCount() - manualCount; - } - - private void validateManualCount(int manualCount) { - if (manualCount < 0) { - throw new IllegalArgumentException("수동 구매 수는 0 이상이어야 합니다."); - } - if (manualCount > calculateLottoCount()) { - throw new IllegalArgumentException("수동 로또 수는 총 로또 수를 넘을 수 없습니다."); - } + public int calculateLottoCount() { + return amount / LOTTO_PRICE; } - private int calculateLottoCount() { - return amount / LOTTO_PRICE; + public int calculateAutoLottoCount(int manualCount) { + return calculateLottoCount() - manualCount; } } From 2a9c1d12ff038d84acdfab558ef9dafef9e72213 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 15:55:43 +0900 Subject: [PATCH 41/51] =?UTF-8?q?test:=20LottoShopTest=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/domain/LottoShopTest.java | 29 +++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/test/java/domain/LottoShopTest.java b/src/test/java/domain/LottoShopTest.java index 6fd4ddd6d..3af690d38 100644 --- a/src/test/java/domain/LottoShopTest.java +++ b/src/test/java/domain/LottoShopTest.java @@ -11,7 +11,7 @@ class LottoShopTest { @Test - void 구입금액만큼_로또를_생성한다() { + void 구입금액에_맞게_자동_로또를_생성한다() { NumberGenerator numberGenerator = new TestNumberGenerator(List.of( new LottoNumber(1), new LottoNumber(2), new LottoNumber(3), new LottoNumber(4), @@ -19,26 +19,37 @@ class LottoShopTest { )); LottoShop lottoShop = new LottoShop(numberGenerator); - Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000), manualLottos); + Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000), new Lottos(List.of())); assertThat(lottos.toNumberLists()).hasSize(3); + assertThat(lottos.toNumberLists()).containsExactly( + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 6), + List.of(1, 2, 3, 4, 5, 6) + ); } @Test - void 생성된_번호로_로또가_구성된다() { + void 수동_로또와_자동_로또를_함께_반환한다() { NumberGenerator numberGenerator = new TestNumberGenerator(List.of( - new LottoNumber(1), new LottoNumber(2), - new LottoNumber(3), new LottoNumber(4), - new LottoNumber(5), new LottoNumber(6) + new LottoNumber(7), new LottoNumber(8), + new LottoNumber(9), new LottoNumber(10), + new LottoNumber(11), new LottoNumber(12) )); LottoShop lottoShop = new LottoShop(numberGenerator); - Lottos lottos = lottoShop.purchase(new PurchaseAmount(3000), manualLottos); + Lottos manualLottos = new Lottos(List.of( + new Lotto(List.of( + new LottoNumber(1), new LottoNumber(2), new LottoNumber(3), + new LottoNumber(4), new LottoNumber(5), new LottoNumber(6) + )) + )); + + Lottos lottos = lottoShop.purchase(new PurchaseAmount(2000), manualLottos); assertThat(lottos.toNumberLists()).containsExactly( List.of(1, 2, 3, 4, 5, 6), - List.of(1, 2, 3, 4, 5, 6), - List.of(1, 2, 3, 4, 5, 6) + List.of(7, 8, 9, 10, 11, 12) ); } } From f7bf823f975c1674852405115a76edd637530d12 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 15:57:11 +0900 Subject: [PATCH 42/51] =?UTF-8?q?test:=20ManualLottoCountTest=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 --- .../java/domain/ManualLottoCountTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/java/domain/ManualLottoCountTest.java diff --git a/src/test/java/domain/ManualLottoCountTest.java b/src/test/java/domain/ManualLottoCountTest.java new file mode 100644 index 000000000..576f63526 --- /dev/null +++ b/src/test/java/domain/ManualLottoCountTest.java @@ -0,0 +1,26 @@ +package domain; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ManualLottoCountTest { + + @Test + void 수동_구매_수가_0_미만이면_예외가_발생한다() { + PurchaseAmount purchaseAmount = new PurchaseAmount(5000); + + assertThatThrownBy(() -> new ManualLottoCount(-1, purchaseAmount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("수동 구매 수는 0 이상이어야 합니다."); + } + + @Test + void 수동_구매_수가_총_구매_가능_수를_초과하면_예외가_발생한다() { + PurchaseAmount purchaseAmount = new PurchaseAmount(4000); + + assertThatThrownBy(() -> new ManualLottoCount(5, purchaseAmount)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("수동 로또 수는 총 로또 수를 넘을 수 없습니다."); + } +} From 76adb1b557c9297dbdc2e0a1ec9720b65a74df82 Mon Sep 17 00:00:00 2001 From: dahyn Date: Mon, 6 Apr 2026 16:34:20 +0900 Subject: [PATCH 43/51] =?UTF-8?q?docs:=20README=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/README.md | 69 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/src/README.md b/src/README.md index 84b3d4a2c..f174ed6b7 100644 --- a/src/README.md +++ b/src/README.md @@ -3,8 +3,9 @@ ## 프로젝트 개요 로또 구매 및 당첨 통계 계산 프로그램을 구현했습니다. -사용자는 구입 금액과 지난 주 당첨 번호를 입력할 수 있으며, 구입 금액에 해당하는 수만큼 로또를 자동으로 발급받습니다. -발급된 로또와 당첨 번호를 비교하여 당첨 통계를 계산하고, 총 수익률을 출력합니다. +사용자는 구입 금액을 입력하고, 수동으로 구매할 로또 개수와 번호를 입력할 수 있습니다. +남은 수량만큼은 자동으로 발급되며, 지난 주 당첨 번호와 보너스 볼을 입력해 당첨 결과를 계산합니다. +구매한 로또와 당첨 번호를 비교하여 당첨 통계를 출력하고, 총 수익률을 계산합니다. ## 기술 스택 @@ -26,25 +27,33 @@ - 로또 번호가 6개가 아니면 예외를 발생시킨다. - 로또 번호가 중복되면 예외를 발생시킨다. - 당첨 번호와 비교하여 일치 개수를 계산한다. +- 특정 번호를 포함하는지 확인한다. ### Lottos - 여러 장의 로또를 관리한다. - 구매한 로또 개수를 반환한다. - 로또 목록을 출력용 번호 리스트로 변환한다. -- 당첨 번호를 기준으로 당첨 통계를 생성한다. +- 로또 목록을 반환한다. ### PurchaseAmount - 구입 금액을 관리한다. - 구입 금액이 1000원 미만이면 예외를 발생시킨다. - 구입 금액이 1000원 단위가 아니면 예외를 발생시킨다. -- 구입 금액으로 구매 가능한 로또 개수를 계산한다. +- 수동 구매 수를 제외한 자동 구매 개수를 계산한다. + +### ManualLottoCount + +- 수동 구매 로또 개수를 관리한다. +- 수동 구매 수가 0 미만이면 예외를 발생시킨다. +- 수동 구매 수가 총 구매 가능 로또 수를 초과하면 예외를 발생시킨다. ### LottoShop - 로또 구매를 담당한다. -- 구입 금액에 해당하는 개수만큼 로또를 생성한다. +- 수동 로또와 자동 로또를 함께 구매한다. +- 자동 로또를 생성하고 수동 로또와 병합한다. ### NumberGenerator @@ -52,32 +61,48 @@ - `RandomNumberGenerator`는 1부터 45 사이의 숫자 중 6개를 무작위로 생성한다. - `TestNumberGenerator`는 테스트에서 원하는 번호를 고정으로 생성한다. +### BonusBall + +- 보너스 볼 번호를 관리한다. +- 보너스 볼이 없으면 예외를 발생시킨다. + +### WinningLotto + +- 지난 주 당첨 번호와 보너스 볼을 함께 관리한다. +- 보너스 볼이 당첨 번호와 중복되면 예외를 발생시킨다. +- 구매한 로또의 당첨 등수를 판별한다. + ### Rank - 당첨 등수를 관리한다. -- 일치 개수에 따라 3등, 4등, 5등, 6등을 판별한다. +- 일치 개수와 보너스 볼 일치 여부에 따라 등수를 판별한다. - 각 등수에 해당하는 당첨 금액을 관리한다. ### WinningStatistics - 등수별 당첨 개수를 관리한다. -- 등수별 당첨 개수를 증가시킨다. +- 구매한 로또 목록과 당첨 번호를 기준으로 당첨 통계를 생성한다. +- 등수별 당첨 개수를 반환한다. - 총 당첨금을 계산한다. - 구입 금액을 기준으로 수익률을 계산한다. ### WinningResult - 당첨 통계 출력에 필요한 데이터를 관리한다. -- 일치 개수, 당첨 금액, 당첨 개수를 담아 출력 계층으로 전달한다. +- `WinningStatistics`를 출력용 결과 목록으로 변환한다. +- 당첨 메시지와 당첨 개수를 담아 출력 계층으로 전달한다. ### InputView - 구입 금액을 입력받는다. +- 수동으로 구매할 로또 수를 입력받는다. +- 수동으로 구매할 번호를 입력받는다. - 지난 주 당첨 번호를 쉼표(,)를 기준으로 구분하여 입력받는다. +- 보너스 볼을 입력받는다. ### OutputView -- 구매 결과 헤더를 출력한다. +- 수동 구매 수와 자동 구매 수를 함께 출력한다. - 발급된 로또 번호를 출력한다. - 당첨 통계를 출력한다. - 총 수익률을 출력한다. @@ -85,7 +110,6 @@ ### LottoController - 로또 게임의 전체 진행을 담당한다. -- 구입 금액 입력, 로또 구매, 당첨 번호 입력, 당첨 통계 계산, 결과 출력을 순서대로 연결한다. ## 테스트 @@ -100,34 +124,39 @@ ### LottoShopTest -- 구입 금액만큼 로또를 구매하는지 테스트한다. +- 구입 금액에 맞게 자동 로또가 생성되는지 테스트한다. +- 수동 로또와 자동 로또가 함께 반환되는지 테스트한다. ### LottoNumberTest -- 로또 번호가 1보다 작으면 예외가 발생하는지 테스트한다. -- 로또 번호가 45보다 크면 예외가 발생하는지 테스트한다. +- 로또 번호가 범위를 벗어나면 예외가 발생하는지 테스트한다. ### PurchaseAmountTest - 구입 금액이 1000원 미만이면 예외가 발생하는지 테스트한다. - 구입 금액이 1000원 단위가 아니면 예외가 발생하는지 테스트한다. -- 구입 금액으로 구매 가능한 로또 개수를 계산하는지 테스트한다. + +### ManualLottoCountTest + +- 수동 구매 수가 0 미만이면 예외가 발생하는지 테스트한다. +- 수동 구매 수가 총 구매 가능 수를 초과하면 예외가 발생하는지 테스트한다. ### WinningStatisticsTest -- 등수를 추가하면 당첨 개수가 증가하는지 테스트한다. +- 당첨 결과별 개수를 집계하는지 테스트한다. - 총 당첨금을 계산하는지 테스트한다. - 수익률을 계산하는지 테스트한다. ## 설계 의도 -로또 번호와 구입 금액을 각각 `LottoNumber`, `PurchaseAmount` 로 포장했습니다. -이를 통해 로또 번호 범위 검증과 구입 금액 검증 책임을 객체가 관리하도록 했습니다. +로또 번호, 구입 금액, 수동 구매 수를 각각 `LottoNumber`, `PurchaseAmount`, `ManualLottoCount`로 포장했습니다. +이를 통해 원시값이 직접 흩어지지 않도록 하고, 각 값에 대한 검증 책임을 객체가 관리하도록 했습니다. 또한 `List`를 `Lottos`로 감싸는 방식으로 로또 목록에 대한 책임을 분리했습니다. -당첨 결과는 `Rank`와 `WinningStatistics`를 통해 계산하도록 했습니다. -`Rank`는 일치 개수에 따른 등수와 당첨 금액을 관리하고, `WinningStatistics`는 등수별 개수와 총 당첨금, 수익률 계산을 담당하도록 구성했습니다. +당첨 결과는 `WinningLotto`, `Rank`, `WinningStatistics`를 통해 계산하도록 했습니다. +`WinningLotto`는 당첨 번호와 보너스 볼을 함께 관리하고, `Rank`는 일치 개수와 보너스 볼 일치 여부에 따른 등수 및 당첨 금액을 관리합니다. +`WinningStatistics`는 등수별 개수와 총 당첨금, 수익률 계산을 담당하도록 구성했습니다. 입력과 출력은 `InputView`, `OutputView`로 분리했습니다. -또한 출력에 필요한 데이터는 `WinningResult`로 별도 전달하여 뷰가 도메인 객체를 직접 해석하지 않도록 구성했습니다. +또한 출력에 필요한 데이터는 `WinningResult`로 변환하여 뷰가 도메인을 직접 알지 못하도록 구성했습니다. From 29b1bc5c0ac2abd7b4fa5e6ef5668ba2f6cfefae Mon Sep 17 00:00:00 2001 From: dahyn Date: Wed, 8 Apr 2026 17:02:27 +0900 Subject: [PATCH 44/51] =?UTF-8?q?refactor:=20=EC=99=80=EC=9D=BC=EB=93=9C?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=20import=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 77561c442..98d7cbf54 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -1,6 +1,14 @@ package controller; -import domain.*; +import domain.BonusBall; +import domain.Lotto; +import domain.LottoNumber; +import domain.LottoShop; +import domain.Lottos; +import domain.ManualLottoCount; +import domain.PurchaseAmount; +import domain.WinningLotto; +import domain.WinningStatistics; import dto.WinningResult; import view.InputView; import view.OutputView; From da199f6d22b84759e1b73dac924f4e5a5ccda288 Mon Sep 17 00:00:00 2001 From: dahyn Date: Wed, 8 Apr 2026 17:36:16 +0900 Subject: [PATCH 45/51] =?UTF-8?q?refactor:=20Lotto=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EC=8B=9C=20=EB=B2=88=ED=98=B8=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 9 ++++++++- src/main/java/view/OutputView.java | 8 +------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 72888a43d..2e6a22196 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -1,6 +1,7 @@ package domain; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; public class Lotto { @@ -9,7 +10,7 @@ public class Lotto { public Lotto(List numbers) { validate(numbers); - this.numbers = new ArrayList<>(numbers); + this.numbers = new ArrayList<>(sortNumbers(numbers)); } public int countMatch(Lotto winningLotto) { @@ -31,6 +32,12 @@ private void validate(List numbers) { validateDuplicate(numbers); } + private List sortNumbers(List numbers) { + return numbers.stream() + .sorted(Comparator.comparingInt(LottoNumber::number)) + .toList(); + } + private void validateLottoSize(List numbers) { if (numbers.size() != LOTTO_SIZE) { throw new IllegalArgumentException("로또 숫자의 갯수는 6개입니다."); diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 86b6585ea..4a91f8fe3 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -12,7 +12,7 @@ public void printResultHeader(int manualCount, int autoCount) { public void printLottos(List> lottoNumbers) { for (List numbers : lottoNumbers) { - System.out.println(sortLotto(numbers)); + System.out.println(numbers); } } @@ -21,12 +21,6 @@ public void printWinningStatistics(List winningResults) { printRankCount(winningResults); } - private List sortLotto(List lotto) { - return lotto.stream() - .sorted() - .toList(); - } - private void printStatisticsHeader() { System.out.println(); System.out.println("당첨 통계"); From 6e33782907cf8aebc6712f6d4d5e50dbbb98e680 Mon Sep 17 00:00:00 2001 From: dahyn Date: Wed, 8 Apr 2026 18:04:26 +0900 Subject: [PATCH 46/51] =?UTF-8?q?refactor:=20Rank=EB=A1=9C=20=EB=8B=B9?= =?UTF-8?q?=EC=B2=A8=20=EA=B2=B0=EA=B3=BC=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=B1=85=EC=9E=84=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Rank.java | 20 +++++++++++--------- src/main/java/dto/WinningResult.java | 5 +---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index 002da03bc..6a59e586e 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,19 +1,21 @@ package domain; public enum Rank { - THREE_MATCH(3, 5000), - FOUR_MATCH(4, 50000), - FIVE_MATCH(5, 1500000), - FIVE_BONUS_MATCH(5, 30000000), - SIX_MATCH(6, 2000000000), - MISS(0, 0); + THREE_MATCH(3, 5000, "3개 일치"), + FOUR_MATCH(4, 50000, "4개 일치"), + FIVE_MATCH(5, 1500000, "5개 일치"), + FIVE_BONUS_MATCH(5, 30000000, "5개 일치, 보너스 볼 일치"), + SIX_MATCH(6, 2000000000, "6개 일치"), + MISS(0, 0, "꽝"); private final int matchCount; private final int prizeMoney; + private final String displayName; - Rank(int matchCount, int prizeMoney) { + Rank(int matchCount, int prizeMoney, String displayName) { this.matchCount = matchCount; this.prizeMoney = prizeMoney; + this.displayName = displayName; } public static Rank from(int matchCount, boolean bonusBallMatched) { @@ -43,7 +45,7 @@ public int getPrizeMoney() { return prizeMoney; } - public int getMatchCount() { - return matchCount; + public String getDisplayName() { + return displayName; } } diff --git a/src/main/java/dto/WinningResult.java b/src/main/java/dto/WinningResult.java index 4ec6c8c77..4e595b0c8 100644 --- a/src/main/java/dto/WinningResult.java +++ b/src/main/java/dto/WinningResult.java @@ -15,9 +15,6 @@ public static List from(WinningStatistics winningStatistics) { } private static WinningResult createMessage(Rank rank, int count) { - if (rank == Rank.FIVE_BONUS_MATCH) { - return new WinningResult("5개 일치, 보너스 볼 일치(" + rank.getPrizeMoney() + "원)", count); - } - return new WinningResult(rank.getMatchCount() + "개 일치 (" + rank.getPrizeMoney() + "원)", count); + return new WinningResult(rank.getDisplayName() + " (" + rank.getPrizeMoney() + "원)", count); } } From 16c27c6d07d7661553f93767f4bcff9a08bf0dd6 Mon Sep 17 00:00:00 2001 From: dahyn Date: Wed, 8 Apr 2026 18:13:16 +0900 Subject: [PATCH 47/51] =?UTF-8?q?refactor:=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20static=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Lotto.java | 6 +++--- src/main/java/domain/LottoNumber.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 2e6a22196..41dced7ef 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -27,7 +27,7 @@ public boolean contains(LottoNumber lottoNumber) { return numbers.contains(lottoNumber); } - private void validate(List numbers) { + private static void validate(List numbers) { validateLottoSize(numbers); validateDuplicate(numbers); } @@ -38,13 +38,13 @@ private List sortNumbers(List numbers) { .toList(); } - private void validateLottoSize(List numbers) { + private static void validateLottoSize(List numbers) { if (numbers.size() != LOTTO_SIZE) { throw new IllegalArgumentException("로또 숫자의 갯수는 6개입니다."); } } - private void validateDuplicate(List numbers) { + private static void validateDuplicate(List numbers) { long distinctCount = numbers.stream().distinct().count(); if (distinctCount != LOTTO_SIZE) { throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다."); diff --git a/src/main/java/domain/LottoNumber.java b/src/main/java/domain/LottoNumber.java index 8d3943ae2..dd131d62c 100644 --- a/src/main/java/domain/LottoNumber.java +++ b/src/main/java/domain/LottoNumber.java @@ -8,11 +8,11 @@ public record LottoNumber(int number) { validate(number); } - private void validate(int number) { + private static void validate(int number) { validateRange(number); } - private void validateRange(int number) { + private static void validateRange(int number) { if (number < MIN_NUMBER || number > MAX_NUMBER) { throw new IllegalArgumentException("로또 번호는 1부터 45 사이여야 합니다."); } From 16f706a25f877ef26f2ff8ba2f5d9eb5aca061a8 Mon Sep 17 00:00:00 2001 From: dahyn Date: Wed, 8 Apr 2026 18:17:41 +0900 Subject: [PATCH 48/51] =?UTF-8?q?refactor:=20matchCount=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/Rank.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/domain/Rank.java b/src/main/java/domain/Rank.java index 6a59e586e..a31885f1b 100644 --- a/src/main/java/domain/Rank.java +++ b/src/main/java/domain/Rank.java @@ -1,19 +1,17 @@ package domain; public enum Rank { - THREE_MATCH(3, 5000, "3개 일치"), - FOUR_MATCH(4, 50000, "4개 일치"), - FIVE_MATCH(5, 1500000, "5개 일치"), - FIVE_BONUS_MATCH(5, 30000000, "5개 일치, 보너스 볼 일치"), - SIX_MATCH(6, 2000000000, "6개 일치"), - MISS(0, 0, "꽝"); + THREE_MATCH(5000, "3개 일치"), + FOUR_MATCH(50000, "4개 일치"), + FIVE_MATCH(1500000, "5개 일치"), + FIVE_BONUS_MATCH(30000000, "5개 일치, 보너스 볼 일치"), + SIX_MATCH(2000000000, "6개 일치"), + MISS(0, "꽝"); - private final int matchCount; private final int prizeMoney; private final String displayName; - Rank(int matchCount, int prizeMoney, String displayName) { - this.matchCount = matchCount; + Rank(int prizeMoney, String displayName) { this.prizeMoney = prizeMoney; this.displayName = displayName; } From 1ae5b413690ba95b2d0434758996df781f45a2d4 Mon Sep 17 00:00:00 2001 From: dahyn Date: Wed, 8 Apr 2026 18:26:00 +0900 Subject: [PATCH 49/51] =?UTF-8?q?refactor:=20WinningStatistics=EC=97=90=20?= =?UTF-8?q?EnumMap=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/domain/WinningStatistics.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/domain/WinningStatistics.java b/src/main/java/domain/WinningStatistics.java index 60440faa3..984edd4fd 100644 --- a/src/main/java/domain/WinningStatistics.java +++ b/src/main/java/domain/WinningStatistics.java @@ -1,13 +1,13 @@ package domain; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; public class WinningStatistics { private final Map statistics; public WinningStatistics() { - statistics = new HashMap<>(); + statistics = new EnumMap<>(Rank.class); initialize(); } From c3bda2c92b5222dac9d3bce2ee2a320aea27184b Mon Sep 17 00:00:00 2001 From: dahyn Date: Thu, 9 Apr 2026 12:33:20 +0900 Subject: [PATCH 50/51] =?UTF-8?q?refactor:=20Lotto,=20Lottos=EC=9D=98=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=20=EC=B1=85=EC=9E=84=EC=9D=84=20=EA=B0=81=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/controller/LottoController.java | 16 ++-------------- src/main/java/domain/Lotto.java | 6 ++++++ src/main/java/domain/Lottos.java | 6 ++++++ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 98d7cbf54..76cc030a5 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -40,7 +40,7 @@ public void run() { } private Lottos purchaseLottos(PurchaseAmount purchaseAmount, ManualLottoCount manualLottoCount) { - Lottos manualLottos = toLottos(inputView.readManualNumbers(manualLottoCount.count())); + Lottos manualLottos = Lottos.from(inputView.readManualNumbers(manualLottoCount.count())); return lottoShop.purchase(purchaseAmount, manualLottos); } @@ -53,23 +53,11 @@ private void printPurchaseResult(Lottos lottos, ManualLottoCount manualLottoCoun private WinningLotto readWinningLotto() { List winningNumbers = inputView.readWinningNumbers(); BonusBall bonusBall = new BonusBall(new LottoNumber(inputView.readBonusBall())); - return new WinningLotto(toLotto(winningNumbers), bonusBall); + return new WinningLotto(Lotto.from(winningNumbers), bonusBall); } private void printWinningResult(WinningStatistics winningStatistics, PurchaseAmount purchaseAmount) { outputView.printWinningStatistics(WinningResult.from(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } - - private Lottos toLottos(List> numbers) { - return new Lottos(numbers.stream() - .map(this::toLotto) - .toList()); - } - - private Lotto toLotto(List numbers) { - return new Lotto(numbers.stream() - .map(LottoNumber::new) - .toList()); - } } diff --git a/src/main/java/domain/Lotto.java b/src/main/java/domain/Lotto.java index 41dced7ef..460352d73 100644 --- a/src/main/java/domain/Lotto.java +++ b/src/main/java/domain/Lotto.java @@ -13,6 +13,12 @@ public Lotto(List numbers) { this.numbers = new ArrayList<>(sortNumbers(numbers)); } + public static Lotto from(List numbers) { + return new Lotto(numbers.stream() + .map(LottoNumber::new) + .toList()); + } + public int countMatch(Lotto winningLotto) { return (int) numbers.stream() .filter(winningLotto::contains) diff --git a/src/main/java/domain/Lottos.java b/src/main/java/domain/Lottos.java index 3412e3926..94a0d9138 100644 --- a/src/main/java/domain/Lottos.java +++ b/src/main/java/domain/Lottos.java @@ -10,6 +10,12 @@ public Lottos(List lottos) { this.lottos = new ArrayList<>(lottos); } + public static Lottos from(List> lottos) { + return new Lottos(lottos.stream() + .map(Lotto::from) + .toList()); + } + public int size() { return lottos.size(); } From f27e9ccdb49a202c06fd6f19221835df9a75092c Mon Sep 17 00:00:00 2001 From: dahyn Date: Thu, 9 Apr 2026 13:34:34 +0900 Subject: [PATCH 51/51] =?UTF-8?q?feat:=20=EC=9E=85=EB=A0=A5=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=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 --- src/main/java/controller/LottoController.java | 26 ++++++++++++++----- src/main/java/view/OutputView.java | 4 +++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/controller/LottoController.java b/src/main/java/controller/LottoController.java index 76cc030a5..7ef84cce8 100644 --- a/src/main/java/controller/LottoController.java +++ b/src/main/java/controller/LottoController.java @@ -13,7 +13,7 @@ import view.InputView; import view.OutputView; -import java.util.List; +import java.util.function.Supplier; public class LottoController { private final InputView inputView; @@ -27,9 +27,9 @@ public LottoController(InputView inputView, OutputView outputView, LottoShop lot } public void run() { - PurchaseAmount purchaseAmount = new PurchaseAmount(inputView.readAmount()); - ManualLottoCount manualLottoCount = new ManualLottoCount(inputView.readManualCount(), purchaseAmount); - Lottos lottos = purchaseLottos(purchaseAmount, manualLottoCount); + PurchaseAmount purchaseAmount = retryUntilValid(() -> new PurchaseAmount(inputView.readAmount())); + ManualLottoCount manualLottoCount = retryUntilValid(() -> new ManualLottoCount(inputView.readManualCount(), purchaseAmount)); + Lottos lottos = retryUntilValid(() -> purchaseLottos(purchaseAmount, manualLottoCount)); printPurchaseResult(lottos, manualLottoCount); @@ -51,13 +51,25 @@ private void printPurchaseResult(Lottos lottos, ManualLottoCount manualLottoCoun } private WinningLotto readWinningLotto() { - List winningNumbers = inputView.readWinningNumbers(); - BonusBall bonusBall = new BonusBall(new LottoNumber(inputView.readBonusBall())); - return new WinningLotto(Lotto.from(winningNumbers), bonusBall); + Lotto winningLotto = retryUntilValid(() -> Lotto.from(inputView.readWinningNumbers())); + return retryUntilValid(() -> { + BonusBall bonusBall = new BonusBall(new LottoNumber(inputView.readBonusBall())); + return new WinningLotto(winningLotto, bonusBall); + }); } private void printWinningResult(WinningStatistics winningStatistics, PurchaseAmount purchaseAmount) { outputView.printWinningStatistics(WinningResult.from(winningStatistics)); outputView.printProfitRate(winningStatistics.calculateProfitRate(purchaseAmount)); } + + private T retryUntilValid(Supplier supplier) { + while (true) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } } diff --git a/src/main/java/view/OutputView.java b/src/main/java/view/OutputView.java index 4a91f8fe3..6ce34d8e7 100644 --- a/src/main/java/view/OutputView.java +++ b/src/main/java/view/OutputView.java @@ -36,4 +36,8 @@ private void printRankCount(List winningResults) { public void printProfitRate(double profitRate) { System.out.println("총 수익률은 " + String.format("%.2f", profitRate) + "입니다."); } + + public void printErrorMessage(String message) { + System.out.println("[ERROR] " + message); + } }