Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
454c575
feat: 1단계 로또 자동 구매 구현
eas-yin Aug 1, 2026
93aa2d4
refactor: MVC 패턴 구조 적용
eas-yin Aug 1, 2026
549c80d
feat: 2단계 로또 당첨 구현
eas-yin Aug 2, 2026
7d69333
feat: 3단계 로또 2등 당첨 구현
eas-yin Aug 2, 2026
d02b955
feat: 4단계 로또 수동 구매 구현
eas-yin Aug 2, 2026
915790b
LottoNumber 생성 팩토리 메서드 추가 및 LottoPick 메서드 수정
eas-yin Aug 6, 2026
d8d6548
LottoNumber 유효성 검사 구현 및 타입 변경
eas-yin Aug 6, 2026
d405a8e
LottoNumber 객체 비교 및 sort 오류 수정
eas-yin Aug 6, 2026
50395ca
test: LottoNumber 정렬 테스트 추가
eas-yin Aug 6, 2026
0664a34
refactor: LottoNumber 생성자 private으로 변경
eas-yin Aug 6, 2026
27c891f
refactor: Lotto를 일급 컬렉션으로 변경 및 LottoGenerator로 책임 분리
eas-yin Aug 6, 2026
5086f62
fix: 수동과 자동을 합친 후 출력하도록 수정
eas-yin Aug 6, 2026
eaf3205
refactor: LottoNumber toString() 추가
eas-yin Aug 6, 2026
92d0254
fix: 불필요한 scanner.nextLine() 제거
eas-yin Aug 6, 2026
d178e57
refactor: PurchaseAmount 검증 및 계산 책임 변경
eas-yin Aug 6, 2026
cf56668
refactor: Lotto 방어적 복사 적용 및 테스트 추가
eas-yin Aug 9, 2026
71a792e
refactor: 숫자 대신 Rank enum으로 변경 및 관련 클래스 수정
eas-yin Aug 9, 2026
2851bd0
style: Java 코드 컨벤션에 맞게 수정
eas-yin Aug 9, 2026
13229a9
refactor: 빈 줄 출력 및 getNumber() 삭제
eas-yin Aug 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import domain.LottoResult;
import domain.WinningRate;
import view.InputView;
import view.ResultView;
import domain.Lotto;

import java.util.List;


public class Application {
public static void main(String[] args) {
Lotto lotto = new Lotto();
LottoResult lottoResult = new LottoResult();
WinningRate winningRate = new WinningRate();

int purchasePrice = InputView.inputPrice();
int lottoCount = lotto.calculateCount(purchasePrice);
int passiveCount = InputView.inputPassiveCount();
int autoCount = lottoCount - passiveCount;
Comment on lines +18 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

이렇게 수동 구매 수를 음수로 입력하면, 실제로 지불한 금액보다 많은 로또를 구매할 수 있게 되네요~
수동 구매 수는 어떤 범위여야 할까요?
그리고 전체 구매 수와 수동 구매 수 사이의 규칙은 어느 객체가 보장하면 좋을지도 고민해보면 좋겠습니다.

0, 전체 구매 수와 같은 값, 전체 구매 수보다 큰 값, 음수를 각각 테스트해보는 것도 도움이 될 것 같아요~


List<List<Integer>> passiveLotto = InputView.inputPassiveLotto(passiveCount);
List<List<Integer>> autoLotto = lotto.lottoLists(autoCount);

ResultView.printPurchase(passiveLotto, passiveCount, autoCount);
passiveLotto.addAll(autoLotto);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컴파일 문제를 해결한 뒤에는 수동 0장, 자동 3장이나 수동 1장, 자동 2장을 입력해서 직접 실행해보면 좋겠습니다.

지금은 수동 로또만 printPurchase()에 전달하고 출력이 끝난 뒤에 자동 로또를 합치고 있네요.
그래서 자동 구매 장수는 출력되지만 자동 번호는 출력되지 않고 있어요.

아무래도 4단계에서 기능을 구현하다가 막혀서 놓치게된 케이스 같은데, 기능을 구현한 뒤에 사용자 입력을 직접 실행하거나 테스트로 남겨두면 이런 문제를 조금 더 일찍 발견할 수 있다는 점 참고하세요!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수동과 자동 번호를 모두 출력할 수 있도록 출력 순서를 바꿨습니다!


List<Integer> wins = InputView.inputWinning();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

위의 수동 구매에서는 1, 2, 3, 4, 5, 6 처럼 공백이 들어가도 괜찮은데,
당첨 번호를 입력할 때는 반드시 1,2,3,4,5,6 같은 형식으로만 들어가야 하네요.

그렇지 않은 경우엔 이렇게 에러가 발생하는데, 어떻게 해결하면 좋을까요?

int bonusBall = InputView.inputBonusBall();
Comment on lines +27 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구매한 로또는 LottoNumberLotto를 통해 유효성을 보장하도록 개선하고 있는데, 당첨 번호는 여전히 List<Integer>로 전달되고 있네요.

당첨 번호 역시 1~45 범위의 중복 없는 번호 6개라는 점에서는 한 장의 Lotto와 같은 규칙을 가지고 있지 않을까요?
보너스 볼도 int 대신 LottoNumber로 표현할 수 있을 것 같아요.

이때 보너스 볼이 당첨 번호 6개 중 하나와 같아도 되는지도 함께 생각해보면 좋겠습니다.
당첨 번호와 보너스 볼을 함께 관리하면서 해당 규칙을 보장하는 객체가 필요할지도 고민해보면 어떨까요?

List<Integer> counts = lottoResult.calculateCounts(passiveLotto, wins, bonusBall);

int winPrice = winningRate.calculateWinPrice(counts);
double rate = winningRate.calculateRate(winPrice, purchasePrice);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 생성한 purchaseAmount가 아니라, 원시값인 purchasePrice를 전달하고 있네요~


ResultView.printResult(counts, rate);
}
}

57 changes: 57 additions & 0 deletions src/main/java/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Lotto {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금은 Lotto가 이름이랑은 다르게 한 장의 로또 번호를 가지는 게 아니고, 구입 장수 계산이랑 자동 번호 생성을 담당하고 있네요.

일급 컬렉션은 단순히 List를 클래스로 감싸는 것보다, 컬렉션 전체가 지켜야 하는 규칙을 한곳에서 보장하는 데 의미가 있는데요. 사실 지금 Lotto에서 사용하고 있는 List<Integer>는 아래 같은 경우도 모두 포함할 수 있거든요.

  • 번호가 5개 또는 7개인 로또
  • 같은 번호가 중복된 로또
  • 유효하지 않은 번호가 포함된 로또

한 장의 로또를 표현하는 LottoList<LottoNumber>를 가지고, 생성될 때 번호 개수와 중복 여부를 검증하도록 만들어보면 어떨까요?

그렇게 바꿨을 때 지금의 Lotto가 담당하고 있는 자동 번호 생성과 구입 장수 계산은 각각 어디에 위치하는 게 자연스러울지도 함께 고민해보면 좋을 것 같습니다~

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재는 로또가 계산 같은 기능을 하는데 로또 한 장의 상태를 나타내야 합니다. 일급 컬렉션은 하나의 객체가 그 컬렉션에 대한 규칙과 책임을 갖게 합니다.

기존의 Lotto에 있던 기능 메서드들은 LottoGenerator 클래스로 이동하였고, 그에 따라서 Application 클래스도 수정하였습니다.

Lotto 클래스에서 컬렉션이 규칙과 책임을 가지도록 private한 리스트를 만들었고, 생성자와 팩토리 메서드를 구현하였습니다.

번호가 5개 또는 7개인 로또 - 유효성 검증 메서드를 구현했습니다.

같은 번호가 중복된 로또 - 유효성 검증 메서드를 구현했습니다.

유효하지 않은 번호가 포함된 로또 - LottoNumber 클래스에서 이미 검증했다고 생각하여 따로 구현하지 않았습니다.

또한 LottoTest 클래스에서 각각을 테스트 하였습니다.

public int calculateCount(int price) {
return price / 1000;
}

private static List<Integer> lottoList() {
List<Integer> lotto = new ArrayList<>();

for (int i = 0; i < 45; i++) {
lotto.add(i+1);
}
return lotto;
}

private void lottoShuffle(List<Integer> lotto) {
Collections.shuffle(lotto);
}

private List<LottoNumber> lottoPick(List<Integer> lotto) {
List<LottoNumber> lottoSix = new ArrayList<>();
for (int i = 0; i < 6; i++) {
lottoSix.add(lotto.get(i));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LottoNumber를 적용하면서 반환 타입을 바꿔보셨군요.
그런데 지금 내부에서는 여전히 Integer를 추가하고 있어서 흐름이 깨지고 있네요.

LottoNumber객체가 실제로 생성되는 지점은 어디일까요?
현재는 생성자가 private이고 팩토리 메서드도 없어서 LottoNumber를 만들 방법이 없는 것 같네요!

로또를 만드는 방식과는 별개로 LottoNumber를 어디서 어떻게 생성하고 사용해야할지 생각해보면 좋을 것 같습니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

팩토리 메서드: 객체를 대신 만들어 주는 메서드

  • from은 다른 타입으로 변환해 객체를 생성
  • of는 주어진 값들로 객체 생성

Lotto에서는 숫자를 LottoNumber 객체로 변환하는 것이기 때문에 from을 사용하였고, Lotto 클래스도 수정하였습니다.

}

return lottoSix;
}

private void lottoSort(List<LottoNumber> lotto) {
Collections.sort(lotto);
}

public List<LottoNumber> run() {
List<Integer> lottoList = lottoList();

lottoShuffle(lottoList);
List<LottoNumber> lotto = lottoPick(lottoList);
lottoSort(lotto);

return lotto;
}

public List<List<LottoNumber>> lottoLists(int count) {
List<List<Integer>> lottos = new ArrayList<>();
for (int i = 0; i < count; i++) {
lottos.add(run());
}
return lottos;
}


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지인님, 인텔리제이 쓰고 계신가요?
윈도우를 쓰신다면 Ctrl + Alt + L를, 맥을 쓰고 계시면 Command + Option + L를 눌렀을 때 해당 파일에 자동 정렬이 되는데, 이런 공백도 없애보면 좋을 것 같습니다.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ctrl + Alt + L으로 정렬했습니다! 감사합니다.

}
13 changes: 13 additions & 0 deletions src/main/java/domain/LottoNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package domain;

public class LottoNumber {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원시값 포장은 단순히 int를 필드로 옮기는 건 아닙니다.
이후에 이 객체를 보는 것 만으로도 유효한 로또 번호라는 걸 믿고 쓸 수 있도록 하는 게 큰 목적인데요.

  • 0이나 46으로도 생성할 수 있는지
  • 값이 3인 두 객체를 같은 번호라고 판단할 수 있는지
  • 번호를 오름차순으로 정렬할 수 있는지
  • 외부에서 실제로 객체를 생성할 수 있는지

이 질문을 만족하도록 생성 방법, 검증, 값 비교, 정렬 기준을 하나씩 구현하고 테스트해보면 원시값 포장의 목적을 이해하는 데 도움이 될 것 같습니다~

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0이나 46으로 생성할 수 있는지 - LottoNumber 유효성 검사 메서드 생성 및 Test 코드 추가하였습니다. 테스트 하는 도중 타입 부분이 컴파일 오류가 떠서 오류가 나는 부분의 타입을 수정하였습니다. 타입을 수정하면서 번호 관리를 하는 원시값 포장에 대해서 이해했습니다!

값이 3인 두 객체를 같은 번호라고 할 수 있는지 - equals로 판단하여 테스트 코드 추가하였습니다. 유효성 검사할 때 수정하지 못했던 정렬 부분 Collections.sort() 메서드도 함께 수정하였습니다.
Collections.sort()에서 Integer는 이미 Comparable을 구현하고 있고, LottoNumber은 Comparable이 따로 필요하다는 걸 학습했습니다. 따라서 comparable 을 새로 생성하였습니다.

번호를 오름차순으로 정렬할 수 있는지 - 확인하는 테스트를 추가하였습니다.

또한 지금까지 커밋 메시지 작성 시 refactor:, test: 등을 적용하지 못했는데, 이번부터는 적용하여 작성하였습니다.

외부에서 실제로 객체를 생성할 수 있는지 - 외부에서 직접 생성자를 호출하면 유효성 검사를 거치지 않은 객체도 생성할 수 있다고 생각했습니다. 따라서 객체 생성은 팩토리 메서드를 통해서만 이루어지도록 생성자를 private으로 변경했습니다. 리뷰어 님의 요구에 대한 답이 제가 이해한 방향이 맞는지 궁금합니다.

답변에 대해 수정하다 보니 원시값 포장에 대해 이해할 수 있었던 것 같습니다. 아직 완벽하진 않지만 계속 미션 하면서 적용해 보겠습니다!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네, 생성자를 감추고 팩터리 메서드에서 검증한 뒤 생성하도록 이해하신 방향이 맞습니다 👍

from은 다른 형태의 값 하나를 변환하고, of는 여러 값을 조합한다는 Java의 대표적인 명명 관례도 잘 찾아보셨네요.
절대적인 규칙은 아니지만 현재 LottoNumber.from(int)Lotto.from(List<LottoNumber>)는 호출부에서도 의미를 이해하기 충분한 것 같습니다.

private final int number;

private LottoNumber(int number) {
this.number = number;
}

public int getNumber() {
return number;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금은 getNumber()를 사용하는 곳이 없는 것 같네요.

이후 출력 구조를 변경하면서 필요한 메서드인지 확인해보고, 사용되지 않는다면 외부에 공개할 필요가 있는지도 고민해보면 좋겠습니다~

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getNumber() 삭제하였습니다!

}
37 changes: 37 additions & 0 deletions src/main/java/domain/LottoResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package domain;

import java.util.ArrayList;
import java.util.List;

public class LottoResult {

private boolean containsWinningNumber(List<Integer> lottoList, int win) {
return lottoList.contains(win);
}

private int resultCounting(List<Integer> lottoList, int win, int count) {
if (containsWinningNumber(lottoList, win)) {
count++;
}
return count;
}

private int checkingWinningNumbers(List<Integer> lottoList, List<Integer> wins, int bonusBall) {
int count = 0;
for (int win : wins) {
count = resultCounting(lottoList, win, count);
}

if (count == 5 && containsWinningNumber(lottoList, bonusBall)) return 7;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count는 일치한 번호 개수라는 의미인데, 2등인 경우에는 실제 일치 개수가 아닌 7을 반환하고 있네요.

이후 코드는 7이 5개 일치와 보너스 볼 일치라는 사실을 모두 알고 있어야 합니다.
처음 보는 사람은 7개가 일치했다는 의미로 이해할 수도 있고요.

일치 개수와 보너스 볼 일치 여부를 전달해 enum이 당첨 결과를 판단하게 하면 어떨까요?
그러면 LottoResult의 결과도 List<Integer>가 아니라 의미를 가진 타입으로 표현할 수 있을 것 같습니다.
enum을 적용했는데 코드가 더 길어진다고 느꼈던 이유도, 지금 enum은 값만 제공하고 등수 판별 행동은 다른 클래스에 남아 있기 때문일 수 있어요~

이와 관련해서는 위에 남겨둔 enum 관련 피드백을 참고해주세요.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존에는 LottoResult에서 당첨 번호가 5개 일치하고 보너스 볼까지 일치한 경우 7을 반환하도록 구현했는데, 말씀해 주신 것처럼 7만으로는 어떤 당첨 결과를 의미하는지 알기 어렵다고 생각했습니다.

이를 수정하여 일치한 번호의 개수와 보너스 볼 일치 여부를 Rank에 전달하고, Rank.find()에서 해당 조건에 맞는 등수를 판단하도록 변경하였습니다.

return count;
}

public List<Integer> calculateCounts(List<List<Integer>> lottos, List<Integer> wins, int bonusBall) {
List<Integer> counts = new ArrayList<>();

for (List<Integer> lotto : lottos) {
counts.add(checkingWinningNumbers(lotto, wins, bonusBall));
}
return counts;
}
}
17 changes: 17 additions & 0 deletions src/main/java/domain/Purchase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package domain;

public class Purchase {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구입 금액이라는 원시값을 포장하려는 방향 자체는 괜찮지만,
지금은 생성자와 메서드 모두 private이고, 실제로는 어떤 역할도 수행하지 않는 상태네요.

Applicationint purchasePrice를 이 객체로 바꾼다고 생각했을 때 다음을 고민해보면 어떨까요?

  • 유효한 구입 금액은 어떤 값인가
  • 구입 가능한 로또 수는 누가 계산해야 하는가
  • 당첨 금액과 비교해 수익률을 계산할 때 원래 구입 금액은 누가 알고 있어야 하는가

이름도 구매 행위가 아니라 금액을 표현한다면 PurchaseAmount가 더 잘 어울릴 수 있을 것 같아요.
다만 먼저 LottoNumberLotto를 완성한 뒤에 건드려보는 게 좋을 듯 해요.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LottoNumber를 구현하면서 원시값 포장과 객체의 책임을 나누는 방향을 조금 이해해서, 비슷한 방식으로 PurchaseAmount도 구입 금액을 표현하는 객체로 변경해 보았습니다. 생성 시 유효한 구입 금액(1,000원 이상, 1,000원 단위)인지 검증하도록 구현하고, 로또 수를 계산하는 책임도 PurchaseAmount로 이동하였습니다.

다만 수익률 계산과 관련된 부분은 잘 모르겠습니다. 구입 금액도 PurchaseAmount가 알고 있어야 할 것 같다고 생각했는데, 수익률 계산까지 PurchaseAmount의 책임으로 두는 것이 맞는지 잘 모르겠습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PurchaseAmount가 구입 금액을 검증하고 구입 가능한 장수를 계산하도록 변경한 것 좋네요~

그런데 수익률은 구입 금액 하나만으로 계산할 수 있는 건 아니고, 구입 금액과 총 당첨금 사이의 관계로 만들어지는 값이죠.
그래서 PurchaseAmount가 직접 계산해야 한다고 보긴 어려울 것 같아요.

PurchaseAmount를 전달받아 계산하거나, 추후 별도 객체가 총 당첨금과 구입 금액을 전달받는 방법도 있을 것 같습니다.
아래에 피드백을 남겨 두겠지만, Application에서 다시 원시값인 purchasePrice를 전달하면 PurchaseAmount가 보장한 정보를 우회하게 되는데요.

우선 지금은 구입 금액을 사용하는 코드가 계속 int를 사용해도 괜찮은지 먼저 고민해보면 좋을 것 같습니다!

private final int price;

private Purchase(int price) {
this.price = price;
}

private int getLottoCount() {
return price / 1000;
}

private int getPrice() {
return price;
}
}
25 changes: 25 additions & 0 deletions src/main/java/domain/WinningPrize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package domain;

public enum WinningPrize {
THREE(3, 5000),
FOUR(4, 50000),
FIVE(5, 1500000),
BONUS(7, 30000000),
SIX(6, 2000000000);

private final int goal;
private final int prize;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이제 지인님이 처음에 남겨주셨던 enum에 대해 한번 살펴볼까요?

지금 enum의 이름은 WinningPrize이지만, 실제로 일치 개수나 당첨금뿐만 아니라 당첨 결과의 종류를 표현하고 있네요.
상수 이름도 THREE, FOUR, BONUS, SIX라서 일치 개수와 보너스 여부 의미가 섞여 있는 것 같습니다.

특히 BONUSgoal7은 처음 코드를 읽는 사람이 의미를 바로 파악하기 어려울 수 있을 것 같아요.
아마 실제 일치 개수가 아니라 2등을 나타내려고 지인님이 만들어 두신 값 같은데요.
이렇게 만들어진 7의 의미는 LottoResult, WinningRate, ResultView가 모두 알고 있어야 합니다.
만약 이 부분을 변경한다면 여러 코드를 함께 수정해야 할 수도 있겠죠?

그렇다면 WinningPrize가 왜 필요했고, 뭘 표현하기 위해 존재하는 enum인지 다시 한번 생각해보면 좋을 것 같습니다.

이 enum의 본질이 무엇일까

일치 개수, 보너스 볼 조건, 당첨금은 결국 로또의 당첨 결과를 판단하기 위한 정보죠.
사용자 입장에서도 “번호를 6개 맞혔다”보다 “1등에 당첨됐다"가 더 직관적이고 이해하기 쉬울 것 같은데요.

그렇다면 이 enum을 Rank처럼 당첨 등수를 표현하도록 바꿔보면 어떨까요?
각 등수는 일치 개수, 보너스 볼 일치 여부, 당첨금 정보를 가지면 되겠죠.

그럼 enum 상수의 이름도 일치 개수가 아니라 실제 등수로 표현할 수 있겠네요.
(ex. FIRST, SECOND, THIRD, FOURTH, FIFTH, MISS)

이젠 5개 일치라는 같은 조건에서도 보너스 여부에 따라 SECONDTHIRD로 구분할 수 있습니다.

  • SECOND: 번호 5개 일치 + 보너스 볼 일치
  • THIRD: 번호 5개 일치 + 보너스 볼 불일치

이렇게 실제 일치 개수와 보너스 볼 일치 여부를 Rank에 전달해 등수를 판단한다면, 현재 2등을 표현하기 위해 사용하고 있는 숫자 7도 제거할 수 있지 않을까요?

그리고 LottoResult7과 같은 숫자를 반환하는 대신 Rank.SECOND처럼 의미를 가진 결과를 반환한다면, WinningRate와 ResultView의 코드는 어떻게 달라질 수 있을지도 함께 고민해보면 좋겠습니다~

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WinningPrize를 Rank로 변경하고, FIRST, SECOND, THIRD, FOURTH, FIFTH, MISS로 당첨 결과를 직접 표현하도록 변경하였습니다. 또한 일치 개수와 보너스 볼 일치 여부를 통해 Rank를 판단하도록 하여 기존에 사용하던 7과 같은 숫자를 제거하였습니다.

이에 맞춰서 LottoResult가 List 대신 List를 반환하도록 변경하고, WinningRate에서는 각 Rank가 가지고 있는 당첨금을 이용해 총 당첨금을 계산하도록 수정하였습니다. ResultView 역시 숫자별 결과가 아닌 각 Rank의 개수를 세어 출력하도록 변경하였습니다.


WinningPrize(int goal, int prize) {
this.goal = goal;
this.prize = prize;
}

public int getGoal() {
return goal;
}

public int getPrize() {
return prize;
}
}
47 changes: 47 additions & 0 deletions src/main/java/domain/WinningRate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package domain;

import java.util.List;

public class WinningRate {

public int calculateWinPrice(List<Integer> counts) {
return threeWin(counts) + fourWin(counts) + fiveWin(counts) + secondWin(counts) + sixWin(counts);
}

private int threeWin(List<Integer> counts) {
return countGoal(counts, WinningPrize.THREE.getGoal()) * WinningPrize.THREE.getPrize();
}

private int fourWin(List<Integer> counts) {
return countGoal(counts, WinningPrize.FOUR.getGoal())
* WinningPrize.FOUR.getPrize();
}

private int fiveWin(List<Integer> counts) {
return countGoal(counts, WinningPrize.FIVE.getGoal())
* WinningPrize.FIVE.getPrize();
}

private int secondWin(List<Integer> counts) {
return countGoal(counts, WinningPrize.BONUS.getGoal())
* WinningPrize.BONUS.getPrize();
}

private int sixWin(List<Integer> counts) {
return countGoal(counts, WinningPrize.SIX.getGoal())
* WinningPrize.SIX.getPrize();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

이렇게 1등 당첨이 2개인데도 불구하고, 수익률이 -147483.65가 되어버렸네요~

1등 한 장의 당첨금 2,000,000,000은 int로 표현할 수 있지만, 1등 로또가 두 장이면 총 당첨금은 40억 원이 되어 int 범위를 넘어갑니다.
각각의 값은 정상적으로 저장된다해도 합산하는 과정에서 예상하지 못한 음수나 잘못된 결과가 만들어질 수 있는데요.
당첨금과 총 당첨금을 어떤 자료형으로 표현하면 좋을지 고민해보고, 1등이 두 장인 경우도 테스트해보면 어떨까요?


private int countGoal(List<Integer> counts, int goal) {
int count = 0;

for (int i = 0; i < counts.size(); i++) {
if (goal == counts.get(i)) count++;
}
return count;
}

public double calculateRate(int winPrice, int purchasePrice) {
return (double) winPrice / purchasePrice;
}
}
68 changes: 68 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package view;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class InputView {

private static Scanner scanner = new Scanner(System.in);

public static int inputPrice() {

System.out.println("구입금액을 입력해 주세요.");
int price = scanner.nextInt();
Comment on lines +15 to +16

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

이렇게 int 범위를 넘는 입력이 들어오면 어떻게 해야할까요?


return price;
}

public static List<Integer> inputWinning() {
scanner.nextLine();
System.out.println("\n지난 주 당첨 번호를 입력해 주세요.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\n은 LF 문자를 직접 사용해서 운영체제마다 개행 방식이 다를 수 있어요.

Java에서는 System.lineSeparator()printf()%n을 사용할 수 있는데요.
그렇지만 여기서는 안내 문구 전에 System.out.println()으로 빈 줄을 먼저 출력하면 의도도 더 명확하게 드러날 것 같습니다~

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System.out.println()으로 빈 줄 출력하였습니다!

String win = scanner.nextLine();

String[] wins = win.split(",");
List<Integer> nums = new ArrayList<>();

for (int i = 0; i < wins.length; i++) {
nums.add(Integer.parseInt(wins[i]));
} return nums;
}
public static int inputBonusBall() {
System.out.println("\n보너스 볼을 입력해 주세요.");
int bonusBall = scanner.nextInt();

return bonusBall;
}

public static int inputPassiveCount() {
System.out.println("\n수동으로 구매할 로또 수를 입력해 주세요.");
int count = scanner.nextInt();
return count;
}

public static List<List<Integer>> inputPassiveLotto(int manualCount) {
scanner.nextLine();

System.out.println("\n수동으로 구매할 번호를 입력해 주세요.");
List<List<Integer>> passiveLottos = new ArrayList<>();

for (int i = 0; i < manualCount; i++) {
passiveLottos.add(inputManualLotto());
}
return passiveLottos;
}

private static List<Integer> inputManualLotto() {
String input = scanner.nextLine();
String[] numbers = input.split(",");

List<Integer> lotto = new ArrayList<>();

for (String number : numbers) {
lotto.add(Integer.parseInt(number.trim()));
}

return lotto;
}
}
33 changes: 33 additions & 0 deletions src/main/java/view/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package view;

import java.util.List;

public class ResultView {

public static void printPurchase(List<List<Integer>> lottos, int passiveCount, int autoCount) {
System.out.println("수동으로 " + passiveCount + "장, 자동으로 " + autoCount + "개를 구매했습니다."
);
for (List<Integer> lotto : lottos) {
System.out.println(lotto);
}
}

public static void printResult(List<Integer> counts, double rate) {
System.out.println();
System.out.println("당첨 통계");
System.out.println("---------");

int[] result = new int[8];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

배열 대신 컬렉션을 사용한다.

프로그래밍 요구사항에는 배열 대신 컬렉션을 사용하도록 되어 있는데, 당첨 결과는 아직 배열로 집계하고 있네요.
여기서 더 중요한 부분은 View3, 4, 5, 6, 7이 각각 어떤 의미인지 알고 있어야 한다는 겁니다.

앞에 남겨둔 리뷰처럼 당첨 결과를 Rank로 표현한다면, View가 숫자 인덱스의 의미를 몰라도 출력할 수 있도록 만들 수 있지 않을까요?

enum을 키로 사용하기 적합한 EnumMap도 함께 알아보면 좋을 것 같습니다~


for (int count : counts) {
result[count]++;
}

System.out.println("3개 일치 (5,000원) - " + result[3] + "개");
System.out.println("4개 일치 (50,000원) - " + result[4] + "개");
System.out.println("5개 일치 (1,500,000원) - " + result[5] + "개");
System.out.println("5개 일치, 보너스 볼 일치 (30,000,000원) - " + result[7] + "개");
System.out.println("6개 일치 (2,000,000,000원) - " + result[6] + "개");
System.out.printf("총 수익률은 %.2f입니다.\n", rate);
}
}