Skip to content

[그리디] 김하은 로또 미션 1,2단계 제출합니다.#155

Merged
boorownie merged 17 commits intonext-step:haeun92e0from
haeun92e0:haeun-lotto
Apr 2, 2026
Merged

[그리디] 김하은 로또 미션 1,2단계 제출합니다.#155
boorownie merged 17 commits intonext-step:haeun92e0from
haeun92e0:haeun-lotto

Conversation

@haeun92e0
Copy link
Copy Markdown

🙋‍♂️ 인사

다빈님 안녕하세요! 백엔드 4기 김하은입니다. 자바를 본격적으로 다루는 것이 처음이라 이번 미션도 쉽지 않았습니다 ㅠㅡㅠ 그래도 미션에서 주어진 제약 조건을 지키고 흐름을 이해하기 위해 노력했습니다. 리뷰 남겨주심에 정말 감사드리며 잘부탁드립니다!

🚗 1, 2단계 - 로또 자동 구매 및 당첨 확인

사용자가 입력한 금액만큼 로또를 자동으로 생성하고, 당첨 번호와 비교해서 당첨 결과와 수익률을 계산하는 프로그램을 구현했습니다.

🤔 고민한 내용

메서드 10라인 제한
처음에는 한 메서드 안에 로직을 다 넣는 게 더 편했는데, 10라인 제한 때문에 메서드를 계속 나누게 되었습니다. 자동차 미션에서도 메서드를 나누는 것이 너무 어려웠는데 이번 미션에서는 메서드 이름을 어떻게 지어야 역할이 잘 드러나는지 고민을 많이 했던 것 같습니다.

Enum을 활용한 조건문 제거
3개, 4개, 5개, 6개 일치 시 각각의 상금을 if-else로 처리하면 코드가 매우 지저분해질 것 같았습니다. 이를 Rank라는 Enum으로 관리하고, stream의 filter를 사용하여 일치하는 등수를 찾아내도록 구현함으로써 가독성을 높이고 유지보수를 쉽게 만들었습니다.

❓ 질문사항

현재는 도메인 로직 중심으로 테스트를 작성했습니다. InputView나 OutputView처럼 입출력과 관련된 부분은 테스트하기가 어려운데, 이런 부분도 테스트를 하는지 궁금합니다.
또한, 지금은 잘못된 입력이 들어오면 IllegalArgumentException을 던지고 프로그램이 종료되도록 구현했습니다. 실제 서비스에서는 이런 경우 사용자에게 다시 입력을 받도록 처리하는 게 일반적인지 궁금합니다.

Copy link
Copy Markdown

@70825 70825 left a comment

Choose a reason for hiding this comment

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

안녕하세요 하은님 저도 잘부탁드려요 🙇 

현재는 도메인 로직 중심으로 테스트를 작성했습니다. InputView나 OutputView처럼 입출력과 관련된 부분은 테스트하기가 어려운데, 이런 부분도 테스트를 하는지 궁금합니다.

음.. 이거는 사람마다 생각이 좀 다를 수도 있을 것 같은데요. 저는 View는 굳이 안해도 된다고 생각해요
우선 저는 View의 경우, 실행하면 바로 눈으로 볼 수 있는 영역 + View 코드는 보통 간단해서 코드만 읽어도 되므로 굳이 테스트 코드를 작성 안해도 된다고 생각해요
그런데 이거는 View도 테스트를 자동화하면 편하니 테스트 하는게 좋다고 생각하는 분들도 있긴합니다. 저는 어차피 게임 실행해서 잘 돌아가는지 확인하면서 View 영역도 확인할텐데, 굳이 테스트 코드까지 작성할 이유는 있나 싶은 귀찮음의 영역이 커서요 ㅋㅋㅋ..

  • 완벽한 테스트 자동화를 원한다 : View 도 테스트하는게 좋음
  • 적당한 테스트 자동화를 원한다 : View는 굳이 안해도 문제 없음

또한, 지금은 잘못된 입력이 들어오면 IllegalArgumentException을 던지고 프로그램이 종료되도록 구현했습니다. 실제 서비스에서는 이런 경우 사용자에게 다시 입력을 받도록 처리하는 게 일반적인지 궁금합니다.

네 보통 화면에서 잘못된 값이 있으면 수정하라는 표시를 해두는 편이에요 (ex. 특정 범위 값을 넘어선 값을 입력하면 해당 입력칸을 빨간색 테두리로 바꿔서 다시 입력해달라는 내용을 보여주기)
그런데 재입력처리는 백엔드 영역이 아니라서 크게 신경 쓸 부분은 아니라 하은님이 해보고 싶으면 재시도 로직을 만들어보셔도 좋고, 아니면 아닌대로 진행하셔도 상관 없어보여요


추가로 구현하면 좋은 점

이전 자동차 경주 미션에서 1~10 사이의 숫자가 나올 때 전진하는 코드를 전략패턴을 이용해서 구현했었는데요

시간이 남으시면 해당 미션도 전략 패턴을 사용해서 만들어보는 것도 좋다는 생각이 들어요~

Comment on lines +8 to +16
public static String inputMoney() {
System.out.println("구입금액을 입력해 주세요.");
return scanner.nextLine();
}

public static String inputWinningNumbers() {
System.out.println("지난 주 당첨 번호를 입력해 주세요.");
return 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.

질문

InputView에서는 Integer나 List를 반환 할 수도 있어 보이는데 String으로 처리해주신 이유가 있나요?

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.

View의 역할에 대한 고민이 있었습니다. view에서 입력값이 숫자인지, 콤마로 구분된 리스트인지 판단하고 변환하는 로직은 비지니스 로직에 가깝다고 판단했고 그래서 view는 사용자가 입력한 문자열을 그대로 전달하는 역할만 맡도록 짰습니다.

import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class LottoGame {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Application.java와 LottoGame.java를 분리해두면 좋을 것 같은데요
static이 어떤 역할을 수행하는지, 어떤 상황에 사용하는게 좋을까요??

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.

static은 클래스가 메모리에 로드될 때 한번 생성되며, 객체 생성 없이도 사용할 수 있게 해줍니다. static은 상태 없이 기능만 제공하는 클래스에서 사용하기 좋고 모든 객체가 공유해야하는 상수일 때 사용하면 좋습니다.
리뷰어님의 제안대로 application은 프로그램 시작점의 역할만 수행하고 lottogame은 게임의 흐름을 관리하는 객체로 분리하여 역할을 명확히 해보겠습니다.


import java.util.Objects;

public class LottoNumber implements Comparable<LottoNumber> {
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로도 미션 진행할 수 있었을텐데 LottoNumber를 따로 만든 이유가 무엇인지 궁금해요

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.

int를 그대로 썼다면 번호를 다루는 모든 곳에서 이 숫자가 1에서 45사이인지를 매번 확인해야했지만 LottoNumber 클래스 안에서 검증하게 함으로써 다른 클래스들이 번호의 유효성을 의심하지 않고 자신의 로직에 집중하게 됐습니다. 역할을 나누니 코드가 단순해지고 각 클래스의 역할이 명확해 진 것 같습니다.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

좋습니다~

Comment on lines +21 to +32
@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 Objects.hash(number);
}
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.

네 이 코드를 짜면서 자바의 동일성과 동등성의 차이에 대해 학습했습니다. 동일성은 ==연산자로 두 객체의 메모리 주소가 같은지를 확인하는 것이고 동등성은 equals 메서드를 통해 두 객체가 가진 값이 같은지를 확인하는 것입니다. 로또 게임에서는 서로 다른 위치에 저장된 객체들이어도 숫자가 같다면 같은 번호로 취급해야하므로 equals를 재정의했습니다.


import java.util.List;

public class LottoTickets {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

일급컬렉션은 만들어졌는데, 이 객체가 주도적으로 할 수 있는 책임은 따로 없는 것 같아요 ㅠㅠ
LottoTickets을 의인화해서 혼자 주도적으로 움직이는 객체라고 생각하면 다른 클래스에 있는 어떤 메서드를 여기 안으로 가져올 수 있을까요??

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

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.

모든 클래스들을 볼 때 LottoGame은 MVC 패턴중 Controller라는 생각이 드는데요
컨트롤러는 어떤 역할을 한다고 생각하시나요??

힌트: 너무 많은 책임이 LottoGame에 몰려있음

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.

제가 생각하는 컨트롤러의 역할은 전체적인 흐름을 제어해주는 중재자입니다. View를 통해 데이터를 받고 이를 모델에 전달하며, 비지니스 로직을 직접 수행하는 것이 아니라 모델에게 실행을 요청하고, 모델이 돌려준 결과를 다시 view에게 전달하여 사용자에게 보여줍니다.
리뷰어님의 힌트를 통해 LottoGame이 너무 많은 비지니스 로직을 수행하고 있다는 점을 깨달았습니다.
로직들을 최대한 도메인 내부로 이동시켜 리팩토링 해보겠습니다.

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. LottoFactory와 LottoMachine은 객체명에서 서로 어떤 차이가 있는지 모르겠어요. 이름만 보면 둘 다 로또 생성하는 목적인 객체로 보이네요
  2. 고민해봤는데 로또 생성의 경우에는 LottoTickets, Lotto에 두어도 괜찮다고 생각하는데 어떻게 생각하시나요? 현재는 생성자만 사용하고 있는데 정적 팩토리 메서드를 활용해도 좋아보여요

Comment on lines +10 to +15

class LottoTest {

@Test
@DisplayName("로또 번호가 6개가 아니면 예외가 발생한다.")
void invalidLottoSize() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

참고로 영문 메서드명을 만들기 귀찮으시면 위처럼 설정해서 한글 테스트명만 추가할 수 있습니다~
아마 한글 + 영문 메서드 둘 다 적어야해서 귀찮은 분들도 많을거라 스터디 시간에 이야기 해보셔도 좋아보여요

Suggested change
class LottoTest {
@Test
@DisplayName("로또 번호가 6개가 아니면 예외가 발생한다.")
void invalidLottoSize() {
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@SuppressWarnings("NonAsciiCharacters")
class LottoTest {
@Test
void 로또번호가_6개가_아니면_예외가_발생한다() {

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.

우왓 좋은 팁 감사합니다!

@haeun92e0 haeun92e0 changed the base branch from main to haeun92e0 March 31, 2026 12:45
Copy link
Copy Markdown

@70825 70825 left a comment

Choose a reason for hiding this comment

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

고생하셨어요 하은님~
테스트 코드도 깔끔하고, 로직도 잘 작성해주셔서 한 번만 더 진행하고 머지하시지요 🚀🚀

}

private void validate(List<LottoNumber> numbers) {
if (numbers.size() != 6) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

여기도 규칙성 있게 6을 상수로 만들면 좋아보이네요~

import java.util.Map;

public class LottoResult {
private final Map<Rank, Long> result;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

여기 EnumMap을 사용해봐도 좋아보여요~

Copy link
Copy Markdown

@70825 70825 left a comment

Choose a reason for hiding this comment

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

고생하셨어요 하은님~

Image

머지된 기념으로 클래스들을 살펴보면 더 이상 제거가 불가능한 꼭 필요한 객체들만 남아있음을 알 수 있는데요
지금처럼 응집도 있게 도메인 로직을 넣어두면 모르는 사람들도 클래스명만 보고도 대충 게임이 어떻게 돌아갈지 쉽게 파악이 가능하다는 장점이 있습니다

그러면 드는 생각이 처음부터 잘 설계하는 방법이 무엇일까라는 생각도 드실 수 있는데요. 저는 직접 어떤 객체를 만들지, 그 객체 안에 어떤 메서드를 추가할지 그려봤던 편이에요

이후 어느정도 완성이 됐으면 여기서 없어도 되는 객체는 무엇일까?, 이 객체가 좀 더 주도적으로 행동한다고 하면 어떤 메서드를 가질 수 있을까?를 고민해보면서 수정하다보면 좋은 설계가 될거라고 생각합니다~

다음 미션도 화이팅이에요~ 🚀🚀

Comment on lines +12 to +19
public void run() {
int money = Integer.parseInt(InputView.inputMoney());
LottoTickets tickets = purchase(money);
OutputView.printTickets(tickets);

Lotto winningLotto = askWinningLotto();
showResult(tickets, winningLotto, money);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이제 LottoGame을 다시 보면 중요 로직은 도메인에 있기 때문에 컨트롤러는 마치 관리자처럼 객체에게 일을 시키는 존재로 변하게 되는 것을 알 수 있습니다

여기서 좀 더 깔끔한 코딩을 가져가자면 View와 Controller 사이에 String 대신 Integer, List로 변환해주는 클래스도 있으면 좋다는 생각이 드네요. 그러면 컨트롤러는 데이터 가공할 필요도 없이 완전히 객체들의 행동을 연결시키는 역할만 수행할 것 같아요

@boorownie boorownie merged commit 940c419 into next-step:haeun92e0 Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants