Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ce89c99
docs : 3, 4단계 명세 작성
leechan7120 Apr 9, 2026
4dae662
feat : 보너스 볼 로직 추가
leechan7120 Apr 9, 2026
f70351f
feat : 수동 구매 로직 추가
leechan7120 Apr 9, 2026
d173524
feat : Enum 수정, Bonus 생성
leechan7120 Apr 9, 2026
f5606e2
fix : 구조에 맞게 main 수정
leechan7120 Apr 9, 2026
dbf845c
fix : 신규 test 작성 및 일부 수정
leechan7120 Apr 9, 2026
cb6c1fc
fix : test case 추가
leechan7120 Apr 9, 2026
dd7db01
fix : Enum 선출 간단하게 수정
leechan7120 Apr 10, 2026
d300697
fix : 개행 추가
leechan7120 Apr 10, 2026
6720eb7
feat : 1000원 이하 자동 종료
leechan7120 Apr 10, 2026
a2b1dc0
feat : bonusball 숫자 아니면 무한반복
leechan7120 Apr 10, 2026
3f87503
feat : bonusNumber LottoNumber로 포장
leechan7120 Apr 10, 2026
85da545
feat : bonusNumber LottoNumber로 포장
leechan7120 Apr 10, 2026
c971249
feat : contains 변경
leechan7120 Apr 10, 2026
d9570cf
feat : 보너스볼이 당첨 번호와 같을 경우 무한 반복
leechan7120 Apr 10, 2026
9cbfaa9
feat : 테스트 2개 추가
leechan7120 Apr 10, 2026
f1782dc
fix : stream 수정
leechan7120 Apr 12, 2026
bd9c1dd
fix : 가격 입력 반복 코드 작성
leechan7120 Apr 12, 2026
aef918e
fix : scanner 이름 변경
leechan7120 Apr 12, 2026
118535d
fix : 메소드 위치 재배치
leechan7120 Apr 12, 2026
bcc4b08
fix : print 변경
leechan7120 Apr 12, 2026
28426d4
fix : 3항 연산자 수정
leechan7120 Apr 12, 2026
29be74a
fix : LottoResult에 비교 책임 추가
leechan7120 Apr 12, 2026
db39c88
fix : test 추가
leechan7120 Apr 12, 2026
e4a22a5
fix : input 로직 분리
leechan7120 Apr 18, 2026
e5417aa
fix : main 메소드 분리
leechan7120 Apr 18, 2026
1f8071b
fix : Test 수정
leechan7120 Apr 18, 2026
fe80899
fix : 오류 수정
leechan7120 Apr 18, 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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,13 @@
* 로또별로 로또 당첨된 개수를 파악한다.
* 일치하는 당첨개수별로 수를 센다.
* 수익률을 계산한다.

### 로또 2등 당첨

* 2등을 위한 보너스볼을 추첨한다.
* 당첨 통계에 2등을 추가한다.

### 로또 수동 구매

* 사용자가 수동으로 추첨 번호를 입력할 수 있도록 해야 한다.
* 입력한 금액, 자동 생성 숫자, 수동 생성 번호를 입력하도록 해야 한다.
12 changes: 8 additions & 4 deletions src/main/java/lotto/LottoDraw.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

public class LottoDraw {
private final Lotto drawnLotto;
private final int bonusNumber;
private final LottoReceipt receipt;
private final MatchingCounts counts;

public LottoDraw(Lotto drawnLotto, LottoReceipt receipt) {
public LottoDraw(Lotto drawnLotto, int bonusNumber, LottoReceipt receipt) {
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 bonusNumber 원시값 포장을 할 수 있는 객체가 충분히 있어 보이는데요? 🤔

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를 만들어놓고 쓰지 않았을까요.. 수정했습니다.

this.drawnLotto = drawnLotto;
this.bonusNumber = bonusNumber;
this.receipt = receipt;

this.counts = getNumberCount();
}

Expand All @@ -32,7 +33,10 @@ private LottoResult getLottoResult(Lotto lottoRow) {

int matchingCount = numbers.size();

return LottoResult.valueOf(matchingCount);
boolean matchBonus = lottoRow.numbers().stream()
.anyMatch(num -> num.number() == bonusNumber);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

bonusNumber 원시값 포장하게 된다면 이 부분도 개선할 수 있어요. (Hint. contains)

  1. 어떻게 개선할 수 있을지?
  2. 개선한 코드가 되는 이유를 작성해보세요!

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.

boolean matchBonus = lottoRow.numbers().contains(bonusNumber); 로 바꿔봤습니다.

lottoRow.numbers()는 List를 반환할 것이고, List에 특정 숫자가 포함되어 있는지를 확인하는 contains 메소드를 활용해서 가독성 측면에서 더욱 간결하게 작성할 수 있을 것 같습니다.

조금 더 찾아보니, anymatch와 contains는 둘 다 선형으로 탐색하다 탐색을 성공하면 바로 return을 하는 것은 똑같지만,
anymatch는 내부에서 함수 호출 (위에서는 lambda식)을 진행하기에 contains에 비해 약간의 비용이 더 발생하고, stream()을 추가로 호출하기 때문에 이에 대한 비용도 추가됩니다. 다만 조건식을 활용하기에 이에 대한 범용성은 더 높을 수 있지만, 단순 비교에 그친 지금의 경우에는 해당 사항이 없습니다.

contains는 함수를 호출하지 않으니 비용(시간) 면에서 약간 더 빠를 수 있고, equals 메소드를 오버라이딩해서 사용하면 값을 직접 꺼내오지 않고 다양한 연산을 활용할 수 있기에 더욱 개선된다고 볼 수 있을 것 같습니다.


return LottoResult.valueOf(matchingCount, matchBonus);
}

public int getCount(LottoResult result) {
Expand All @@ -43,4 +47,4 @@ public float getRateOfReturn() {
int sumOfReturn = counts.getSumOfReturn();
return (float) sumOfReturn / receipt.totalPrice();
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

여기도 마지막 줄 개행,, 자동 정렬 꼭 적용해보기! ☺️

33 changes: 28 additions & 5 deletions src/main/java/lotto/LottoPurchase.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,53 @@
package lotto;

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

public class LottoPurchase {
private static final int LOTTO_PRICE = 1000;

private final Lottos lottos;
private final int totalPrice;
private final int change;
private final int manualCount;

public LottoPurchase(int totalPrice, LottoMaker lottoMaker) {
public LottoPurchase(int totalPrice, List<Lotto> manualLottos, LottoMaker lottoMaker) {
this.totalPrice = totalPrice;
this.change = totalPrice % LOTTO_PRICE;
this.manualCount = manualLottos.size();

int totalTicketCount = totalPrice / LOTTO_PRICE;
int autoCount = totalTicketCount - manualCount;

int numberOfLotto = this.totalPrice / LOTTO_PRICE;
change = totalPrice % LOTTO_PRICE;
if (autoCount < 0) {
throw new IllegalArgumentException("구입 금액보다 많은 수동 로또를 선택하셨습니다.");
}

this.lottos = Lottos.from(numberOfLotto, lottoMaker);
List<Lotto> combinedLottos = new ArrayList<>(manualLottos);
for (int i = 0; i < autoCount; i++) {
combinedLottos.add(lottoMaker.makeLotto());
}

this.lottos = new Lottos(combinedLottos);
}

public LottoReceipt printReceipt() {
return new LottoReceipt(lottos, totalPrice);
}

public int getManualCount() {
return manualCount;
}

public int getAutoCount() {
return getNumberOfLotto() - manualCount;
}

public int getNumberOfLotto() {
return lottos.size();
}

public int getChange() {
return change;
}
}
}
30 changes: 20 additions & 10 deletions src/main/java/lotto/LottoResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum LottoResult {
THREE(3, 5_000),
FOUR(4, 50_000),
FIVE(5, 1_500_000),
BONUS(5, 30_000_000),
SIX(6, 2_000_000_000);

private final int matchingCount;
Expand All @@ -15,20 +16,29 @@ public enum LottoResult {
this.reward = reward;
}

public static LottoResult valueOf(int count) {
for (LottoResult result : values()) {
if (result.matchingCount == count) {
return result;
}
public static LottoResult valueOf(int matchingCount, boolean matchBonus) {
if (matchingCount == 6) {
return SIX;
}
// 3개 미만은 모두 NONE으로 처리
if (count >= 0 && count < 3) {
if (matchingCount == 5 && matchBonus) {
return BONUS;
}
if (matchingCount == 5) {
return FIVE;
}
if (matchingCount == 4) {
return FOUR;
}
if (matchingCount == 3) {
return THREE;
}
if (matchingCount < 3 && matchingCount >= 0) {
return NONE;
}
throw new IllegalArgumentException("유효하지 않은 당첨 개수입니다: " + count);
throw new IllegalArgumentException("유효하지 않은 당첨 개수입니다: " + matchingCount);
}
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 으로 옮겼지만 요구 사항의 "함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다." 준수해 볼까요?

이 부분 리뷰가 3번째 이긴한데, 어렵게 느껴지시면 DM 주셔도 좋아요! 🙂 대신!! 충분히 고민 후!! 왜냐면 이미 사용한 곳이 있어서 고민하시면 풀리실 거 같아서요! ☺️

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.

이전에 썼던 게 for문을 활용한 반복문이었던 것 같은데, code depth가 2 이상이면 안된다는 요구 사항도 있어서 이를 지키려다 자꾸 저런 식으로 짜네요..!

또 다른 방법이 있을까요? Enum 자체 값 mapping을 1, 2도 만들어서 사용하는 방법도 있을 것 같긴 한데, 어차피 결과가 똑같은 걸 중복해서 생성하니 괜히 더 복잡해지는 게 아닐지.. 싶습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

code depth가 '2' 넘지 말라는 요구 사항이 왜 있었을까요?! 🤔 그걸 해결하기 위한 방법이 무엇이 있을 지 생각해보면, 메서드 분리가 좋은 방향이 될 수 있습니다. 그 뿐만 아니라 다른 방식으로도 개선해볼 수 있죠!

public static LottoResult of(int count, boolean matchBonus) {
      if (count == 5 && matchBonus) {
          return BONUS;
      }

      return Arrays.stream(values())
              .filter(result -> result != BONUS && result != NONE)
              .filter(result -> result.matchingCount == count)
              .findFirst()
              .orElse(NONE);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stream 활용을 제시해드린 이유는 다른 코드에서 이미 Stream 사용한 사례가 있어서 이 질문도 했었고, 예시를 전달드렸습니다! ☺️

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.

stream에 대한 것을 좀 찾아보니 되게 많은 상황에서 사용될 수 있겠네요..! stream이 사실 mapping이나 그런 쪽에서만 활용될 수 있을 줄 알았는데 문서를 좀 더 찾아보니까 제가 생각한 것보다 훨씬 많은 방향에 활용될 수 있네요.

메서드 분리에 대한 건 생각을 안 해본 건 아닌데, 그럼 지금 존재하는 메소드의 역할이 좀 애매해지는 것 같아서 그 요구사항 때문에 괜히 객체를 하나 더 생성하는 게 아닌가 싶은 생각이 들었습니다. 제공해주신 예시가 전 가장 좋아보이네요.


public int getReward(){
public int getReward() {
return reward;
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

지난 리뷰때 소개해 드렸던, 자동 정렬을 사용하면 마지막 줄 개행을 자동 추가해줘요! ☺️

35 changes: 26 additions & 9 deletions src/main/java/lotto/Main.java
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 접근 제어자로 설정된 메서드도 순서가 중요할 수 있어요! 찬형님이 다른 사람의 코드를 보았을 때, 어떤식으로 순서가 정렬되어 있다면 보기 편할지? 상상해볼까요?! ☺️

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.

메서드가 main의 실행 로직 순서대로 배치되어 있으면 읽을 때 조금 더 빠르게 확인할 수 있을 것 같네요. 변수나 메소드나 중요한 것을 위에 두고, 아래로 갈수록 중요도가 떨어지는 것을 배치하라고 했는데, 지금은 거의 다 필수 로직에 해당하는 것 같아서 프로그램 동작 흐름별로 배치해 봤습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

main 클래스가 100줄이 넘어갔습니다. 현재 로또의 요구 사항에 비해 상당한 코드 라인이 존재해요! 어떤 부분을 분리해볼 수 있을까요? 🤔

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.

main의 메소드들을 입력, 출력, 처리를 담당하는 객체로 따로 분리해서 각각을 호출할 수 있게끔 하면 좋을 것 같습니다. 기존의 메소드는 일부 public으로 변경되겠지만, 가독성이나 책임 측면에서 훨씬 좋을 것 같긴 합니다.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package lotto;

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

Expand All @@ -9,7 +10,7 @@ public class Main {

public static void main(String[] args) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.

요구 사항을 지켜볼까요?! 상당히 줄이 길면 비즈니스 흐름을 파악하기 어려워요!! 😭

Scanner scanner = new Scanner(System.in);
int totalPrice;
int totalPrice, numberOfManual;

System.out.println("구입금액을 입력해 주세요. (ex. 1000) (숫자가 아닌 경우 0으로 간주)");

Expand All @@ -21,7 +22,22 @@ public static void main(String[] args) {

System.out.println();

LottoPurchase purchase = purchaseAndPrintLotto(totalPrice);
System.out.println("수동으로 구매할 로또 수를 입력해 주세요.");
numberOfManual = Integer.parseInt(scanner.nextLine());

System.out.println();

System.out.println("수동으로 구매할 번호를 입력해 주세요.");
List<Lotto> manualLottos = new ArrayList<>();
for (int i = 0; i < numberOfManual; i++) {
manualLottos.add(LOTTO_PARSER.parse(scanner.nextLine()));
}

LottoPurchase purchase = new LottoPurchase(totalPrice, manualLottos, LOTTO_MAKER);

System.out.println("\n수동으로 " + numberOfManual + "장, 자동으로 " +
(purchase.getNumberOfLotto() - numberOfManual) + "개를 구매했습니다.");

LottoReceipt receipt = purchase.printReceipt();
displayReceipt(receipt);
displayChange(purchase.getChange());
Expand All @@ -30,16 +46,14 @@ public static void main(String[] args) {
System.out.println("지난주 당첨 번호를 입력해 주세요.");
Lotto drawnLotto = LOTTO_PARSER.parse(scanner.nextLine());

LottoDraw draw = new LottoDraw(drawnLotto, receipt);
System.out.println("보너스 볼을 입력해 주세요.");
int bonusNumber = Integer.parseInt(scanner.nextLine());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

NumberFormatException 이전과 동일하게 에러가 발생하지 않을까요?! 🤔

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.

숫자를 넣을때까지 시도하게 만들어봤습니다..!


LottoDraw draw = new LottoDraw(drawnLotto, bonusNumber, receipt);
System.out.println();
displayResult(draw);
}

private static LottoPurchase purchaseAndPrintLotto(int totalPrice) {
LottoPurchase purchase = new LottoPurchase(totalPrice, LOTTO_MAKER);
System.out.println(purchase.getNumberOfLotto() + "개를 구매했습니다.");
return purchase;
}

private static void displayReceipt(LottoReceipt receipt) {
for (Lotto lotto : receipt.lottos().getLottos()) {
Expand All @@ -63,8 +77,11 @@ private static void displayResult(LottoDraw draw) {
System.out.println("3개 일치 (" + LottoResult.THREE.getReward() + "원)- " + draw.getCount(LottoResult.THREE));
System.out.println("4개 일치 (" + LottoResult.FOUR.getReward() + "원)- " + draw.getCount(LottoResult.FOUR));
System.out.println("5개 일치 (" + LottoResult.FIVE.getReward() + "원)- " + draw.getCount(LottoResult.FIVE));
System.out.println(
"5개 일치, 보너스 볼 일치 (" + LottoResult.BONUS.getReward() + "원)- " + draw.getCount(LottoResult.BONUS));
System.out.println("6개 일치 (" + LottoResult.SIX.getReward() + "원)- " + draw.getCount(LottoResult.SIX));

System.out.println("총 수익률은 " + draw.getRateOfReturn() + "입니다.");
}
}

}
Loading