Skip to content
Open
9 changes: 9 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import controller.LottoController;

public class Application {

public static void main(String[] args) {
LottoController lottoController = new LottoController();
lottoController.run();
}
}
78 changes: 78 additions & 0 deletions src/main/java/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package controller;

import model.*;
import view.ErrorView;
import view.InputView;
import view.ResultView;

import java.util.List;

public class LottoController {

public void run() {
try {
Money money = getPurchaseAmount();
int manualCount = getManualCount(money);
LottoTickets lottoTickets = generateLottoTickets(money, manualCount);

ResultView.printOrderTickets(manualCount, money.getTicketCount() - manualCount);
ResultView.printPurchasedLottoTickets(lottoTickets);

processWinningResults(lottoTickets, money);

InputView.closeScanner();

} catch (IllegalArgumentException e) {
ErrorView.printErrorMessage(e.getMessage());
}
}

private Money getPurchaseAmount() {
Money money = new Money(InputView.getPurchaseAmount());
validatePurchaseAmount(money);
return money;
}

private int getManualCount(Money money) {
int manualCount = InputView.getManualTicketCount();
validateManualCount(manualCount, money);
return manualCount;
}

private LottoTickets generateLottoTickets(Money money, int manualCount) {
List<List<Integer>> manualNumbers = InputView.getManualNumbers(manualCount);
int autoCount = money.getTicketCount() - manualCount;
return new LottoTickets(manualNumbers, autoCount);
}

public void validateManualCount(int manualCount, Money money) {
if (manualCount > money.getTicketCount()) {
throw new IllegalArgumentException("수동 구매 개수가 구매 가능한 개수를 초과할 수 없습니다.");
}
}

public void validatePurchaseAmount(Money money) {
int purchaseAmount = money.getAmount();

if (purchaseAmount < 1000) {
throw new IllegalArgumentException("구매 금액은 1000원 이상이어야 합니다.");
}
if (purchaseAmount % 1000 != 0) {
throw new IllegalArgumentException("구매 금액은 1000원 단위여야 합니다.");
}
}

private void processWinningResults(LottoTickets lottoTickets, Money money) {
ResultView.printWinningStatistics(createLottoResult(lottoTickets, getWinningNumbers()), money.getAmount());
}

private WinningNumbers getWinningNumbers() {
List<Integer> winningNumbers = InputView.getWinningNumbers();
int bonusNumber = InputView.getBonusNumber();
return new WinningNumbers(winningNumbers, bonusNumber);
}

private LottoResult createLottoResult(LottoTickets lottoTickets, WinningNumbers winningNumbers) {
return new LottoResult(lottoTickets.getTickets(), winningNumbers);
}
}
20 changes: 20 additions & 0 deletions src/main/java/model/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package model;

import java.util.*;

public class Lotto {

private final LottoNumbers lottoNumbers;

public Lotto() {
this.lottoNumbers = new LottoNumbers();
}

public Lotto(List<Integer> numbers) {
this.lottoNumbers = new LottoNumbers(numbers);
}

public List<Integer> getSortedNumbers() {
return lottoNumbers.getSortedNumbers();
}
Copy link

Choose a reason for hiding this comment

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

네이밍과 해당 메서드의 동작이 다른 것 같아요
또한 디미터의 법칙에서는 "내부의 내부에 접근하지 말라"를 제안하고 있어요 해당 코드는 이를 위반하고 있는 것 같아요
클라이언트가 Lotto 클래스의 내부 구조도 알게 되어 결합도도 높아보여요

정빈이는 어떻게 생각해요?

Copy link
Author

Choose a reason for hiding this comment

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

  • 기존의 getSortedNumbers()는 정렬된 번호를 반환하는 것으로 해석될 수 있습니다. 그러나 실제로는 LottoNumbers 의 메서드를 그대로 호출하는 구조였습니다. 그래서 메서드명과 실제 동작이 일치하지 않았습니다.

  • 메서드명을 getNumbers()로 변경하여 로또번호를 가져온다는 의미를 명확하도록 하였습니다.

  • 디미터의 법칙을 이해하는 데 어려웠습니다.. 제가 이해한 바로는 ‘내가 소유한 객체의 내부 로직을 알아서는 안된다.’였습니다. 더 조사해보니 소유한 객체의 메서드는 호출해도 괜찮으나 내부 로직까지 신경써야 하는 경우면 디미터의 법칙을 위반한다는 것을 깨닫게 되었습니다! 그래서 현재 코드에서는 getSortedNumbers() 메서드가 로또번호를 보여줄 뿐만 아니라 정렬해주는 역할까지 담당하고 있었기 때문에 내부 로직이 노출된다는 것을 알게되었습니다. 그래서 LottoNumbers 에서 정렬하는 sortNumbers() 메서드와 로또 번호를 조회하는 getNumbers() 메서드로 분류하였습니다. 그리고 로또번호 조회만 가능한 getNumbers()를 사용하면 디미터의 법칙을 위반하지 않는 것 같다고 판단하였습니다…! 아래와 같이 수정하였는데, 제가 알맞게 이해하고 리팩토링한 것인지 궁금합니다..!

public List<Integer> getNumbers() {
    return lottoNumbers.getNumbers();
}

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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class LottoNumbers {
private static final int LOTTO_MIN_NUMBER = 1;
private static final int LOTTO_MAX_NUMBER = 45;
private static final int LOTTO_CREATE_SIZE = 6;
private static final List<Integer> LOTTO_NUMBER_POOL =
IntStream.rangeClosed(LOTTO_MIN_NUMBER, LOTTO_MAX_NUMBER)
.boxed()
.collect(Collectors.toList());

private List<Integer> numbers;

public LottoNumbers() {
this.numbers = createLottoNumbers();
}

public LottoNumbers(List<Integer> numbers) {
validate(numbers);
this.numbers = List.copyOf(numbers);
}
Copy link

Choose a reason for hiding this comment

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

불변 컬렉션 사용을 통한 방어적 복사가 미흡해보여요

현재 List<Integer> numbers는 가변 리스트로서 방어적 복사가 불완전해보여요
List.copyOf()를 통해서 외부에 불변 리스트를 반환하지만, 내부적으로는 가변의 상태를 유지하고 있어요
추가적으로 List.copyOf()를 통해서 불변 리스트로 반환하지만 getSortedNumbers()를 통해서 정렬된 리스트를 새로 만들어주고 있어요 -> 불필요한 리스트 복사와 정렬이 매번 발생하게 되는 구조

개선해봅시다!

Copy link
Author

@JeongBeanHyun JeongBeanHyun Mar 16, 2025

Choose a reason for hiding this comment

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

네 피드백 감사합니다!
1.가변 리스트 문제

  • 내부 리스트 (numbers)가 가변 상태로 유지되는 문제를 해결하기 위해 final 키워드를 추가하고, 리스트를 List.copyOf()를 통해 한 번만 할당하여 변경할 수 없도록 했습니다.

2.getSortedNumbers()에서 매번 정렬 리스트를 새로 만드는 문제

  • 정렬된 리스트를 한 번만 저장하고, 이후에는 불필요한 정렬 연산이 발생하지 않도록 했습니다.
  • 정렬 로직을 sortedNumbers() 메서드로 분리하여 객체 생성 시 한 번만 정렬하도록 했습니다.
    private List<Integer> sortedNumbers(List<Integer> numbers) {
        return List.copyOf(numbers.stream().sorted().toList());
    }

    public List<Integer> getNumbers() {
        return numbers;
    }


private void validate(List<Integer> numbers) {
if (numbers.size() != LOTTO_CREATE_SIZE) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
if (numbers.stream().distinct().count() != LOTTO_CREATE_SIZE) {
throw new IllegalArgumentException("중복된 숫자가 있습니다.");
}
if (numbers.stream().anyMatch(n -> n < LOTTO_MIN_NUMBER || n > LOTTO_MAX_NUMBER)) {
throw new IllegalArgumentException("로또 번호는 1~45 사이여야 합니다.");
}
}

private List<Integer> createLottoNumbers() {
Copy link

Choose a reason for hiding this comment

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

로또 번호를 생성한다? 보다는 더 자세하게 네이밍 할 수 있을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

자동 로또 번호를 생성하는 메서드이기 때문에 generateAutoLottoNumbers로 수정하였습니다. 네이밍에 더욱 신경써서 작성해야할 것 같습니다..! 피드백 감사합니다!

List<Integer> shuffledNumbers = new ArrayList<>(LOTTO_NUMBER_POOL);
Copy link

@woogym woogym Mar 5, 2025

Choose a reason for hiding this comment

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

매번 new ArrayList<>(LOTTO_NUMBER_POOL)를 호출하지 않고 IntStream을 사용하여 바로 새로운 리스트를 생성할 수 있을 것 같아요

Copy link
Author

@JeongBeanHyun JeongBeanHyun Mar 16, 2025

Choose a reason for hiding this comment

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

LOTTO_NUMBER_POOL 변수를 삭제하고, 우진 리뷰어님께서 추천해주신 IntStream을 사용하여 리스트를 생성하도록 수정하였습니다!

List<Integer> shuffledNumbers = IntStream.rangeClosed(LOTTO_MIN_NUMBER, LOTTO_MAX_NUMBER)
        .boxed()
        .collect(Collectors.toList());

Collections.shuffle(shuffledNumbers);

return shuffledNumbers.subList(0, LOTTO_CREATE_SIZE);
}

public List<Integer> getSortedNumbers() {
return numbers.stream()
.sorted()
.collect(Collectors.toList());
}
}
42 changes: 42 additions & 0 deletions src/main/java/model/LottoResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package model;

import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class LottoResult {
private final Map<Rank, Integer> matchCountMap = new EnumMap<>(Rank.class);

public LottoResult(List<Lotto> tickets, WinningNumbers winningNumbers) {
for (Lotto ticket : tickets) {
Rank rank = determineRank(ticket, winningNumbers);
matchCountMap.put(rank, matchCountMap.getOrDefault(rank, 0) + 1);
}
}

private Rank determineRank(Lotto ticket, WinningNumbers winningNumbers) {
List<Integer> ticketNumbers = ticket.getSortedNumbers();
int matchCount = (int) ticketNumbers.stream()
.filter(winningNumbers.getNumbers()::contains)
.count();
boolean hasBonus = ticketNumbers.contains(winningNumbers.getBonusNumber());

if (matchCount == 6) return Rank.FIRST;
if (matchCount == 5 && hasBonus) return Rank.SECOND;
if (matchCount == 5) return Rank.THIRD;
if (matchCount == 4) return Rank.FOURTH;
if (matchCount == 3) return Rank.FIFTH;
return Rank.NONE;
}
Copy link

Choose a reason for hiding this comment

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

요구사항 - "하나의 메서드는 10줄을 넘지 않는다"를 충족하고 있지 못한 것 같아요 수정해봅시다

Copy link
Author

Choose a reason for hiding this comment

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

요구사항에 맞게 메서드 분리를 통해 10줄을 넘지 않도록 수정하였습니다. 피드백 감사합니다!

private Rank determineRank(Lotto ticket, WinningNumbers winningNumbers) {
    int matchCount = countMatchingNumbers(ticket, winningNumbers);
    boolean hasBonus = hasBonusNumber(ticket, winningNumbers);

    return getRank(matchCount, hasBonus);
}

// 일치하는 숫자 개수 계산
private int countMatchingNumbers(Lotto ticket, WinningNumbers winningNumbers) {
    return (int) ticket.getNumbers().stream()
            .filter(winningNumbers.getNumbers()::contains)
            .count();
}

// 보너스 번호 포함 여부 확인
private boolean hasBonusNumber(Lotto ticket, WinningNumbers winningNumbers) {
    return ticket.getNumbers().contains(winningNumbers.getBonusNumber());
}

private Rank getRank(int matchCount, boolean hasBonus) {
    if (matchCount == 6) return Rank.FIRST;
    if (matchCount == 5 && hasBonus) return Rank.SECOND;
    if (matchCount == 5) return Rank.THIRD;
    if (matchCount == 4) return Rank.FOURTH;
    if (matchCount == 3) return Rank.FIFTH;
    return Rank.NONE;
}


public Map<Rank, Integer> getMatchCountMap() {
return matchCountMap;
}

public double calculateProfitRate(int totalCost) {
int totalPrize = matchCountMap.entrySet().stream()
.mapToInt(entry -> entry.getKey().getPrizeMoney() * entry.getValue())
.sum();
return (double) totalPrize / totalCost;
}
Copy link

Choose a reason for hiding this comment

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

stream 안에서 계산 로직과 데이터 변환이 동시에 이루어지고 있어 가독성이 떨어져요
한 줄에 너무 많은 로직이 들어가 있어서 유지보수를 하더라도 어려움이 있어보이는데 정빈이는 어떻게 생각해요?
entry라는 람다식 인자도 어떤 의미를 하는지 불분명 한 것 같아요 개선해봅시다!

Copy link
Author

Choose a reason for hiding this comment

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

기존 코드에서는 stream 내부에서 데이터 변환과 계산 로직이 동시에 이루어지고 있었습니다. 이렇게 되면 한 줄에 너무 많은 기능이 포함되어 가독성이 떨어지고, 유지보수 시에도 수정이 어렵다는 문제가 있었습니다.
그래서 데이터 변환과 계산 로직을 분리하여 각각의 역할을 명확하게 나누었습니다.

  1. 데이터 변환: convertToPrizeAmounts() 메서드에서 matchCountMap을 순회하며 Rank → 당첨금 리스트로 변환하는 역할을 수행합니다.
  2. 개별 변환: convertToPrizeAmount() 메서드를 분리하여 각 Rank의 당첨금을 개수와 곱하는 연산을 별도로 수행하도록 했습니다.
  3. 합산 계산: 변환된 리스트를 받아 sumPrizeAmounts() 메서드에서 총 당첨금 합산을 수행하도록 분리했습니다.

이렇게 리팩토링함으로써,
• 데이터 변환과 계산 로직을 분리하여 stream 내부의 복잡도를 줄였습니다.
• convertToPrizeAmount() 메서드를 별도로 만들어 람다식 인자(entry)가 무엇을 의미하는지 명확하게 했습니다.
• 한 줄에서 여러 연산이 이루어지는 문제를 해결하여 가독성을 높였습니다.
피드백 감사합니다!

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

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

public class LottoTickets {

private final List<Lotto> tickets;

public LottoTickets(List<List<Integer>> manualNumbers, int autoTicketCount) {
this.tickets = new ArrayList<>();
addManualTickets(manualNumbers);
addAutoTickets(autoTicketCount);
}
Copy link

Choose a reason for hiding this comment

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

하나의 생성자가 수동, 자동 로또 생성까지 모두 담당하고 있는데 단일 책임 원칙에는 부합해보이지 않아요
어떻게 개선할 수 있을까요?

Copy link
Author

@JeongBeanHyun JeongBeanHyun Mar 16, 2025

Choose a reason for hiding this comment

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

수동, 자동 로또 생성자를 구분하여 수정하였습니다. 생성자는 하나의 책임을 가져야하기 때문에 분류하는 것이 올바른 것 같습니다! 피드백 감사합니다!

    public LottoTickets(List<List<Integer>> manualNumbers) {
        List<Lotto> manualTickets = new ArrayList<>();
        addManualTickets(manualTickets, manualNumbers);
        this.tickets = List.copyOf(manualTickets);
    }

    public LottoTickets(int autoTicketCount) {
        List<Lotto> autoTickets = new ArrayList<>();
        addAutoTickets(autoTickets, autoTicketCount);
        this.tickets = List.copyOf(autoTickets);
    }


private void addManualTickets(List<List<Integer>> manualNumbers) {
for (List<Integer> numbers : manualNumbers) {
tickets.add(new Lotto(numbers));
}
}

private void addAutoTickets(int autoTicketCount) {
for (int i = 0; i < autoTicketCount; i++) {
tickets.add(new Lotto());
}
}

public List<Lotto> getTickets() {
return new ArrayList<>(tickets);
}
Copy link

Choose a reason for hiding this comment

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

이곳또한 불변성을 고려하지 못한 설계 같아요

Copy link
Author

Choose a reason for hiding this comment

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

List.copyOf()를 사용하여 불변리스트로 변환 후, getTickets()에서 추가적인 복사 없이 불변 리스트를 그대로 반환하도록 수정하였습니다. 피드백 덕분에 불변성에 대해 다시 한번 생각할 수 있었습니다. 감사합니다!

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

public class Money {
private static final int LOTTO_PRICE = 1000;
private final int amount;

public Money(int amount) {
this.amount = amount;
}

public int getAmount() {
return amount;
}

public int getTicketCount() {
return amount / LOTTO_PRICE;
}
}
Copy link

Choose a reason for hiding this comment

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

Money라는 네이밍이 많이 어색해보여요 🤔
단순히 티켓의 갯수를 계산하는 역할을 하는 것으로 보이는데 더 의미있는 클래스명을 사용 or 단일 함수로서 클래스가 아닌 단일 함수 메서드로 분류할수도 있을 것 같아요

정빈이가 고민해보고 개선하여 어떤 이유로 개선 방법을 고려했는지 알려주세요!

Copy link
Author

@JeongBeanHyun JeongBeanHyun Mar 16, 2025

Choose a reason for hiding this comment

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

로또 구매에 대한 정보(로또 구매 금액, 티켓 개수 계산)를 담고 있기때문에LottoPurchaseInfo라고 수정하여 의미를 명확히 하였습니다. 그리고 우진 리뷰어님의 말씀대로 클래스를 유지할지, 단일 함수 메서드로 분류할지 고민하다가 클래스를 유지하는 것으로 결정하였습니다!

  1. LottoPurchaseInfo 객체를 유지한 이유
    LottoPurchaseInfo는 단순한 계산 기능을 수행하는 것이 아니라, "로또 구매 금액을 관리하는 도메인 객체" 로서의 역할을 가지도록 설계하였습니다.
    이렇게 하면 다른 개발자들도 "로또 구매 금액을 관리하는 객체가 별도로 존재한다"는 점을 명확히 이해할 수 있습니다.
  2. 컨트롤러의 책임을 분리한 이유
    기존 LottoController에서 금액 검증을 직접 수행하고 있었지만,
    "금액이 유효한지 판단하는 책임은 LottoPurchaseInfo가 가져야 한다" 고 판단하여 검증 로직을 LottoPurchaseInfo객체 내부로 이동하였습니다.
    피드백 감사합니다!

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

public enum Rank {
FIFTH(3, false, 5_000),
FOURTH(4, false, 50_000),
THIRD(5, false, 1_500_000),
SECOND(5, true, 30_000_000), // 5개 일치 + 보너스 볼 일치
FIRST(6, false, 2_000_000_000),
NONE(0, false, 0); // NONE은 출력에서 제외됨

private final int matchCount;
private final boolean bonusMatch;
private final int prizeMoney;

Rank(int matchCount, boolean bonusMatch, int prizeMoney) {
this.matchCount = matchCount;
this.bonusMatch = bonusMatch;
this.prizeMoney = prizeMoney;
}

public int getMatchCount() {
return matchCount;
}

public int getPrizeMoney() {
return prizeMoney;
}

public boolean isBonusMatch() {
return bonusMatch;
}
}
43 changes: 43 additions & 0 deletions src/main/java/model/WinningNumbers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package model;

import java.util.List;

public class WinningNumbers {
private static final int SIZE = 6;
private static final int MIN_NUMBER = 1;
private static final int MAX_NUMBER = 45;
private final List<Integer> numbers;
private final int bonusNumber;

public WinningNumbers(List<Integer> numbers, int bonusNumber) {
validate(numbers, bonusNumber);
this.numbers = List.copyOf(numbers);
this.bonusNumber = bonusNumber;
}

private void validate(List<Integer> numbers, int bonusNumber) {
if (numbers.size() != SIZE) {
throw new IllegalArgumentException("당첨 번호는 6개의 숫자로 입력해야 합니다.");
}
if (numbers.stream().distinct().count() != SIZE) {
throw new IllegalArgumentException("중복된 숫자가 있습니다.");
}
if (numbers.stream().anyMatch(n -> n < MIN_NUMBER || n > MAX_NUMBER)) {
throw new IllegalArgumentException("당첨 번호는 1부터 45 사이여야 합니다.");
}
if (bonusNumber < MIN_NUMBER || bonusNumber > MAX_NUMBER) {
throw new IllegalArgumentException("보너스 볼은 1부터 45 사이여야 합니다.");
}
if (numbers.contains(bonusNumber)) {
throw new IllegalArgumentException("보너스 볼은 당첨 번호와 중복될 수 없습니다.");
}
}
Copy link

Choose a reason for hiding this comment

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

요구사항인 하나의 함수가 10줄을 넘지 않는다를 준수하고 있지 못한 것 같아요 🫠
이와 별개로 만약 검증해야하는 규칙이 더욱더 많아진다면 거대 메서드가 만들어질 것 같은데 어떻게 개선할 수 있을까요?

Copy link
Author

Choose a reason for hiding this comment

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

검증 로직이 추가될 경우 기존 validate() 메서드가 지나치게 커지고, 유지보수가 어려워질 가능성이 있어 각 검증을 개별 메서드로 분리하는 방식으로 개선하였습니다. 검증해야하는 규칙이 많아진다면 단일 책임을 잘 준수하도록 메서드로 각각 분리하여 개선하는 것이 좋은 방향성 같습니다..! 피드백 감사합니다!

public void validate(List<Integer> numbers, int bonusNumber) {
    validateSize(numbers);
    validateDuplicates(numbers);
    validateNumberRange(numbers);
    validateBonusNumber(bonusNumber);
    validateBonusNotInWinningNumbers(numbers, bonusNumber);
}

private void validateSize(List<Integer> numbers) {
    if (numbers.size() != SIZE) {
        throw new IllegalArgumentException("당첨 번호는 6개의 숫자로 입력해야 합니다.");
    }
}

private void validateDuplicates(List<Integer> numbers) {
    if (numbers.stream().distinct().count() != SIZE) {
        throw new IllegalArgumentException("중복된 숫자가 있습니다.");
    }
}

private void validateNumberRange(List<Integer> numbers) {
    if (numbers.stream().anyMatch(n -> n < MIN_NUMBER || n > MAX_NUMBER)) {
        throw new IllegalArgumentException("당첨 번호는 1부터 45 사이여야 합니다.");
    }
}

private void validateBonusNumber(int bonusNumber) {
    if (bonusNumber < MIN_NUMBER || bonusNumber > MAX_NUMBER) {
        throw new IllegalArgumentException("보너스 볼은 1부터 45 사이여야 합니다.");
    }
}

private void validateBonusNotInWinningNumbers(List<Integer> numbers, int bonusNumber) {
    if (numbers.contains(bonusNumber)) {
        throw new IllegalArgumentException("보너스 볼은 당첨 번호와 중복될 수 없습니다.");
    }
}


public List<Integer> getNumbers() {
return numbers;
}

public int getBonusNumber() {
return bonusNumber;
}
}
7 changes: 7 additions & 0 deletions src/main/java/view/ErrorView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package view;

public class ErrorView {
public static void printErrorMessage(String message) {
System.out.println(message);
}
}
Loading