Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
98a1b03
docs: 리드미 작성하여 1단계 요구사항 정리
mikeylili Mar 31, 2026
6a3ca76
feat: 1단계 도메인 클래스 구현
mikeylili Mar 31, 2026
3a7f998
feat: 1단계 뷰 클래스 구현
mikeylili Mar 31, 2026
c23b07b
feat: 1단계 컨트롤러 클래스 구현
mikeylili Mar 31, 2026
a883c16
feat: LottoCalculator에 사용하기 위해 getAmount 메서드 추가
mikeylili Apr 1, 2026
0d08c93
feat: Lotto라는 일급 클래스를 정의하여 로또배열을 처리.
mikeylili Apr 1, 2026
408b4ce
feat: LottoNumber라는 포장 클래스를 만들어서 로또번호라는 의미를 확실하게 원시값인 int와 구분.
mikeylili Apr 1, 2026
d470a88
feat: Lotto객체를 저장할 수 있는 클래스 생성.
mikeylili Apr 1, 2026
c796209
feat: else if 로직을 사용하지 않고 진행을 할 수 있게 Enum을 사용하여 매칭되는 수의 개수와 실제 수령되는 회…
mikeylili Apr 1, 2026
d7805a2
feat: 2단계 진행을 위해 로또 당첨을 통한 수익률 출력 로직을 추가함.
mikeylili Apr 1, 2026
021a3f1
feat: 입력받은 로또번호들을 Lotto자료형으로 저장하는 로직을 추가하였고, 책임을 적절히 분배하기 위해 이 과정을 두개…
mikeylili Apr 1, 2026
2f79b40
feat: 수익률 계산을 Map을 이용하여 저장한 정보를 바탕으로 효과적으로 진행함. 이때 Map의 Key는 Rank를 넣어…
mikeylili Apr 1, 2026
59d3178
docs: 2단계 리드미 작성
mikeylili Apr 1, 2026
ffb852a
docs: 2단계 도메인 구현
mikeylili Apr 1, 2026
baf39a6
docs: 2단계 뷰 구현
mikeylili Apr 1, 2026
8545419
docs: 2단계 컨트롤러 구현
mikeylili Apr 1, 2026
b7b010e
feat: 접근제어자 작성, 인덴트 1개 이하로 줄이기
mikeylili Apr 5, 2026
7210ab1
feat: 변수명 변경, getMatchNumbers 자료형 바꿔서 구현
mikeylili Apr 5, 2026
862dfe6
feat: 검증을 따로 뺌
mikeylili Apr 5, 2026
e466fce
feat: 로또 개수 저장하는 클래스 새로 생성
mikeylili Apr 5, 2026
4448e93
feat: Max Min public으로 변경
mikeylili Apr 5, 2026
282d716
feat: Money 책임 축소
mikeylili Apr 5, 2026
3906648
feat: parseTolotto 추가
mikeylili Apr 5, 2026
ed4b505
feat: 메서드명 수정
mikeylili Apr 5, 2026
b1e595d
feat: 리드미 수정
mikeylili Apr 5, 2026
412cb11
feat: 인풋 검증 따로 클래스로 분리
mikeylili Apr 5, 2026
97ab632
feat: 불필요한 개행 삭제
mikeylili Apr 5, 2026
e4e62f4
feat: 접근제어자 수정
mikeylili Apr 5, 2026
7d25314
feat: 개행 삭제
mikeylili Apr 6, 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
121 changes: 121 additions & 0 deletions README.md
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

리드미를 통해서 어떤 작업을 하셨는지 조금 더 전달받고 싶어요.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

사실 리드미가 없어도 됩니다! 😄
코드의 의도와 동작을 문서 없이 표현하는 방법이 있을까요?

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.

기능요구사항이랑 프로그래밍요구사항을 점검하고자 적어보긴했습니다. 클래스마다 설명을 적어보며 책임에 대해 점검을 해보기도 했습니다. 또한 다른 사람이 보기에 실행했을때의 테스트 케이스도 보여주는 것이 기능을 직관적으로 보여줄 것이라 생각해서 테스트 케이스도 적었습니다. 코드의 의도와 동작을 문서없이 표현하려면.. 클래스나 메서드 상단에 주석을 달아서 클래스,메서드의 역할을 간단히 적어보는것도 방법일 것 같습니다. 하지만 코드가 좀 길어질 것 같아 리드미에 몰아 넣긴 했지만요 ㅎㅎ

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

깊게 생각해주셨군요!

실제로 기대한 동작을 하고 있다는 것을 한 눈에 볼 수 있는 방법도 있을텐데, 그것은 무엇일까요 ㅎㅎㅎㅎㅎ

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# 1단계 요구사항 정리

---

## 기능 요구사항

- [x] 로또 구입 금액을 입력하면 구입 금액에 해당하는 로또를 발급해야 한다.
- [x] 로또 1장의 가격은 1000원이다.
- [x] 로또 당첨 번호를 받아 일치한 번호 수에 따라 당첨 결과를 보여준다.

## 새로운 프로그래밍 요구사항

- [x] 배열 대신 컬렉션을 사용한다.
- [x] 줄여 쓰지 않는다(축약 금지).
- [x] 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.
- [x] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
Comment on lines +13 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.

기능 요구 사항인가요?

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.

앗 프로그래밍요구사항이 섞였네요😅

- [x] 모든 원시 값과 문자열을 포장한다.
- [x] 일급 컬렉션을 쓴다.

## 기존 프로그래밍 요구사항

자바 코드 컨벤션을 지키면서 프로그래밍한다.
기본적으로 Java Style Guide을 원칙으로 한다.
indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
예를 들어 while문 안에 if문이 있으면 들여쓰기는 2이다.
힌트: indent(인덴트, 들여쓰기) depth를 줄이는 좋은 방법은 함수(또는 메서드)를 분리하면 된다.
3항 연산자를 쓰지 않는다.
else 예약어를 쓰지 않는다.
else 예약어를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.

---

## 사용할 클래스의 종류 및 특징 정리

### 도메인

#### Money

1. 입력금액을 저장한다.
2. 로또 횟수정보를 환산하여 저장한다.
3. 입력과정에서 예외처리를 수행한다.

#### RandomLottoGenerator

원소가 6개인 중복 없는 랜덤 ArrayList를 생성하여 반환한다.

#### Lotto

리스트 형태의 로또 일급 클래스

#### LottoCalculator

겹침 횟수를 환산하고 Rank와 매핑하고 전체 상금액을 구하여 수익률을 구한다.

#### LottoNumber

자료형 형태의 로또 숫자 클래스

#### Lottos

리스트 형태의 로또 일급 클래스를 담는 일급 클래스

#### Rank(Enum)

겹침 횟수와 해당 금액을 저장한다.
겹침 횟수를 받으면 겹침 횟수와 해당 금액을 함께 반환한다.

### 뷰

#### InputView

금액을 입력받는다.
이후 입력받은 금액을 return함.

### ResultView

횟수를 출력하고,
이 횟수만큼 반복하여 ArrayList를 출력한다.
통계와 수익률을 출력한다.

### 컨트롤러

#### Application

도메인과 뷰를 실행한다.

# 예외 처리

- 구입금액에서 빈값을 받았을 때
- 구입금액이 숫자가 아닐 때
- 구입금액이 1000원보다 작을 때
- 구입금액이 1000원 단위가 아닐 때
- 지난 주 당첨번호가 적절한 형식이 아닐 때
- 지난 주 당첨번호들 중 중복된 번호가 있을 때
- 로또 번호가 1~45 범위가 아닐때
- 지난 주 당첨번호 개수가 6개가 아닐 때

# 테스트 케이스

```text
구입금액을 입력해 주세요.
5000

5개를 구매했습니다.
[3, 14, 16, 18, 22, 40]
[4, 18, 19, 30, 31, 36]
[7, 9, 20, 22, 30, 41]
[6, 18, 20, 28, 35, 39]
[5, 13, 14, 37, 44, 45]

지난 주 당첨번호를 입력해 주세요.
3, 14, 20, 22, 30, 40

당첨 통계
---------
6개 일치 (2000000000)- 0개
5개 일치 (1500000)- 0개
4개 일치 (50000)- 1개
3개 일치 (5000)- 1개
총 수익률은 11.00입니다.(기준이 1이기 때문에 결과적으로 이득이라 더 뽑아도 된다는 의미임)
```
Comment on lines +98 to +121
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

저도 이렇게 테스트 하면 될까요?

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.

지난 주 당첨번호를 다양한 경우로 입력해보며 테스트 가능합니다

Copy link
Copy Markdown

@m-a-king m-a-king Apr 6, 2026

Choose a reason for hiding this comment

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

직접 입력하는 것보다 더 좋은 방법이 있을까요 ? 희창님도 매번 직접 실행해서 눈으로 확인하면 번거롭지 않으신가요?
리팩터링이 잦다보니 여쭤봅니당

79 changes: 79 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import domain.Lotto;
import domain.LottoCalculator;
import domain.LottoMachine;
import domain.Lottos;
import domain.Money;
import domain.RandomLottoGenerator;
import domain.Rank;
import java.util.ArrayList;
import java.util.List;
import view.InputView;
import view.ResultView;

public class Application {
private final InputView inputView = new InputView();
private final ResultView resultView = new ResultView();
private final RandomLottoGenerator random = new RandomLottoGenerator();

public void run() {
final int amount = inputView.getMoney(); //돈 받기
Money money = new Money(amount); // 돈 저장
final int number = LottoMachine.calculateTicketCount(money); // 로또 뽑는 횟수
resultView.printPurchaseCount(number);
Lottos lottos = purchaseLotto(number);
Lotto winnerNumbers = inputView.getWinnerNumbers(); //당첨번호 로또 입력
LottoCalculator calculator = calculatorResult(lottos, winnerNumbers); //당첨 결과 계산
printStatistics(calculator, money);//최종 통계 및 수익률 출력
}


public Lottos purchaseLotto(int count) {
List<Lotto> purchased = new ArrayList<>();
for (int i = 0; i < count; i++) {
Lotto lotto = random.generate();
printLotto(lotto);
purchased.add(lotto);
}
return new Lottos(purchased);
}

public void printLotto(Lotto lotto) {
resultView.printLottoNumbers(lotto);
}

private LottoCalculator calculatorResult(Lottos lottos, Lotto winnerNumbers) {
LottoCalculator calculator = new LottoCalculator();
for (Lotto lotto : lottos.getLottos()) {
int matchCount = lotto.getMatchNumbers(winnerNumbers);
Rank rank = Rank.MISS.valueOf(matchCount);
calculator.valueAdd(rank);
}
return calculator;
}

public void printStatistics(LottoCalculator calculator, Money money) {
resultView.printStatics();
for (Rank rank : Rank.values()) {
AddWinningMoney(calculator, rank);
}
double yield = calculator.calculateYield(money);
resultView.printYield(yield, yield >= 1.0);
}

public void AddWinningMoney(LottoCalculator calculator, Rank rank) {
if (rank != Rank.MISS) {
resultView.printWinningStatics(
rank.getMatchnumbers(),
rank.getPrizemoney(),
calculator.getResult().get(rank)
);
}
}

public static void main(String[] args) {
Application lotto = new Application();
lotto.run();

}

Comment on lines +76 to +78
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

리뷰하는 입장에서는 이런 부분들이 자꾸 신경쓰이네요 ㅋㅋㅋ 😄 사실 별 것 아니지만 수정 부탁드립니다~

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.

넵넵

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

import java.util.List;

public class Lotto {
public static final int LOTTO_NUMBER_COUNT = 6;
private final List<LottoNumber> lottoNumbers;

public Lotto(List<LottoNumber> lottoNumbers) {
validate(lottoNumbers);
this.lottoNumbers = lottoNumbers;
}

private void validate(List<LottoNumber> numbers) {
validateSize(numbers);
validateDuplicate(numbers);
}

private void validateSize(List<LottoNumber> numbers) {
if (numbers.size() != LOTTO_NUMBER_COUNT) {
throw new IllegalArgumentException("로또 숫자 개수가 " + LOTTO_NUMBER_COUNT + "개여야 합니다.");
}
}

private void validateDuplicate(List<LottoNumber> numbers) {
long distinctCount = numbers.stream()
.distinct()
.count();

if (distinctCount != numbers.size()) {
throw new IllegalArgumentException("중복된 로또 숫자가 존재합니다.");
}
}

public List<LottoNumber> getLotto() {
return lottoNumbers;
}
Comment on lines +35 to +37
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

로또를 get하는데, 왜 로또 넘버가 나올까요?

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는 클래스 명을 따르며 6개 숫자를 묶은 로또 한개를 의미했고 변수설정을 위에서 numbers로 했는데 이것도 6개 숫자를 묶은 로또 한개를 의미했습니다. 다른 사람이 보기에 너무 헷갈릴 것 같아
private final List<LottoNumber> numbers;
를 말씀 주신 대로
private final List<LottoNumber> lottoNumbers;로 변수명을 설정해보는 것이 좋아보입니다.
혹시나 클래스명도 LottoNumbers로 바꿀 필요가 있을까요..?
저는 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.

아하 제 질문 의도는 getNumbers 혹은 getLottoNumbers가 맞지 않을까? 입니다 ㅎㅎ

취향 차이이긴 한데, 저는 클래스 이름이 이미 문맥을 설명하고 있다면 필드명이나 메서드명에서 그 이름을 반복하지 않는 편이 더 좋더라고요~ 그래서 getNumbers가 가장 좋다고 생각해요!

Comment on lines +35 to +37
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.getLotto()를 호출하고, 로또 넘버를 바꿀 수 있는 상황에 대해서 어떻게 생각하시나요?

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.

(현재 numbers를 lottoNumbers로 변수명 바꿈)
Lotto.java에서는 private final List lottoNumbers;
LottoNumber.java에서는 private final int number;로 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.

final은 이 변수가 가리키는 주소를 바꿀 수 없다는 뜻입니다! 그래서 리스트를 바라보는 참조를 변경할 수는 없어요.
그치만 리스트 안의 요소를 변경할 수 있습니다. add, remove, clear 같은 조작이 가능해요~
여기서 헷갈릴 수 있는 부분이 있는데, 리스트 안의 LottoNumber도 final입니다. 그래서 내부적인 int 값은 못 바꿔요. 하지만 이건 그 객체의 값 변경을 막는 거고, 리스트에서 그 객체를 빼거나 넣는 건 리스트의 동작이라 별개입니다. 즉, 요소 자체를 삭제하거나 추가하는 것은 막을 수 없어요.

이런 동작을 막기 위해서는 어떻게 해야할까요? 학습해보시면 좋을 것 같습니다!


public int getMatchNumbers(Lotto winnerNumbers) {
return (int) lottoNumbers.stream()
.filter(winnerNumbers::contains)
.count();
}

public boolean contains(LottoNumber number) {
return lottoNumbers.contains(number);
}
}
31 changes: 31 additions & 0 deletions src/main/java/domain/LottoCalculator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package domain;

import java.util.LinkedHashMap;
import java.util.Map;

public class LottoCalculator {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

로또 계산기는 무엇을 하나요?

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.

겹침 횟수에 따라 상금을 누적하는 역할을 합니다. 이부분도 저에게 생소한 Enum관련된 로직이 있어 설명이 안되네요...

private final Map<Rank, Integer> result;

public LottoCalculator() {
result = new LinkedHashMap<>();
for (Rank rank : Rank.values()) {
result.put(rank, 0);
}
}

public void valueAdd(Rank rank) {
result.put(rank, result.get(rank) + 1);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

+1 이 없으면 가독성이 더 좋아지지 않을까요 ?

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.

앗 제가 Enum부분을 처음 다뤄봐 마땅한 해결책은 모르겠네요..

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 관련 학습을 진행하면서 수정해봐도 좋을 것 같아요~
미리 학습해보셔도 좋습니다.

}

public double calculateYield(Money money) {
double totalPrize = 0;
for (Rank rank : Rank.values()) {
totalPrize += rank.getPrizemoney() * result.get(rank);
}
return totalPrize / money.getAmount();
}

public Map<Rank, Integer> getResult() {
return result;
}
}
17 changes: 17 additions & 0 deletions src/main/java/domain/LottoMachine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package domain;

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

public static int calculateTicketCount(Money money) {
int amount = money.getAmount();
validateLottoUnit(amount);
return amount / LOTTO_PRICE;
}

private static void validateLottoUnit(int amount) {
if (amount < LOTTO_PRICE || amount % LOTTO_PRICE != 0) {
throw new IllegalArgumentException("로또는 1000원 단위로만 구매 가능합니다.");
}
}
}
44 changes: 44 additions & 0 deletions src/main/java/domain/LottoNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package domain;

public class LottoNumber {
public static final int MAX_NUMBER = 45;
public static final int MIN_NUMBER = 1;
private final int number;

public LottoNumber(int number) {
validatorNumber(number);
this.number = number;
}

public void validatorNumber(int number) {
if (number < MIN_NUMBER || number > MAX_NUMBER) {
throw new IllegalArgumentException("로또 번호가 범위를 벗어났습니다.");
}
}

public int getNumber() {
return number;
}

@Override
public String toString() {
return String.valueOf(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 java.util.Objects.hash(number);
}
}
15 changes: 15 additions & 0 deletions src/main/java/domain/Lottos.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package domain;

import java.util.List;

public class 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.

Lottos 클래스가 더 많은 일을 할 수 있을 것 같아요! 30줄 이상으로 만들어보는 미션을 드리고 싶어요~

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.

아앗.. 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.

음... 사용자가 돈을 내면 로또가 하나 이상 생성되잖아요? 그 생성부터 결과 확인까지, 여러 장의 로또를 가지고 해야 하는 일들을 생각해보세요. 기존 클래스를 삭제해도 좋습니다!

private final List<Lotto> lottos;

public Lottos(List<Lotto> lottos) {
this.lottos = lottos;
}

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

public class Money {
private final int amount;

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

private void validatePositive(int amount) {
if (amount < 0) {
throw new IllegalArgumentException("금액은 음수일 수 없습니다.");
}
}

public int getAmount() {
return amount;
}
}
Loading