Skip to content

[완두콩] 이지인 로또 미션 제출합니다. - #206

Open
eas-yin wants to merge 15 commits into
next-step:eas-yinfrom
eas-yin:step1
Open

[완두콩] 이지인 로또 미션 제출합니다.#206
eas-yin wants to merge 15 commits into
next-step:eas-yinfrom
eas-yin:step1

Conversation

@eas-yin

@eas-yin eas-yin commented Aug 2, 2026

Copy link
Copy Markdown

안녕하세요, 리뷰어님! 이번 로또 미션에 참여하게 된 이지인입니다.

Java는 2년 전 학교 프로그래밍 수업에서 처음 접한 게 다입니다. 이후에는 Java를 사용할 기회가 거의 없었지만, 전 미션을 하면서 조금씩 익숙해지고 있습니다.
제가 코드를 짤 때 바로 생각나는 건 바로 짜는 편이고, 뭔가 막히는 게 있거나 머리가 안 돌아가면 아이패드에 그림 그려가면서 짜려고 하는 편입니다. 그래서 간단하게 따로 설명해 드리겠습니다!

단계별 과정

1단계 - 먼저 하나의 메서드가 너무 많은 역할을 수행하지 않도록 기능을 분리했습니다. 로또 번호 생성 과정은 리스트, 셔플, 선택, 정렬의 단계로 나누어 각각 메서드로 분리하였습니다.
초기에는 main 메서드에서 입력, 로또 생성, 출력까지 모두 수행했지만, 메서드의 길이가 길어지고 역할이 많아져 run() 메서드와 가격 입력 부분을 별도의 메서드로 분리하였습니다.

이후 코드의 역할을 명확하게 하기 위해 MVC 패턴으로 리팩터링하였습니다.

2단계 - 당첨 번호와 로또 번호를 비교하여 일치 개수를 계산하는 기능은 LottoResult 클래스로 분리하였습니다.
여러 장의 로또를 구매하기 때문에 각 로또 번호를 List<List> 형태로 저장하였으며, 각 로또마다 일치하는 번호의 개수를 계산한 뒤 그 결과를 counts 리스트에 저장하도록 구현했습니다.
수익률 계산은 당첨금 / 구입 금액으로 계산하였으며, 출력 형식에 맞추기 위해 소수점 둘째 자리까지 출력하도록 구현했습니다.

처음에는 당첨금 계산과 수익률 계산을 하나의 메서드에서 처리하려고 했지만 메서드의 길이가 길어지고 역할이 많아졌습니다. 이를 개선하기 위해 WinningRate 클래스를 별도로 생성하였고, 당첨금 계산과 수익률 계산을 각각의 메서드로 분리하였습니다. 또한 counts에는 각 로또의 일치 개수만 저장되어 있으므로, 일치 개수별 당첨금을 계산하는 메서드를 별도로 두어 총 당첨금을 계산한 후 최종 수익률을 계산하도록 구현했습니다.

모든 원시값과 문자열 포장, 일급 컬렉션 적용 요구사항은 구현하지 못했습니다. 어려웠던 점은 아래에 작성하겠습니다.

3단계 - 기존에는 각 로또마다 당첨 번호가 몇 개 일치하는지만 계산하였기 때문에, 먼저 보너스 볼을 입력받을 수 있도록 입력 기능을 추가하였습니다. 이후 각 로또의 일치 개수를 계산한 뒤, 5개가 일치한 경우에만 보너스 볼까지 함께 비교하여 2등을 판별하도록 구현하였습니다.
또한 처음에는 Enum을 바로 적용하는 것이 익숙하지 않아 먼저 기능이 정상적으로 동작하도록 구현한 뒤, 마지막에 Enum을 적용하여 등수별 일치 개수와 상금을 한 곳에서 관리하도록 리팩터링하였습니다.

4단계 - 이번 단계에서는 원시값 포장과 일급 컬렉션에 대한 힌트를 먼저 이해하려고 하였습니다. 힌트만 읽었을 때는 어떤 구조로 변경해야 하는지 잘 이해되지 않아 AI를 활용하여 힌트의 의미를 먼저 찾아보았습니다.

이후 기존에 Integer를 사용하던 부분을 LottoNumber로 변경해 보았지만, 단순히 자료형만 변경해서는 해결되지 않았습니다. 서로 다른 타입이기 때문에 기존 코드와 맞지 않아 실행되지 않았고, 여러 부분에서 오류가 발생하였습니다.

우선 힌트를 제외한 기능은 정상적으로 동작하도록 구현한 후, 요구사항에 맞추기 위해 LottoNumber클래스를 새로 생성하였습니다. 다만 힌트에서 요구하는 구조와 기존 코드의 구조가 많이 달라 바로 적용하기 어려웠고, LottoNumber를 적용하는 과정에서 기존 로직과 충돌하여 현재는 실행되지 않는 상태입니다. 따라서 피드백과 도움을 얻고 일급 컬렉션 형태로 리팩터링하면서 LottoNumber를 함께 적용하는 방향으로 개선할 계획입니다.

어려웠던 점 (궁금한 점)

  1. 처음에 그냥 메서드로 나누기만 했는데 mvc 패턴을 써야 할지 고민했습니다. mvc 패턴은 필수로 해야 하는 건가요? 그리고 처음 코드 짤 때부터 mvc 패턴을 적용해서 나눠서 코드를 짜야 하는지 다 짜고 분리를 해야 하는지 궁금합니다.

  2. 2단계 요구사항에서 모든 원시 값과 문자열을 포장하고, 일급 컬렉션을 쓰라고 했는데 잘 모르겠습니다. 일단 요구사항을 반영하지 않은 2단계 코드로 푸시했습니다. 요구사항을 지키려 했으나 하는 과정에서 일급 컬렉션을 쓰기 위해서 Purchase 클래스를 만들었는데 제대로 작성된지도 모르겠고 이걸 어떻게 다른 클래스에서 적용할 수 있는지 잘 모르겠습니다.

3.1, 2 단계를 수행하면서 Application이나 View 같이 domain이 아닌 부분은 메서드 길이가 10 이내이기 힘들었는데 전부 다 10 이내로 해야 하나요? 특히 Applicaion은 메인 메서드가 있어서 어떻게 해야 할지 모르겠습니다.

4.3단계를 수행하면서 enum을 적용하라는 요구사항이 있었는데, 아직 enum을 언제 사용하는 것이 좋은지 잘 이해하지 못했습니다. 현재는 오히려 코드만 길어진다는 느낌이 들었는데, 실제 실무에서도 이런 경우에 enum을 많이 사용하는지 궁금합니다. 또한 이번 로또 미션에서는 어떤 점 때문에 enum을 사용하는 것이 더 좋은 설계인지 설명해 주시면 많은 도움이 될 것 같습니다.

  1. 4단계에서 힌트를 제외한 기능 구현은 올바른 방향으로 진행한 것인지 궁금합니다. 힌트 적용 전까지의 코드에서 개선하면 좋을 부분이 있다면 피드백 부탁드립니다.

중점적으로 봐주셨으면 하는 부분

  1. 요구사항과 힌트에 대해 적용하려고 시도했는데 어려움을 겪었습니다. purchase 클래스와 같이 구조를 짜는 게 맞는지 봐주시면 감사하겠습니다.

  2. 기능 구현은 4단계 빼고 어느 정도 했다고 생각하는데 역할 분리가 잘 되었는지 봐주셨으면 합니다. 최대한 메서드를 최소한으로 사용하려고 했는데 잘 되었는지 궁금합니다.

  3. 힌트를 이해하기 위해 AI의 도움을 받아 방향을 찾아보았지만, 실제 코드에 적용하는 과정이 어려웠습니다. LottoNumber와 Lotto 구조를 살짝 변경하였는데 이런 식이면 괜찮을지, 어떻게 변경하면 좋을지 조언 부탁드립니다.

추가적으로 간단하게 궁금한 점

테스트 코드를 알아서 메서드마다 짜는 게 좋을까요?
코드를 짤 때 도저히 생각이 안 날 때는 어떻게 하시는지 궁금합니다.

바쁘신 와중에도 제 코드를 리뷰해 주셔서 감사합니다. 이번 미션을 진행하면서 기능 구현 자체도 어려웠지만, 객체를 어떻게 분리하고 역할을 나누어야 하는지에 대해 많이 고민해 볼 수 있는 시간이었습니다. 아직은 익숙하지 않아 적용하는 과정에서 많은 어려움을 느꼈습니다. 부족한 부분이 많지만, 리뷰어님의 피드백을 바탕으로 왜 그렇게 구현하는 것이 좋은지 이해하고 더 좋은 코드를 작성할 수 있도록 노력하겠습니다. 작은 부분이라도 개선하면 좋을 점이 있다면 편하게 말씀해 주시면 감사하겠습니다. 소중한 시간 내어 리뷰해 주셔서 다시 한번 감사드립니다. 😊

@zzaekkii zzaekkii left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

안녕하세요 지인님, 이번에 리뷰를 맡게된 최재영입니다.

3단계 커밋까지 확인해보니 보완할 부분은 있었지만, 정상 입력 기준으로 로또 생성부터 당첨 결과 계산까지 핵심 흐름은 동작하고 있었습니다 👍
다만 지금은 현재 커밋에서 ./gradlew test를 실행해보니 IntegerLottoNumber가 섞이면서 5개의 컴파일 오류가 발생하고 있네요.

말해주신 것처럼 4단계에서 일급 컬렉션이나 원시값 포장 같은 요구사항을 맞추려다 문제가 생긴 걸로 보여요.
우선 한 번에 모든 요구사항을 해결하려고 하기보다, 차근차근 하나씩 해결해가는 게 좋을 것 같습니다.

  1. 컴파일 가능한 상태로 복구하기
  2. LottoNumber가 유효한 번호를 보장하도록 구현하기
  3. 한 장의 로또를 Lotto라는 일급 컬렉션으로 표현하기

그런 의미에서 첫 번째 사이클에서는 이 세 가지를 충족하는데에만 초점을 맞춰볼까요.

그리고 질문해주신 MVC는 이번 미션의 필수 요구사항은 아닙니다.
다른 요구사항(메서드 10라인 제한 등) 적용과 책임 분리가 잘 이루어졌는지에 대한 부분들도 애플리케이션이 정상 동작하도록 만든 뒤에 다음 사이클에서 함께 개선해보는 편이 좋을 것 같습니다.


추가적으로 간단하게 궁금한 점
테스트 코드를 알아서 메서드마다 짜는 게 좋을까요?
코드를 짤 때 도저히 생각이 안 날 때는 어떻게 하시는지 궁금합니다.

생각이 안 날때 어떻게 하는가?

저는 우선 코드를 짜기 전에 지인님이 하시는 것처럼 그림을 그려보는 편인데요.

알고리즘 문제를 풀 때도 얼핏 번뜩이는 아이디어로 바로 풀 수 있을 것 같지만, 순차적인 플로우를 잘 설계해놓고 들어가는 것이 실수를 줄이고 오해가 있었던 부분을 캐치하는데 효과적이더라고요.
코드를 짜다가 막힐 때도 키보드에서 손을 떼고, 막힌 부분에 대해서 생각했던 걸 노트에 풀어내 고민해보곤 했었습니다.

요즘은 AI가 참 많이 발달해서 얼른 답을 알아내고 싶다라는 생각이 들 수도 있을 것 같아요.
다만 저는 쉽게 얻은 답은 쉽게 잊어버리게 되고, 사고과정이 체화되지 않는 것처럼 느껴지더라고요.
그래서 AI를 활용한다면, 바로 답을 알려달라고 하기 보다는 현재 막힌 상황을 알려주고 힌트만 달라는 식으로 직접 해결해보는 편이 더 좋다고 생각해요.

테스트 코드

먼저 모든 메서드에 테스트를 작성할 필요는 없습니다.
private 메서드와 같은 내부 구현보다, 외부에 약속한 동작과 객체가 지켜야 하는 규칙을 테스트하는 편이 좋습니다.

테스트 코드도 처음엔 막막하게 느껴지실텐데요.

간단히 생각해보면 처음에 “어떤 기능을 만들어야 한다”는 요구사항을 받게 되는데, 그 기능에는 입력과 기대하는 결과가 있을 겁니다.
테스트는 “~이런 입력이나 상황에서는 ~이런 결과가 나올 거야”라는 명세라고 생각하면 좋을 것 같아요.

그리고 이런 명세 자체는 실제 구현을 시작하기 전에도 작성해 볼 수 있겠죠?
구현이 끝난 다음에 테스트를 실행해보면, 처음 생각한 시나리오대로 동작하는지 확인할 수 있고, 이후에 리팩터링할 때도 기존 기능이 깨지진 않았는지 알 수 있어요.

이번 사이클에서는 우선 핵심 규칙부터 테스트해보면 어떨까요?

  • LottoNumber는 1과 45로 생성할 수 있다.
  • LottoNumber는 0이나 46으로 생성할 수 없다.
  • 같은 숫자를 가진 LottoNumber는 같은 번호로 판단한다.
  • Lotto는 정확히 6개의 번호를 가진다.
  • Lotto에는 중복된 번호가 들어갈 수 없다.

Comment thread src/main/java/domain/Lotto.java Outdated
Comment on lines +25 to +28
private List<LottoNumber> lottoPick(List<Integer> lotto) {
List<LottoNumber> lottoSix = new ArrayList<>();
for (int i = 0; i < 6; i++) {
lottoSix.add(lotto.get(i));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LottoNumber를 적용하면서 반환 타입을 바꿔보셨군요.
그런데 지금 내부에서는 여전히 Integer를 추가하고 있어서 흐름이 깨지고 있네요.

LottoNumber객체가 실제로 생성되는 지점은 어디일까요?
현재는 생성자가 private이고 팩토리 메서드도 없어서 LottoNumber를 만들 방법이 없는 것 같네요!

로또를 만드는 방식과는 별개로 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.

팩토리 메서드: 객체를 대신 만들어 주는 메서드

  • from은 다른 타입으로 변환해 객체를 생성
  • of는 주어진 값들로 객체 생성

Lotto에서는 숫자를 LottoNumber 객체로 변환하는 것이기 때문에 from을 사용하였고, Lotto 클래스도 수정하였습니다.

Comment thread src/main/java/domain/LottoNumber.java Outdated
@@ -0,0 +1,13 @@
package domain;

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

원시값 포장은 단순히 int를 필드로 옮기는 건 아닙니다.
이후에 이 객체를 보는 것 만으로도 유효한 로또 번호라는 걸 믿고 쓸 수 있도록 하는 게 큰 목적인데요.

  • 0이나 46으로도 생성할 수 있는지
  • 값이 3인 두 객체를 같은 번호라고 판단할 수 있는지
  • 번호를 오름차순으로 정렬할 수 있는지
  • 외부에서 실제로 객체를 생성할 수 있는지

이 질문을 만족하도록 생성 방법, 검증, 값 비교, 정렬 기준을 하나씩 구현하고 테스트해보면 원시값 포장의 목적을 이해하는 데 도움이 될 것 같습니다~

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.

0이나 46으로 생성할 수 있는지 - LottoNumber 유효성 검사 메서드 생성 및 Test 코드 추가하였습니다. 테스트 하는 도중 타입 부분이 컴파일 오류가 떠서 오류가 나는 부분의 타입을 수정하였습니다. 타입을 수정하면서 번호 관리를 하는 원시값 포장에 대해서 이해했습니다!

값이 3인 두 객체를 같은 번호라고 할 수 있는지 - equals로 판단하여 테스트 코드 추가하였습니다. 유효성 검사할 때 수정하지 못했던 정렬 부분 Collections.sort() 메서드도 함께 수정하였습니다.
Collections.sort()에서 Integer는 이미 Comparable을 구현하고 있고, LottoNumber은 Comparable이 따로 필요하다는 걸 학습했습니다. 따라서 comparable 을 새로 생성하였습니다.

번호를 오름차순으로 정렬할 수 있는지 - 확인하는 테스트를 추가하였습니다.

또한 지금까지 커밋 메시지 작성 시 refactor:, test: 등을 적용하지 못했는데, 이번부터는 적용하여 작성하였습니다.

외부에서 실제로 객체를 생성할 수 있는지 - 외부에서 직접 생성자를 호출하면 유효성 검사를 거치지 않은 객체도 생성할 수 있다고 생각했습니다. 따라서 객체 생성은 팩토리 메서드를 통해서만 이루어지도록 생성자를 private으로 변경했습니다. 리뷰어 님의 요구에 대한 답이 제가 이해한 방향이 맞는지 궁금합니다.

답변에 대해 수정하다 보니 원시값 포장에 대해 이해할 수 있었던 것 같습니다. 아직 완벽하진 않지만 계속 미션 하면서 적용해 보겠습니다!

Comment thread src/main/java/domain/Purchase.java Outdated
@@ -0,0 +1,17 @@
package domain;

public class Purchase {

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이고, 실제로는 어떤 역할도 수행하지 않는 상태네요.

Applicationint purchasePrice를 이 객체로 바꾼다고 생각했을 때 다음을 고민해보면 어떨까요?

  • 유효한 구입 금액은 어떤 값인가
  • 구입 가능한 로또 수는 누가 계산해야 하는가
  • 당첨 금액과 비교해 수익률을 계산할 때 원래 구입 금액은 누가 알고 있어야 하는가

이름도 구매 행위가 아니라 금액을 표현한다면 PurchaseAmount가 더 잘 어울릴 수 있을 것 같아요.
다만 먼저 LottoNumberLotto를 완성한 뒤에 건드려보는 게 좋을 듯 해요.

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.

LottoNumber를 구현하면서 원시값 포장과 객체의 책임을 나누는 방향을 조금 이해해서, 비슷한 방식으로 PurchaseAmount도 구입 금액을 표현하는 객체로 변경해 보았습니다. 생성 시 유효한 구입 금액(1,000원 이상, 1,000원 단위)인지 검증하도록 구현하고, 로또 수를 계산하는 책임도 PurchaseAmount로 이동하였습니다.

다만 수익률 계산과 관련된 부분은 잘 모르겠습니다. 구입 금액도 PurchaseAmount가 알고 있어야 할 것 같다고 생각했는데, 수익률 계산까지 PurchaseAmount의 책임으로 두는 것이 맞는지 잘 모르겠습니다.

import java.util.Collections;
import java.util.List;

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

지금은 Lotto가 이름이랑은 다르게 한 장의 로또 번호를 가지는 게 아니고, 구입 장수 계산이랑 자동 번호 생성을 담당하고 있네요.

일급 컬렉션은 단순히 List를 클래스로 감싸는 것보다, 컬렉션 전체가 지켜야 하는 규칙을 한곳에서 보장하는 데 의미가 있는데요. 사실 지금 Lotto에서 사용하고 있는 List<Integer>는 아래 같은 경우도 모두 포함할 수 있거든요.

  • 번호가 5개 또는 7개인 로또
  • 같은 번호가 중복된 로또
  • 유효하지 않은 번호가 포함된 로또

한 장의 로또를 표현하는 LottoList<LottoNumber>를 가지고, 생성될 때 번호 개수와 중복 여부를 검증하도록 만들어보면 어떨까요?

그렇게 바꿨을 때 지금의 Lotto가 담당하고 있는 자동 번호 생성과 구입 장수 계산은 각각 어디에 위치하는 게 자연스러울지도 함께 고민해보면 좋을 것 같습니다~

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에 있던 기능 메서드들은 LottoGenerator 클래스로 이동하였고, 그에 따라서 Application 클래스도 수정하였습니다.

Lotto 클래스에서 컬렉션이 규칙과 책임을 가지도록 private한 리스트를 만들었고, 생성자와 팩토리 메서드를 구현하였습니다.

번호가 5개 또는 7개인 로또 - 유효성 검증 메서드를 구현했습니다.

같은 번호가 중복된 로또 - 유효성 검증 메서드를 구현했습니다.

유효하지 않은 번호가 포함된 로또 - LottoNumber 클래스에서 이미 검증했다고 생각하여 따로 구현하지 않았습니다.

또한 LottoTest 클래스에서 각각을 테스트 하였습니다.

Comment thread src/main/java/Application.java Outdated
Comment on lines +24 to +25
ResultView.printPurchase(passiveLotto, passiveCount, autoCount);
passiveLotto.addAll(autoLotto);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

컴파일 문제를 해결한 뒤에는 수동 0장, 자동 3장이나 수동 1장, 자동 2장을 입력해서 직접 실행해보면 좋겠습니다.

지금은 수동 로또만 printPurchase()에 전달하고 출력이 끝난 뒤에 자동 로또를 합치고 있네요.
그래서 자동 구매 장수는 출력되지만 자동 번호는 출력되지 않고 있어요.

아무래도 4단계에서 기능을 구현하다가 막혀서 놓치게된 케이스 같은데, 기능을 구현한 뒤에 사용자 입력을 직접 실행하거나 테스트로 남겨두면 이런 문제를 조금 더 일찍 발견할 수 있다는 점 참고하세요!

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.

수동과 자동 번호를 모두 출력할 수 있도록 출력 순서를 바꿨습니다!

@eas-yin

eas-yin commented Aug 6, 2026

Copy link
Copy Markdown
Author

안녕하세요, 재영 님. 리뷰 정말정말 감사합니다😊

재영 님이 말씀해 주신 대로 이번에는 모든 코드와 테스트 코드가 실행 가능하게 했습니다.

추가로 수정한 부분 설명 드리겠습니다.

  • 커밋 후에 실행했더니 결과가 전부 숫자가 아닌 LottoNumber 객체를 출력하고 있어서 toString()을 오버라이드 했습니다.
  • 단계별로 구현하는 과정에서 기존에는 scanner.nextLine()이 필요했지만, inputPassiveLotto()에서 줄바꿈 문자를 처리하도록 변경하면서 inputWinning()의 scanner.nextLine()은 불필요해졌습니다. 실제 실행 중 당첨 번호 입력 전에 한 번 더 입력을 대기하는 현상을 확인하여 해당 코드를 제거하였습니다.

질문 드릴 것이 있습니다!
리뷰를 반영하면서 느낀 점이 있는데, 저는 코드를 작성할 때 '번호가 6개인지', '오름차순으로 정렬되는지'처럼 객체가 가져야 하는 규칙들이 잘 떠오르지 않습니다. 이런 규칙들을 자연스럽게 떠올리려면 어떤 방식으로 연습하는 것이 좋을까요?

꼼꼼한 좋은 리뷰 감사합니다! 남겨주신 리뷰 통해서 미션의 요구사항에 대해서 잘 배우게 됐습니다. 피드백 바탕으로 더 고민하고 개선해 보겠습니다.
감사합니다😊

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.

2 participants