Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
01a15d2
1,2단계 초안(테스트코드 전)
ke-62 Sep 23, 2025
b74016a
README수정
ke-62 Sep 23, 2025
c56ec71
LottoTest테스트코드완성
ke-62 Sep 24, 2025
e7fdac1
원시값포장
ke-62 Sep 24, 2025
7c0f366
원시값, 일급컬렉션 수정
ke-62 Sep 24, 2025
8d25d65
테스트 코드 오류 수정
ke-62 Sep 24, 2025
aa93828
피드백 1차 수정사항
ke-62 Sep 27, 2025
712355d
Match오류부분 LottoNumber에 로직추가로 수정
ke-62 Sep 27, 2025
5483100
[TEST]하드코딩 되어있는 부분들 수정, 문법적 오류 수정
ke-62 Sep 29, 2025
a950c6d
[TEST]ParameterizedTest사용 및 와일드카드 사용부 제거
ke-62 Sep 29, 2025
32e4236
[TEST]오타수정
ke-62 Sep 29, 2025
db12651
[TEST]오타수정2
ke-62 Sep 29, 2025
3ee3b9e
리스트->hastset,treeset변경
ke-62 Sep 30, 2025
a7f1d52
상수화, 네이밍 변경
ke-62 Sep 30, 2025
c1a8a28
오탈자 수정
ke-62 Sep 30, 2025
684a177
[feat]보너스볼 로직 추가
ke-62 Sep 30, 2025
bb099d0
정팩메추가
ke-62 Sep 30, 2025
c0a617d
예외처리 로직 추가
ke-62 Oct 1, 2025
0a3d25d
[TEST]보너스볼테스트코드작성
ke-62 Oct 1, 2025
a195cdd
[feat]4단계 README 및 오류 수정
ke-62 Oct 1, 2025
31cf72e
[feat]수동입력 로직 추가
ke-62 Oct 1, 2025
448f371
[refector]보너스볼 테스트코드 수정
ke-62 Oct 1, 2025
461131a
[feat]오류 수정
ke-62 Oct 1, 2025
58cf0ec
[refector]숫자들 매직넘버화
ke-62 Oct 2, 2025
fff7970
[TEST]수동입력 테스트코드
ke-62 Oct 2, 2025
7995e01
Merge branch 'ke-62' into goeun
ke-62 Oct 4, 2025
ebf2453
[refector]getTicketCount부분 수정
ke-62 Oct 4, 2025
f157862
[refector]getTicketCount수정
ke-62 Oct 4, 2025
050a96b
[refector]MathCountTest 간결하게 수정
ke-62 Oct 5, 2025
b2ca2bc
[refector]코드 가독성을 위한 줄바꿈 적용
ke-62 Oct 5, 2025
43244b9
[refector]로또 금액이 1000 미만일 때, 수동 구매 로또가 총 로또 수보다 클 때 다시 입력받고록 수정
ke-62 Oct 5, 2025
0e25574
[refector]LottoTickets -> Lottos 클래스 네이밍 더 연관성이 보이도록 수정, Lotto 유효성 검사…
ke-62 Oct 6, 2025
9326c01
[refector]보너스볼, 당첨로또 오류 입력 시 다시 입력받도록 수정
ke-62 Oct 6, 2025
cea3d80
[refector]줄바꿈 수정
ke-62 Oct 6, 2025
0f760a5
[refector]ResultView에서 네이밍 가독성 좋게 수정
ke-62 Oct 9, 2025
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
32 changes: 32 additions & 0 deletions README.md
Copy link

Choose a reason for hiding this comment

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

5단계 요구사항이 없어 보이는데 확인 부탁드려요!

Copy link
Author

Choose a reason for hiding this comment

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

5단계는 리팩토링이라 크게 적은게 없긴 한데, 추가해 두겠습니다!!

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## 로또 미션 1-5단계

### 기능 요구사항

#### 1단계
- 사용자는 로또 구매 금액을 입력할 수 있다.
- 로또 가격은 1,000원이다.
- 사용자는 구매 금액에 해당하는 로또를 자동으로 발급받는다.
- 1~45 사이의 숫자 중 6개를 랜덤으로 선택한다.
- 겹치는 숫자는 있으면 안 된다.
- 로또 번호는 크기 순서대로 정렬되어야 한다.

#### 2단계
- 사용자는 당첨 번호를 입력할 수 있다.
- 일치한 번호 수에 따른 당첨 금액을 알려준다.
- 3개 일치: 5000원
- 4개 일치: 50000원
- 5개 일치: 1500000원
- 5개 일치: 30000000원
- 6개 일치: 2000000000원
- 총 수익률을 알려준다.
- 수익률 = (총 당첨 금액) / (총 구매 금액)
- 소수점 둘째 자리까지 표시한다.
- 1미만시 손해이고 1이상이면 이득이다.

#### 3단계
- 보너스볼이 들어온다
- 5개 일치 + 보너스볼 일치: 30000000원
- 5개 일치 + 보너스볼 일치하지 않음 : 1500000원

#### 4단계
- 사용자는 수동으로 구매할 로또 수를 입력할 수 있다.
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
plugins {
id 'java'
id 'application'
}

application {
mainClass = 'Main'
}

group = 'cholog'
Expand Down
Empty file removed src/main/java/.gitkeep
Empty file.
9 changes: 9 additions & 0 deletions src/main/java/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import controller.LottoController;

public class Main {
public static void main(String[] args) {

LottoController lottoController = new LottoController();
lottoController.run();
}
}
85 changes: 85 additions & 0 deletions src/main/java/controller/LottoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package controller;

import domain.Lotto;
import domain.LottoNumber;
import domain.LottoService;
import domain.LottoTicketCount;
import domain.LottoTickets;
import domain.LottoTotalPrice;
import domain.Money;
import domain.MatchCount;
import domain.ProfitRate;
import view.InputView;
import view.OutputView;
import view.ResultView;

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


public class LottoController {
OutputView outputView = new OutputView();
InputView inputView = new InputView();
ResultView resultView = new ResultView();

public void run() {
outputView.printWonMessage();
Money money = new Money(inputView.inputMoney());

LottoTickets lottoTickets = buyLotto(money);
checkLotto(lottoTickets, money);
}


public LottoTickets buyLotto(Money money) {
LottoTicketCount ticketNumber = Money.getTicketCount(money);
Copy link

Choose a reason for hiding this comment

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

Money money 인자를 전달 받을 때 내부에 금액(money)이 이미 정의 되어있을텐데, Money객체 내부에 있는 getTicketCount() 메서드에 Money 클래스를 전달하는 이유가 있을까요??

Copy link
Author

Choose a reason for hiding this comment

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

처음에는 금액으로부터 티켓 개수를 계산하는 기능을 Money의 책임으로 두고 싶었습니다. 다만 getTicketCount를 static으로 작성하다 보니 내부 필드 money에 직접 접근할 수 없어, 어쩔 수 없이 Money 객체를 인자로 전달받아 사용하게 되었습니다. 리뷰를 받고 생각해 보니 인스턴스 메서드로 두고 money.getTicketCount() 형태로 호출하는 편이 더 자연스러운 설계인 것 같습니다!

resultView.printTicketNumbers(ticketNumber.getCount());

outputView.printManualCount();
int manualCount = inputView.inputManualCount();

List<Lotto> manualLottos = new ArrayList<>();
outputView.printManualNumbers();

LottoService lottoService = new LottoService();
for (int i = 0; i < manualCount; i++) {
String manualNumbers = inputView.inputManualNumbers();
manualLottos.add(lottoService.parseLottoAnswer(manualNumbers));

}

int autoCount = ticketNumber.getCount() - manualCount;

resultView.printManualAuto(manualCount, autoCount);
outputView.lottoResult();
LottoTickets lottoTickets = LottoTickets.createMixedTickets(manualLottos, autoCount);

for (Lotto lotto : lottoTickets.getTickets()) {
System.out.println(lotto);
}

return lottoTickets;
}


public void checkLotto(LottoTickets tickektAutoCount, Money money) {
outputView.printLottoAnswer();

LottoService lottoService = new LottoService();
String lottoAnswer = inputView.inputLottoAnswer();
outputView.printBonusMessage();
int bonusBallNumber = inputView.inputBonusNumber();
LottoNumber bonuseBall = new LottoNumber(bonusBallNumber);

Lotto lottoAnswerObj = lottoService.parseLottoAnswer(lottoAnswer);
lottoService.validateBonusBall(lottoAnswerObj, bonuseBall);

MatchCount matchCount = lottoService.calculateMatchCount(tickektAutoCount.getTickets(), lottoAnswerObj, bonuseBall);

ProfitRate profitRate = new ProfitRate(money, new LottoTotalPrice(matchCount));

outputView.lottoResult();
resultView.printLottoMatch(matchCount);
resultView.printLottoProfit(profitRate.getProfitRate());
}
}
30 changes: 30 additions & 0 deletions src/main/java/domain/Lotto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package domain;

import java.util.SortedSet;
import java.util.TreeSet;

public class Lotto {
private final SortedSet<LottoNumber> numbers;

public Lotto(SortedSet<LottoNumber> numbers) {
if (numbers.size() != createList.LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("로또 숫자는 6개여야 하며, 중복될 수 없습니다.");
}
this.numbers = new TreeSet<>(numbers);
}

public int contains(LottoNumber number) {
if (numbers.contains(number)) return 1;
return 0;
}

public SortedSet<LottoNumber> getNumbers() {
return new TreeSet<>(numbers);
}
Comment on lines 26 to 28
Copy link

Choose a reason for hiding this comment

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

Treeset를 새롭게 만들어 반환하는 이유가 있을까요?
내부에 존재하는 numbersSortedSet을 사용중이고 반환하는 타입도 SortedSet으로 지정되어 있는데, TreeSet으로 새롭게 만들어 반환하는 이유가 궁금하네요!

Copy link
Author

Choose a reason for hiding this comment

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

방어적 복사를 하고 싶은 의도가 가장 컸습니다. 처음에는 반환 타입이 SortedSet이니 그대로 생성할 수 있을 거라 생각했는데, SortedSet은 인터페이스라 직접 인스턴스를 만들 수 없어서 정렬과 중복 제거 특성을 가진 대표 구현체인 TreeSet으로 복사하여 반환하도록 했습니다.

Copy link

Choose a reason for hiding this comment

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

List 자료구조도 존재하고 해당 자료구조에도 정렬할 수 있는 로직이 존재하는데, 타입을 Set으로 선택하신 이유도 있을까요?

Lotto 객체 생성 특성상 한번 생성된 이후로 번호가 변경되거나 새로 삽입되지 않아서 이유가 궁금하네요!

Copy link
Author

Choose a reason for hiding this comment

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

처음에는 List를 사용해 코드를 작성했지만, 중복 체크와 정렬을 직접 처리해야 하는 불편함이 있었습니다. 1, 2단계 리뷰어님께서 “적절한 자료구조를 사용하면 코드가 훨씬 간결해진다”는 조언을 주셔서 여러 자료구조를 공부한 끝에 Set을 선택했습니다.
Set을 사용한 이유는 로또 번호의 특성상 중복이 허용되지 않고, 생성 이후 값이 변경되거나 추가되지 않는데, Set 자료구조는 중복을 자동으로 방지해 주고, 또한 TreeSet을 사용하면 번호가 항상 정렬된 상태로 유지되어 별도의 정렬 로직이 필요하지 않아 더욱 간결하게 코드를 짤 수 있기 떄문입니다.


@Override
public String toString() {
return numbers.toString();
}

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

public class LottoNumber implements Comparable<LottoNumber> {
private final int number;

public LottoNumber(int number) {
if (number < createList.LOTTO_MIN_NUMBER || number > createList.LOTTO_MAX_NUMBER) {
throw new IllegalArgumentException("로또 번호는 1부터 45 사이의 숫자여야 합니다.");
}
this.number = number;
}

public int getNumber() {
return number;
}

@Override
public int compareTo(LottoNumber o) {
return Integer.compare(this.number, o.number);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LottoNumber that = (LottoNumber) o;
return number == that.number;
}

@Override
public int hashCode() {
return number;
}

public String toString() {
return String.valueOf(number);
}
}
19 changes: 19 additions & 0 deletions src/main/java/domain/LottoPrice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain;

public enum LottoPrice {
MATCH_3(5000),
MATCH_4(50000),
MATCH_5(1500000),
MATCH_5_BONUS(30000000),
MATCH_6(2000000000);

private final int price;

LottoPrice(int price) {
this.price = price;
}

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

import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

public class LottoService {
public Lotto parseLottoAnswer(String lottoAnswer) {
SortedSet<LottoNumber> numbers = new TreeSet<>();
for (String num : lottoAnswer.split(",")) {
numbers.add(new LottoNumber(Integer.parseInt(num.trim())));
}
return new Lotto(numbers);
}

public void validateBonusBall(Lotto answer, LottoNumber bonusBall) {
if (answer.getNumbers().contains(bonusBall)) {
throw new IllegalArgumentException("보너스 볼은 당첨 번호와 중복될 수 없습니다.");
}
}

public MatchCount calculateMatchCount(List<Lotto> tickets, Lotto answer, LottoNumber bonusBall) {
return MatchCount.calculateStatistics(tickets, answer, bonusBall);
}
}

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

public class LottoTicketCount {
private final int count;

public LottoTicketCount(int count) {
this.count = count;
}

public int getCount() {
return count;
}
}
37 changes: 37 additions & 0 deletions src/main/java/domain/LottoTickets.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;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class LottoTickets {
private List<Lotto> tickets;

public LottoTickets() {
this.tickets = new ArrayList<>();
}

public static LottoTickets createMixedTickets(List<Lotto> manualLottos, int autoCount) {
LottoTickets lottoTickets = new LottoTickets();

lottoTickets.tickets.addAll(manualLottos);

for (int i = 0; i < autoCount; i++) {
Set<Integer> generatedNumbers = createList.generateLottoNumbers();
if (generatedNumbers.size() != 6) {
throw new IllegalArgumentException("로또 번호에 중복된 숫자가 있습니다.");
}
SortedSet<LottoNumber> lottoNumbers = generatedNumbers.stream().map(LottoNumber::new).collect(Collectors.toCollection(TreeSet::new));
lottoTickets.tickets.add(new Lotto(lottoNumbers));
}

return lottoTickets;
}

public List<Lotto> getTickets() {
return tickets;
}
}
19 changes: 19 additions & 0 deletions src/main/java/domain/LottoTotalPrice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain;

import java.util.Arrays;

public class LottoTotalPrice {
private final long totalSum;

public LottoTotalPrice(MatchCount matchCount) {
this.totalSum = calculateTotalSum(matchCount);
}

private long calculateTotalSum(MatchCount matchCount) {
return Arrays.stream(LottoPrice.values()).mapToLong(price -> (long) matchCount.getCount(price) * price.getPrice()).sum();
}

public long getTotalSum() {
return totalSum;
}
}
15 changes: 15 additions & 0 deletions src/main/java/domain/Match.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package domain;

public class Match {

public static int getMatchCount(Lotto lotto, Lotto lottoAnswer) {
int matchCount = 0;

for (LottoNumber number : lotto.getNumbers()) {
if (lottoAnswer.getNumbers().contains(number)) {
matchCount++;
}
}
return matchCount;
}
}
Loading