Skip to content
Open
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
12bedd7
feat: 기본 구조 만들기
juhee0223 Jul 31, 2026
601eb71
feat: Lotto 클래스
juhee0223 Aug 1, 2026
7d6e0c4
feat: 로또 구입금액 입력받아서 수량 출력
juhee0223 Aug 1, 2026
5bc1190
feat: 1단계 로또자동구매 완료
juhee0223 Aug 1, 2026
809289d
chore: 출력형식 준수 - 줄바꿈 추가
juhee0223 Aug 1, 2026
8a8fe2b
feat: 당첨번호 및 당첨통계 기능 구현
juhee0223 Aug 1, 2026
ba04b5d
feat: 각 매칭개수별 enum 만들기
juhee0223 Aug 2, 2026
2746792
feat: 당첨금이 없는 타입일때 처리
juhee0223 Aug 2, 2026
bc789de
feat: 보너스 번호 입력받는 기능 및 LottoChecker 생성자 수정
juhee0223 Aug 2, 2026
7298be7
chore: 오타수정
juhee0223 Aug 2, 2026
ef22e91
chore: 불필요한 주석 지우기
juhee0223 Aug 2, 2026
c7b2252
feat: enum 수정 및 등수별 설명 추가
juhee0223 Aug 2, 2026
07dfdff
chore: 타입 올바르게 수정
juhee0223 Aug 2, 2026
abc19c4
chore: enum 파일 이름 수정
juhee0223 Aug 2, 2026
de6a9bd
fix
juhee0223 Aug 2, 2026
7aa9f9f
feat: enum을 활용한 LottoStatistics 구현
juhee0223 Aug 2, 2026
af23a2a
chore: 3단계 완료 - 불필요한 임포트 삭제 및 주석 지우기
juhee0223 Aug 2, 2026
b59ec31
refactor: scanner close추가
juhee0223 Aug 2, 2026
be8d61a
fix: scanner close 코드 오타로 인한 스택오버플로우 수정
juhee0223 Aug 2, 2026
c86563a
feat: 사용자가 직접 입력한번호 받는 로또 생성자 추가
juhee0223 Aug 2, 2026
922eb10
fix: 금액 계산 오류 수정
juhee0223 Aug 2, 2026
047f5fd
feat: 수동구매 번호 입력받는 기능
juhee0223 Aug 2, 2026
d569fd4
feat: 수동 자동 장수 구분해서 출력하기
juhee0223 Aug 2, 2026
4095f4f
feat: 수동 입력번호를 받아와서 LottoTickets에서 처리
juhee0223 Aug 2, 2026
1dd8f86
refactor: 함수명 변수명 수정
juhee0223 Aug 2, 2026
11d0438
refactor: 4단계 수동입력 main 실행 되도록 수정
juhee0223 Aug 2, 2026
f8fdd96
feat: 입력에 대한 예외처리 추가
juhee0223 Aug 2, 2026
e9513b6
docs: readme 추가
juhee0223 Aug 2, 2026
2096b3c
refactor: 로또번호개수 - 변경범위 줄이기
juhee0223 Aug 3, 2026
7aa2362
refactor: 로또번호 상한 하한 상수화 - 매직넘버 제거
juhee0223 Aug 3, 2026
793fefe
docs: 기능 요구사항 중심으로 readme 수정
juhee0223 Aug 3, 2026
0bb6ac2
docs: 기능 요구사항 중심으로 readme 수정
juhee0223 Aug 3, 2026
cd808b3
refactor: 생성자 내부 호출 순서대로 메서드 선언 및 접근제어 큰 범위부터 메서드 배치
juhee0223 Aug 3, 2026
8a34a79
refactor: 생성자 내부 호출 순서대로 메서드 선언 및 접근제어 큰 범위부터 메서드 배치
juhee0223 Aug 3, 2026
aa09b3f
refactor: 상수 클래스간 중복선언 제거 및 public 상수 활용
juhee0223 Aug 3, 2026
6322c11
refactor: 로또가격상수 클래스간 중복선언 제거 및 public 상수 활용
juhee0223 Aug 3, 2026
5b11507
test: Lotto 클래스 단위테스트
juhee0223 Aug 3, 2026
daddef8
feat: Lotto 중복금지관련 예외추가
juhee0223 Aug 3, 2026
f8053a0
feat: 금액입력시 1000원이상의 1000원단위가 아니라면 재입력 받도록 수정
juhee0223 Aug 4, 2026
576427f
test: LottoTicketCount 단위테스트
juhee0223 Aug 4, 2026
1dd8354
feat: 구입금액 1000원 이상의 1000원단위로 제한
juhee0223 Aug 4, 2026
0db8025
test: LottoTicketCount 클래스 단위테스트
juhee0223 Aug 4, 2026
d574ef3
chore: 불필요한 주석 제거
juhee0223 Aug 4, 2026
82d8d69
test: LottoStatistics 단위테스트
juhee0223 Aug 4, 2026
d6361ea
test: Statistics와 Tickets와 Enum 테스트
juhee0223 Aug 4, 2026
3b6dd3f
feat: 로또번호 입력시 공백 개수와 상관없이 올바르게 입력처리 되도록 수정
juhee0223 Aug 4, 2026
954fb8b
feat: 사용자 입력 예외터지면 재입력받도록 수정
juhee0223 Aug 4, 2026
b6546b2
refactor: InputView public메서드부터 배치하도록 수정
juhee0223 Aug 4, 2026
8616304
refactor: 상태가 필요없는 클래스를 유틸리티클래스로 변경
juhee0223 Aug 5, 2026
423146b
refactor: Lotto 생성자 내부에서 검증추가
juhee0223 Aug 5, 2026
0aca2e0
refactor: 로또구매금액과 실제 로또발급수량 간의 상한 보장
juhee0223 Aug 5, 2026
d19037c
refactor: 컬렉션 사용
juhee0223 Aug 5, 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
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 로또 (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.

README 작성 잊지 않고 잘 해주셨네요 ~ 👍


## 프로젝트 소개

이 프로그램은 주어진 기능 및 프로그래밍 요구사항을 만족하는 콘솔 기반의 로또 게임을 구현하는 것을 목표로 합니다.
사용자는 로또를 수동 또는 자동으로 구매하고, 당첨 번호와 비교하여 당첨금 관련 결과를 확인할 수 있습니다.

---

## 기능 요구사항

- 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또 티켓을 발급합니다.
- 사용자가 수동으로 로또 번호를 입력할 수 있어야 합니다.
- 수동으로 구매할 로또 수를 입력받고, 그 수만큼 로또 번호를 입력받습니다.
- 수동 구매 후 남은 금액만큼 자동으로 로또를 발급합니다.
- 지난 주 당첨 번호 6개와 보너스 볼 1개를 입력받습니다.
- 로또 번호와 당첨 번호를 비교하여 당첨 결과를 결정합니다.
- 최종적으로 당첨 통계와 수익률을 계산하여 출력합니다.
- 수익률이 1 미만일 경우 손해임을 명시합니다.
- 수익률이 1 이상일 경우 이득임을 명시합니다. (임의 설정)

---

## 비즈니스 규칙

- **로또 구매 규칙**
- 로또 1장의 가격은 1,000원입니다.
- 구매 금액은 1,000원 단위로 입력해야 합니다.
- **로또 번호 규칙**
- 로또 번호는 1부터 45 사이의 숫자입니다.
- 로또 한 장은 중복되지 않는 6개의 숫자로 구성됩니다.
- 수동으로 로또를 구매할 때 6개의 번호를 입력하지 않으면 오류가 발생합니다.
- **당첨 조건 및 상금**
- 1등: 6개 번호 일치 (2,000,000,000원)
- 2등: 5개 번호 일치 + 보너스 볼 일치 (30,000,000원)
- 3등: 5개 번호 일치 (1,500,000원)
- 4등: 4개 번호 일치 (50,000원)
- 5등: 3개 번호 일치 (5,000원)

---

## 프로그래밍 요구사항

- **코드 컨벤션**: 자바 코드 컨벤션을 지키면서 프로그래밍합니다.
- **들여쓰기**: `indent`(인덴트, 들여쓰기) depth를 1까지만 허용합니다. (메서드 분리로 해결)
- **`else` 사용 금지**: `else` 예약어, `switch/case`, 3항 연산자를 사용하지 않습니다. (if문에서 값 반환으로 해결)
- **메서드 분리**:
- 함수(또는 메서드)의 길이는 10라인을 넘어가지 않도록 구현합니다.
- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만듭니다.
- **자료구조**: 배열이 아닌 컬렉션을 사용합니다.
- **축약 금지**: 변수명, 클래스명, 메서드명 등에 축약을 사용하지 않습니다.
51 changes: 51 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import static view.InputView.lottoScanner;

import domain.LottoChecker;
import domain.LottoResult;
import domain.LottoStatistics;
import domain.LottoTicketCount;
import domain.LottoTickets;
import domain.LottoWinningType;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import view.InputView;
import view.OutputView;

public class Application {

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 Image

사용자 입장으로 볼 때 어떠신가요?

6개로 입력을 잘 했고, 콤마로 구분 잘 한 것 같아요.
그리고 로또 구입금액이 얼마인지 모르겠어요. 1000원이지만 100원을 입력해도 정상 진행이 되네요.

1000원 미만으로 구매하면 안내 문구를 띄워주면 좋을 것 같아요

자동차 경주 미션의 핵심 키워드는 예외 처리였던 것 같아요.
사용자가 잘못 입력할 수 있는 상황을 미리 고려하고, 이를 적절히 방어하는 것은 중요한 개발 역량이라고 생각합니다.
위의 경우 이외에도 다양한 예외 상황이 있을 수 있겠죠?

아무리 코드를 잘 작성했더라도, 실제 사용하는 입장에서 불편함이 생긴다면 그 코드가 충분한 가치를 제공한다고 보기 어려울 수 있어요.

@juhee0223 juhee0223 Aug 3, 2026

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.

1. 생각해보니 아예 1000원 단위로 구매하도록 수정해볼까 합니다!

private static void validatePurchaseAmount(int price) {
        if (price < PRICE_PER_ONE_LOTTO_TICKET) {
            throw new IllegalArgumentException("구입 금액은 " + PRICE_PER_ONE_LOTTO_TICKET + "원 이상이어야 합니다.");
        }
        if (price % PRICE_PER_ONE_LOTTO_TICKET != 0) {
            throw new IllegalArgumentException("구입 금액은 " + PRICE_PER_ONE_LOTTO_TICKET + "원 단위로 입력해야 합니다.");
        }
    }

2. [1,2,3,4,5,6] 과 같은 형식일 경우도 처리 가능하도록 구현해보는 방향을 떠올려봤습니다

InputView.inputWinningLottoNumbers().split(", ") 이 부분을
InputView.inputWinningLottoNumbers().split(",\\s*")로 수정하여 콤마 뒤에 공백이 있든 없든 유연하게 파싱하도록 변경해두었습니다

이는 쉼표 뒤에 0개 이상의 공백(\s*)이 올 때 이를 기준으로 문자열을 쪼개주는 방식으로, 지금 간단하게 적용하기에 좋은 방법같아서 이렇게 해보았습니다.

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 Image

저는 부자가 되었어요 👍 😄

수동 구매 개수에 상한 검증이 빠져 있는 것 같아요.
2000원(2장)을 넣고 수동 5장을 입력하면 "자동으로 -3개를 구매했습니다"가 출력되면서
5장이 전부 발급됩니다. 구입 금액보다 많은 로또를 받게 되는 상황이에요.

원인을 발견하고 수정해볼까요?

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.

기존은 수동 구매 개수를 사용자로부터 입력받을 때, 해당 입력값이 총 구매 금액으로 구매 가능한 로또의 총 개수(totalCount)를 초과할 수 있는지에 대한 검증이 없었습니다.
이 때문에 autoCount = totalCount - manualCount 계산 시 manualCount가 totalCount보다 커져서 autoCount가 음수가 되는게 가능했던 것입니다.

그래서 수동 구매 개수를 입력받는 시점에 총 구매 가능 개수를 초과하는지 검증하는 로직을 추가했습니다.
만약 입력값이 구매 가능한 총량을 넘어서면, 사용자에게 오류 메시지를 보여주고 재입력을 요청하도록 에러처리를 해두었습니다.
그리고 이와 동시에 수동 및 자동 구매 개수가 항상 0 이상임을 보장하는 방향으로 수정했습니다.


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라인을 넘어가지 않도록 구현한다.

main 메서드에도 동일하게 프로그래밍 요구사항을 지켜주세요

LottoTicketCount lottoTicketCount = new LottoTicketCount();
int totalCount = lottoTicketCount.convertLottoPriceToTicketCount(InputView.inputLottoTotalPrice());

int manualCount = InputView.inputUserSelectedLottoCount();
List<String> userSelectedNumbersInput = InputView.inputUserSelectedLottoNumbers(manualCount);

int autoCount = totalCount - manualCount;

OutputView.printLottoCount(manualCount, autoCount);

LottoTickets lottoTickets = new LottoTickets();
lottoTickets.addUserSelectedLottos(userSelectedNumbersInput);
lottoTickets.addAutoLottos(autoCount);

OutputView.printLottoNumbers(lottoTickets);

String[] winningNumbers = InputView.inputWinningLottoNumbers().split(",\\s*");
String bonusNumber = InputView.inputBonusBallNumber();

LottoChecker lottoChecker = new LottoChecker(winningNumbers, lottoTickets, bonusNumber);
ArrayList<LottoWinningType> checkedTickets = lottoChecker.checkAllTickets();

LottoStatistics lottoStatistics = new LottoStatistics();
Map<LottoWinningType, Integer> countedMatches = lottoStatistics.countMatches(checkedTickets);

OutputView.printMatchCount(countedMatches);

LottoResult lottoResult = new LottoResult(lottoStatistics, (totalCount * LottoTicketCount.PRICE_PER_ONE_LOTTO_TICKET));
OutputView.printRateOfReturn(lottoResult.calculateProfitRate());

InputView.closeScanner(lottoScanner);
}
}
40 changes: 40 additions & 0 deletions src/main/java/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package domain;

import java.util.List;
import java.util.Random;
import java.util.TreeSet;

public class Lotto {
public static final int LOTTO_NUMBER_LOWER_BOUND = 1;
public static final int LOTTO_NUMBER_BOUND = 45;
public static final int LOTTO_NUMBER_COUNT = 6;
Comment on lines +9 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

해당 클래스에서만 사용되는데, 접근 제어자를 public으로 하신 이유가 있을까요?

@juhee0223 juhee0223 Aug 3, 2026

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.

public을 해서 다른 클래스에서 사용할수있도록 하려고 했었는데, 제가 그렇게 안썼더라구요....
동일한 상수선언을 LottoChecker에 중복으로 선언해서 사용했었던것을 삭제하고,
Lotto.LOTTO_NUMBER_BOUND 형태로 사용하도록 수정했습니다.
따라서 LOTTO_NUMBER_BOUND에 대해서는 public으로 그대로 두었습니다.

image
    public static final int LOTTO_NUMBER_LOWER_BOUND = 1;
    public static final int LOTTO_NUMBER_BOUND = 45;
    private static final int LOTTO_NUMBER_COUNT = 6;

다만 LOTTO_NUMBER_COUNT 는 해당 클래스에서만 사용하기때문에 private으로 고쳐두었습니다


TreeSet<Integer> randomNumberSet = new TreeSet<>();
Random random = new Random();

public Lotto() {
setLottoNumber();
}

public Lotto(List<Integer> userSelectedNumbers) {
if (userSelectedNumbers.size() != LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("로또 번호는" + LOTTO_NUMBER_COUNT + "개여야 합니다.");
}
this.randomNumberSet.addAll(userSelectedNumbers);
if (this.randomNumberSet.size() != LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
}

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.of(-1, 2, 3, 4, 5, 50)처럼 로또 번호 범위를 벗어난 값을 전달해도 Lotto 객체가 생성될 수 있습니다.

Lotto는 항상 유효한 로또 번호만 가지도록 1~45 범위 검증도 생성자 내부에서 함께 수행하는 것이 좋겠네요.

다른 개발자가 로또를 이렇게 생성한다면?

List<Integer> lottoNumbers = List.of(-1, 2, 3, 4, 5, 50);
Lotto lotto = new Lotto(lottoNumbers);

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.

자동로또 생성자를 만들 당시에는 어차피 주어진 범위내에서 랜덤번호가 만들어졌다보니 로또번호 범위검증을 안넣었습니다.
그래서 수동로또를 만들때 미처 그부분을 고려하지 못했는데 아주 당연한 부분을 빼먹은듯합니다...ㅎ

수동로또 생성자에 번호 범위 검증을 추가했습니다!


public TreeSet<Integer> getRandomNumberSet() {
return this.randomNumberSet;
}

private void setLottoNumber(){
while (randomNumberSet.size() < LOTTO_NUMBER_COUNT) {
randomNumberSet.add(random.nextInt(LOTTO_NUMBER_LOWER_BOUND, LOTTO_NUMBER_BOUND + 1));
}

}

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Collectors;

public class LottoChecker {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

생성자에 메서드를 호출하는 순서와 실제 메서드 선언 순서가 다릅니다.
생성자 내부에서는 wrappingToIntegerLottoNumbers()를 먼저 호출하지만, 메서드 선언은 validateBonusNumber()가 먼저 나오고 있어요.
사용하는 순서에 맞춰 메서드 선언 순서를 정리하면 코드 흐름을 따라가기 더 쉬울 것 같습니다~

    public LottoChecker(...) {
        wrappingToIntegerLottoNumbers(...);
        validateBonusNumber(...);
    }

    private void wrappingToIntegerLottoNumbers(...) {

    }

    private void validateBonusNumber(...) {

    }

또한 일반적으로 클래스 내부에서는 public, protected, private처럼 접근 범위가 넓은 메서드부터 좁은 메서드 순으로 배치하는 관례를 많이 따릅니다. LottoResult클래스에서도 동일하게 public을 먼저 배치해주세요 ~ 다른 클래스도 검토 부탁드려요

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.

생성자 내 호출순서와 접근제어자
두가지를 고려해서 배치를 수정해보았습니다!


private final ArrayList<Integer> winningLottoNumbers;
private final LottoTickets lottoTickets;

private final int bonusNumber;

public LottoChecker(String[] lastWeekWinnerLottoNumbers, LottoTickets lottoTickets, String bonusNumber) {
this.winningLottoNumbers = wrappingToIntegerLottoNumbers(lastWeekWinnerLottoNumbers);
this.lottoTickets = lottoTickets;
validateBonusNumber(bonusNumber);
this.bonusNumber = Integer.parseInt(bonusNumber);
}

public ArrayList<LottoWinningType> checkAllTickets() {
ArrayList<LottoWinningType> winningTypes = new ArrayList<>();

for (int i = 0; i < lottoTickets.getSize(); i++) {
int matchCount = calculateMatchCountForTicket(i);
boolean matchBonus = hasBonusNumber(i);

winningTypes.add(LottoWinningType.valueOf(matchCount, matchBonus));
}
return winningTypes;
}

public boolean hasBonusNumber(int lottoTicketIndex) {
return lottoTickets.getLottoTreeSet(lottoTicketIndex).contains(this.bonusNumber);
}

private ArrayList<Integer> wrappingToIntegerLottoNumbers(String[] stringWinnerNumbers) {
return (ArrayList<Integer>) Arrays.stream(stringWinnerNumbers)
.map(Integer::parseInt)
.collect(Collectors.toList());
}

private void validateBonusNumber(String bonusNumber) {
try {
int number = Integer.parseInt(bonusNumber);
if (number < Lotto.LOTTO_NUMBER_LOWER_BOUND || number > Lotto.LOTTO_NUMBER_BOUND) {
throw new IllegalArgumentException(
"보너스 볼은" + Lotto.LOTTO_NUMBER_LOWER_BOUND + "과" + Lotto.LOTTO_NUMBER_BOUND + "사이의 숫자여야 합니다.");
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("보너스 볼은 숫자여야 합니다.");
}
}

private int calculateMatchCountForTicket(int lottoTicketIndex) {
int matchCount = 0;
for (int winningNumber : winningLottoNumbers) {
matchCount += getMatchScore(lottoTicketIndex, winningNumber);
}
return matchCount;
}

private int getMatchScore(int lottoTicketIndex, int winningNumber) {
if (lottoTickets.getLottoTreeSet(lottoTicketIndex).contains(winningNumber)) {
return 1;
}
return 0;
}

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

import java.util.Map;

public class LottoResult {

private final LottoStatistics lottoStatistics;
private final int purchaseAmount;

public LottoResult(LottoStatistics lottoStatistics, int purchaseAmount) {
this.lottoStatistics = lottoStatistics;
this.purchaseAmount = purchaseAmount;
}

public double calculateProfitRate() {
long totalWinningPrize = calculateTotalPrize();
if (totalWinningPrize == 0) {
return 0.0;
}
return (double) totalWinningPrize / purchaseAmount;
}

private long calculateTotalPrize() {
long totalPrize = 0;
Map<LottoWinningType, Integer> stats = lottoStatistics.getMatchStatistics();

for (Map.Entry<LottoWinningType, Integer> entry : stats.entrySet()) {
LottoWinningType type = entry.getKey();
int count = entry.getValue();
totalPrize += (long) type.prizeExpression(count);
}
return totalPrize;
}

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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class LottoStatistics {
private Map<LottoWinningType, Integer> matchStatistics = new HashMap<>();

public LottoStatistics() {
for (LottoWinningType type : LottoWinningType.values()) {
matchStatistics.put(type, 0);
}
}

public Map<LottoWinningType, Integer> countMatches(ArrayList<LottoWinningType> winningTypes) {
for (LottoWinningType type : winningTypes) {
matchStatistics.put(type, matchStatistics.get(type) + 1);
}
return matchStatistics;
}

public Map<LottoWinningType, Integer> getMatchStatistics() {
return matchStatistics;
}
}
21 changes: 21 additions & 0 deletions src/main/java/domain/LottoTicketCount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package domain;

public class LottoTicketCount {
public static final int PRICE_PER_ONE_LOTTO_TICKET = 1000;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LottoResult에도 PRICE_PER_ONE_LOTTO_TICKET이 있어요.

로또 하나의 가격을 2000원으로 변경한다면, 변경 지점이 여러 곳 이네요!
조금 개선해보면 좋을 것 같아요

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.

LottoTicketCount 에 두는것이 나을거같아서 여기 클래스에 있는 티켓가격상수를 살려두고
LottoResult에 있는것은 지웠습니다.
Application.java에서 사용시에도 LottoTicketCount.PRICE_PER_ONE_LOTTO_TICKET 해서 사용하도록 수정했습니다.

private int lottoTicketCount;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

lottoTicketCount를 객체의 상태로 두어야 하는 이유가 무엇일까요?

단순히 특정 메서드 안에서 계산하고 바로 반환하는 값 인 것 같아요 ~

@juhee0223 juhee0223 Aug 5, 2026

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.

큰 고민없이 클래스의 인스턴스 변수로 두었었는데요, 성현님 질문을 통해 새롭게 배운 내용입니다.

  1. 객체지향프로그래밍에서 클래스의 인스턴스 변수는 그 객체가 존재하는동안 유지하고 관리해야할 데이터이다.
  2. 인스턴스 변수 (=상태)는 여러 메서드에 의해 변경되고 사용된다.
  3. 하지만 현재 LottoTicketCount 클래스의 lottoTicketCount 필드는 convertLottoPriceToTicketCount 메서드가 실행되는 동안에만 잠깐 사용되고, 그 값이 바로 반환된다. 다른 메서드에서 이 값을 다시 사용하지도 않고, 이 객체가 생성된 후에 lottoTicketCount 값이 계속해서 의미를 갖지도 않는다.

=> 결론: 인스턴스 변수로 두게 되면, 불필요한 상태가 되는 셈이다. 즉, 인스턴스 변수가 아닌, 메서드 안에서 사용하는 지역변수이면 충분하다.

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.

위와 같은 이유로 LottoTickectCount에서 상태를 가지지 않게 수정을 했습니다.
수정을 하고보니, LottoTicketCount 클래스를 유틸리티 클래스로 사용하는게 좋겠다는 판단이 들어서 유틸리티 클래스로 고쳐보았고 이 클래스 사용하던 곳에서도 객체생성없이 바로 클래스 호출하도록 코드를 수정했습니다.


public int convertLottoPriceToTicketCount(int totalLottoPrice) {
validatePurchaseAmount(totalLottoPrice);
lottoTicketCount = totalLottoPrice / PRICE_PER_ONE_LOTTO_TICKET;
return lottoTicketCount;
}

private void validatePurchaseAmount(int price) {
if (price < PRICE_PER_ONE_LOTTO_TICKET) {
throw new IllegalArgumentException("구입 금액은 " + PRICE_PER_ONE_LOTTO_TICKET + "원 이상이어야 합니다.");
}
if (price % PRICE_PER_ONE_LOTTO_TICKET != 0) {
throw new IllegalArgumentException("구입 금액은 " + PRICE_PER_ONE_LOTTO_TICKET + "원 단위로 입력해야 합니다.");
}
}
}
34 changes: 34 additions & 0 deletions src/main/java/domain/LottoTickets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package domain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class LottoTickets {
ArrayList<Lotto> lottoArrayList = new ArrayList<>();

public void addUserSelectedLottos(List<String> userSelectedNumbersInput) {
for (String numbersString : userSelectedNumbersInput) {
List<Integer> numbers = Arrays.stream(numbersString.split(",\\s*"))
.map(Integer::parseInt)
.collect(Collectors.toList());
lottoArrayList.add(new Lotto(numbers));
}
}

public void addAutoLottos(int autoCount) {
for (int i = 0; i < autoCount; i++) {
lottoArrayList.add(new Lotto());
}
}

public TreeSet<Integer> getLottoTreeSet(int lottoTicketNumber){
return new TreeSet<>(lottoArrayList.get(lottoTicketNumber).getRandomNumberSet());
}

public int getSize() {
return lottoArrayList.size();
}
}
48 changes: 48 additions & 0 deletions src/main/java/domain/LottoWinningType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package domain;

import java.util.Arrays;
import java.util.function.Function;

public enum LottoWinningType {
FIRST_PLACE("6개 일치 (2000000000원)- ", tickets -> tickets * 2000000000),
SECOND_PLACE("5개 일치, 보너스 볼 일치(30000000원)- ", tickets -> tickets * 30000000),
THIRD_PLACE("5개 일치 (1500000원)- ", tickets -> tickets * 1500000),
FOURTH_PLACE("4개 일치 (50000원)- ", tickets -> tickets * 50000),
FIFTH_PLACE("3개 일치 (5000원)- ", tickets -> tickets * 5000),
NO_PRIZE("2개 이하 일치 (0원)- ", tickets -> 0d);

private String winningDescription;
private Function<Double, Double> prizeExpression;


LottoWinningType(String winningDescription, Function<Double, Double> prizeExpression) {
this.winningDescription = winningDescription;
this.prizeExpression = prizeExpression;

}

public double prizeExpression(double matchingTickets) {
return prizeExpression.apply(matchingTickets);
}

public String getWinningDescription() {
return winningDescription;
}

public static LottoWinningType valueOf(int matchCount, boolean matchBonus) {
if (matchCount == 6) return FIRST_PLACE;
if (matchCount == 5 && matchBonus) return SECOND_PLACE;
if (matchCount == 5) return THIRD_PLACE;
if (matchCount == 4) return FOURTH_PLACE;
if (matchCount == 3) return FIFTH_PLACE;
return NO_PRIZE;
}

public static LottoWinningType findLottoWinningType(String winningType){
return Arrays.stream(LottoWinningType.values())
.filter(lottoWinningTypePrize -> lottoWinningTypePrize.name().equals(winningType))
.findAny()
.orElse(NO_PRIZE);
}

}
Loading