Skip to content

[로또] 박민욱 미션 제출합니다.#829

Open
JohnPrk wants to merge 10 commits intowoowacourse-precourse:mainfrom
JohnPrk:main
Open

[로또] 박민욱 미션 제출합니다.#829
JohnPrk wants to merge 10 commits intowoowacourse-precourse:mainfrom
JohnPrk:main

Conversation

@JohnPrk
Copy link

@JohnPrk JohnPrk commented Nov 3, 2025

🎰 로또 게임 (Lotto Game)

사용자가 입력한 금액만큼 로또를 자동으로 발행하고, 당첨 번호와 보너스 번호를 기준으로 결과 통계와 수익률을 출력하는 콘솔 기반 프로그램입니다.

Java 21 환경에서 동작하며, 기능별 책임을 분리해 구현하는 데 중점을 두었습니다.



🍭 🎯 프로젝트의 지향점 및 방향

관련된 데이터와 동작을 함께 묶어 각 클래스가 분명한 책임을 갖도록 구성하고, 여러 객체들이 역할에 따라 협력하며 하나의 기능을 완성할 수 있도록 설계합니다.

TDD 사이클 중 Inside-Out 방식을 기반으로, 입출력보다는 먼저 도메인 로직을 중심으로 개발을 시작하고, 테스트 가능한 작은 단위부터 점차 확장해 나갑니다.



🔁 시퀀스 다이어그램

애플리케이션의 전체 동작 과정과 객체 간의 상호작용을 시각화하기 위해 시퀀스 다이어그램을 사용했습니다.

sequenceDiagram
    participant U as 사용자
    participant I as InputView
    participant O as OutputView
    participant C as LottoController
    participant M as Money
    participant F as LottoFactory
    participant G as LottoGenerator
    participant L as Lotto
    participant W as WinningNumbers
    participant R as LottoResults

Note over U,C: 🎫 1단계: 로또 구매
    loop 유효한 금액이 입력될 때까지
        U->>I: 구입 금액 입력
        I->>C: 금액 전달
        C->>M: Money 생성 및 검증
        alt 검증 성공
            M-->>C: 유효한 Money 객체
        else 검증 실패
            M-->>C: IllegalArgumentException
            C->>O: 에러 메시지 출력
            O->>U: 에러 표시
        end
    end

    C->>M: getLottoCount() 호출
    M-->>C: 구매 가능한 로또 개수(count)

    C->>F: generate(count) 호출
    F->>G: generate(count) 위임

    loop count번
        G->>G: Randoms.pickUniqueNumbersInRange(1,45,6)
        G->>L: Lotto(numbers) 생성
    end
    G-->>F: List<Lotto> 반환
    F-->>C: List<Lotto> 반환

    C->>O: 구매한 로또 목록 출력
    O->>U: 로또 목록 표시
    		
Note over U,C: 🏆 2단계: 당첨 번호 입력
    loop 유효한 당첨 번호가 입력될 때까지
        U->>I: 당첨 번호 입력 (쉼표 구분)
        I->>C: 당첨 번호 전달
        C->>W: WinningNumbers 생성 및 검증
        alt 검증 성공
            W-->>C: 당첨 번호 객체 생성됨
        else 검증 실패
            W-->>C: IllegalArgumentException
            C->>O: 에러 메시지 출력
            O->>U: 에러 표시
        end
    end

    loop 유효한 보너스 번호가 입력될 때까지
        U->>I: 보너스 번호 입력
        I->>C: 보너스 번호 전달
        C->>W: 보너스 번호 설정 및 검증
        alt 검증 성공
            W-->>C: 최종 당첨 번호 객체 완성
        else 검증 실패
            W-->>C: IllegalArgumentException
            C->>O: 에러 메시지 출력
            O->>U: 에러 표시
        end
    end
    Note over U,C: 📊 3단계: 결과 분석 및 출력
    C->>R: LottoResults 생성 (List<Lotto>, WinningNumbers)
    loop 각 로또에 대해
        R->>R: 등수별 카운트 증가
        R->>R: 총 상금 누적
    end
    C->>R: calculateYield(구입 금액) 호출
    R-->>C: 수익률 반환
    C->>O: 당첨 통계 출력
    O->>U: 통계 표시
    C->>O: 수익률 출력
    O->>U: 수익률 표시
Loading


🧱 객체 역할 요약

객체 이름 역할(책임) 보유 값(상태) 주요 행위
Money 구입 금액을 표현하고 관련 규칙을 검증하는 값 객체 * 구입 금액 - 금액 유효성 검사, 구매 가능한 로또 수 계산
Lotto 로또 한 장을 표현하며 번호 규칙(개수, 범위, 중복)의 불변성을 보장 * 6개의 로또 번호 - 번호 정렬 및 중복 검사, 당첨 번호와 일치 개수 계산, 보너스 포함 여부 확인
LottoGenerator 로또 번호 생성 전략을 추상화하는 인터페이스 - 요청된 개수만큼 Lotto 리스트 생성
RandomLottoGenerator 랜덤 전략으로 로또 규칙에 맞는 번호를 생성 - Randoms.pickUniqueNumbersInRange()를 사용해 번호 생성, Lotto 객체 생성
LottoFactory 로또 생성 전략(LottoGenerator)에 생성 요청을 위임하는 팩토리 * LottoGenerator 구현체 - 생성기 null 검증, 생성 개수 검증, 생성기에 로또 생성 위임
WinningNumbers 당첨 번호와 보너스 번호를 가지며 관련 규칙(개수, 범위, 중복)을 책임짐 * 6개 당첨 번호, 1개 보너스 번호 - 당첨 번호 유효성 검사, 보너스 번호 유효성 검사 및 중복 확인
LottoRank 당첨 등수(1~5등, 꽝)의 조건(일치 개수, 보너스)과 상금을 정의 * Enum(일치 개수, 보너스 포함 여부, 당첨 금액) - 일치 수/보너스 포함 여부로 등수 반환, 상금 반환
LottoResults 전체 당첨 결과를 집계하고 최종 수익률을 계산 * 등수별 당첨 횟수, 누적 상금 - 각 로또에 대한 등수 계산, 당첨 결과 누적, 수익률 계산
InputView 사용자로부터 입력을 받는 모든 로직을 담당 * 입력 접두사 - 사용자로부터 입력을 받음, Console.readLine() 사용
OutputView 사용자에게 정보를 출력하는 모든 로직을 담당 * 출력 접두사 - 구매한 로또 목록 출력, 당첨 결과 출력, 수익률 출력
InputValidator 문자열 입력을 숫자/리스트로 변환하고 형식을 검증하는 유틸리티 - - 구입 금액/당첨 번호/보너스 번호 문자열 파싱, 공백 및 숫자 형식 검증
LottoController 애플리케이션의 전체 실행 흐름을 조율. View와 Domain 객체들을 연결 - 전체 흐름 제어


🚰 구현 순서 (Inside-Out 방식)

1단계: 핵심 도메인 모델 구현

비즈니스의 가장 본질적인 규칙과 데이터를 정의하고 테스트합니다.

  • Money: 금액을 표현하고, 1,000원 단위 검증 및 로또 구매 가능 개수 계산 책임을 가집니다.
  • Lotto: 로또 한 장(6개 번호)을 표현하며, 번호 개수, 범위, 중복 등 규칙의 불변성을 보장합니다.

2단계: 당첨 규칙 및 판정 로직 구현

당첨을 결정하는 기준과 로직을 구현합니다.

  • WinningNumbers: 당첨 번호(6개)와 보너스 번호(1개)를 가지며, 관련된 유효성 검증 책임을 가집니다.
  • LottoRank: 5개 등수와 '꽝'에 대한 조건(일치 개수, 보너스 여부)과 상금을 enum으로 정의합니다. (등수 판정 로직 포함)

3단계: 결과 집계 및 분석 로직 구현

구매한 로또들의 결과를 종합하여 통계를 내는 책임을 구현합니다.

  • LottoResults: 구매한 모든 LottoWinningNumbers를 비교하여, 등수별 당첨 횟수를 집계하고 최종 수익률을 계산합니다.

4단계: 로또 생성 로직 구현

도메인 규칙에 따라 로또를 생성하는 책임을 구현합니다.

  • LottoFactory: LottoGenerator를 사용해 규칙에 맞는 로또 목록을 생성합니다.

5단계: 입출력(UI) 계층 구현

사용자와 상호작용하는 부분을 구현합니다.

  • InputView: 사용자로부터 구입 금액, 당첨 번호, 보너스 번호를 입력받는 책임을 가집니다.
  • OutputView: 구매 내역, 당첨 통계, 수익률, 에러 메시지 등 모든 출력 내용을 형식에 맞게 보여주는 책임을 가집니다.
  • InputValidator: InputView를 통해 들어온 사용자 입력 값의 형식(숫자인지, 쉼표로 구분되는지 등)을 검증합니다.

6단계: 애플리케이션 통합 및 실행

구현된 모든 객체를 조립하여 프로그램을 완성합니다.

  • LottoController: 전체 애플리케이션의 실행 흐름(로또 구매 → 당첨 번호 입력 → 결과 발표)을 제어하고, 예외 발생 시 재입력 로직을 처리합니다.
  • Application: 프로그램의 시작점(main 메서드)으로, LottoController를 생성하고 실행합니다.

7단계: 리팩토링 및 최종 검토

코드 품질을 개선하고 요구사항을 최종 점검합니다.

  • ValidationUtil, Retry 등 중복 로직을 유틸리티 클래스로 추출합니다.
  • 전체 코드에 대해 프로그래밍 제약 조건(들여쓰기, 메서드 길이 등) 준수 여부를 검토합니다.


📋 예외 처리

사용자의 입력 오류로 예외가 발생하면 [ERROR] ... 로 시작하는 에러 메시지를 사용자에게 출력하고 해당 입력부터 다시 입력을 진행할 수 있도록 합니다.

예외 상황 에러 메시지
당첨 결과를 계산할 로또 목록이 null인 경우 [ERROR] 당첨 결과를 계산할 로또 목록은 null일 수 없습니다.
로또 목록에 null이 포함된 경우 [ERROR] 로또 목록에 null이 포함될 수 없습니다.
당첨 번호가 null인 경우 [ERROR] 당첨 번호는 null일 수 없습니다.
수익률 계산시 구입금액이 0 이하인 경우 [ERROR] 구입 금액은 0보다 커야 합니다.
구입 금액이 0 이하인 경우 [ERROR] 구입 금액은 0보다 커야 합니다.
구입 금액이 1,000원 단위가 아닌 경우 [ERROR] 1,000원 단위로 입력해 주세요.
로또 번호 리스트가 null인 경우 [ERROR] 로또 번호는 null일 수 없습니다.
비교할 로또 번호 리스트가 null인 경우 [ERROR] 비교할 로또가 null일 수 없습니다.
로또 번호가 6개가 아닌 경우 [ERROR] 로또 번호는 6개여야 합니다.
로또 번호에 중복이 있는 경우 [ERROR] 로또 번호는 중복될 수 없습니다.
로또 번호가 1~45 범위를 벗어나는 경우 [ERROR] 로또 번호는 1부터 45 사이여야 합니다.
보너스 번호가 1~45 범위를 벗어난 경우 [ERROR] 보너스 번호는 1부터 45 사이여야 합니다.
보너스 번호가 당첨 번호와 중복되는 경우 [ERROR] 보너스 번호는 당첨 번호와 중복될 수 없습니다.
로또 생성기가 null인 경우 [ERROR] 로또 생성기는 null일 수 없습니다.
로또 생성 개수가 0 이하인 경우 [ERROR] 로또 생성 개수는 1개 이상이어야 합니다.
입력 문자열이 null 또는 공백인 경우 [ERROR] 입력은 비어 있을 수 없습니다.
구입 금액 입력이 숫자가 아닌 경우 [ERROR] 구입 금액은 숫자여야 합니다.
당첨 번호 입력에 숫자가 아닌 값이 포함된 경우 [ERROR] 당첨 번호는 숫자여야 합니다.
보너스 번호 입력이 숫자가 아닌 경우 [ERROR] 보너스 번호는 숫자여야 합니다.


⚙️ 개발 및 기술 제약

  • 이 프로그램은 Java 21에서 실행되어야 한다.
  • 프로그램의 시작점은 Application 클래스의 main() 메서드여야 한다.
  • 제공된 build.gradle 파일은 수정할 수 없다. 외부 라이브러리를 추가하지 않는다.
  • 로또 번호 생성은 camp.nextstep.edu.missionutils.Randoms.pickUniqueNumbersInRange(1, 45, 6) 을 사용한다.
  • 사용자 입력은 camp.nextstep.edu.missionutils.Console.readLine() 으로만 받는다.
  • 프로그램 종료를 위해 System.exit()를 호출해서는 안 된다.
  • 들여쓰기는 2단계 이내로 유지한다.
  • else 문, switch/case, 삼항 연산자는 사용하지 않는다.
  • 하나의 메서드는 한 가지 일만 하도록 작성하고, 15줄을 넘기지 않도록 한다.
  • 제공된 Lotto 클래스에는 필드를 추가하지 않는다.


📝 구현 기록 / 고민 정보

구현 과정에서 배우고 고민했던 것들을 기록하는 공간입니다.



JohnPrk and others added 10 commits November 2, 2025 15:56
- 1,000원 단위 검증
- 0원 및 음수 금액 방지를 위한 검증
- 구매 가능한 로또 개수 계산
- IllegalArgumentException을 통한 도메인 예외 처리
- 위의 기능 단위 테스트 작성
- README.md 예외 처리 표에 Money 관련 예외 메시지 추가
- 로또 번호 개수(6개) 검증
- 로또 번호 범위(1~45) 검증
- 로또 번호 중복 검증
- 로또 번호 오름차순 자동 정렬 및 불변 리스트 저장
- IllegalArgumentException을 통한 도메인 예외 처리
- 위의 검증 및 기능 단위 테스트 작성
- README.md 예외 처리 표에 Lotto 관련 예외 메시지 추가
- Lotto를 활용해 당첨 번호 불변성(개수 6개, 1~45 범위, 중복 불가) 선행 검증
- 보너스 번호 범위(1~45) 및 당첨 번호와의 중복 검증
- 구매한 로또와의 일치 개수 조회
- 구매한 로또가 보너스 번호를 포함하는지 여부 확인
- [Lotto 확장] 생성자에서 null 방어
- [Lotto 확장] 번호 포함 여부 조회
- [Lotto 확장] 비교 대상 로또가 null일 때 예외 발생하도록 방어
- 로또 번호 목록을 불변 리스트로 노출하도록 변경해 캡슐화 강화
- WinningNumbersTest, LottoTest에 경계값/중복/null 검증 및 기능 단위 테스트 추가
- README.md 예외 처리 표에 Lotto/WinningNumbers 관련 예외 메시지 추가
- 1등부터 5등 및 꽝에 대한 등수 조건 정의
- 등수별 상금 제공 기능
- 일치 개수와 보너스 일치 여부 기반 등수 판정 로직 구현
- 등수 판정 및 상금 제공 기능에 대한 단위 테스트
- 보너스 번호가 등수에 미치는 영향 범위 검증
- 구매한 로또 목록과 당첨 번호를 기반으로 등수별 당첨 개수 집계
- 등수별 상금을 누적해 총 당첨 금액 계산
- 구입 금액을 인자로 받아 수익률(총 당첨금 / 구입 금액) 계산
- 로또 목록 및 각 원소, 당첨 번호에 대한 null 방어 로직과 예외 메시지 정의
- 검증 및 총 당첨금 집계, 수익률 조회 기능 관련 단위 테스트 작성
- README.md 예외 처리 표에 LottoResults 관련 예외 메시지 추가
- 로또 생성 로직을 LottoGenerator 인터페이스로 추상화
- Randoms 유틸을 사용하는 RandomLottoGenerator 구현
- LottoFactory가 LottoGenerator에 의존하도록 수정 (DIP)
- LottoFactory의 생성 개수(count) 유효성 검증
- 테스트 더블 FakeLottoGenerator 구현
- FakeLottoGenerator를 사용한 LottoFactory 단위 테스트 작성
- LottoGenerator 인터페이스의 구현 클래스 관련 단위 테스트 작성
- README.md 예외 처리 표에 LottoGenerator, LottoFactory 관련 예외 메시지 추가
- README.md 시퀀스 다이어그램 및 객체 역할 요약 추가
- InputView: 구입 금액, 당첨 번호, 보너스 번호 입력 기능 구현
- OutputView: 구매 로또, 당첨 통계, 수익률, 에러 메시지 출력 기능 구현
- InputValidator: View로부터 받은 입력의 형식(공백, 숫자) 검증 로직 분리
- InputValidator 관련 단위 테스트 작성
- README.md 예외 처리 표에 InputValidator 관련 예외 메시지 추가
- LottoController에서 구입 금액 입력 → 로또 생성 → 당첨 번호 입력 → 결과 집계 → 수익률 출력까지 전체 시나리오 제어
- 잘못된 입력, 금액, 당첨 번호, 보너스 번호에 대해 예외 메시지 출력 후 재입력 받도록 반복 처리
- Money 값을 활용해 구매 가능 로또 수 계산 및 수익률 계산 시 구입 금액 전달
- Application main에서 LottoFactory, RandomLottoGenerator, LottoController를 조립해 프로그램 실행 진입점 구성
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.

1 participant

Comments