Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[로또 게임] 김유선 미션 제출합니다. #624

Open
wants to merge 49 commits into
base: main
Choose a base branch
from

Conversation

useon
Copy link

@useon useon commented Nov 8, 2023

💫 javascript-lotto

미션 - 로또

🎮 프로그램 동작 순서

  1. 로또 구입금액을 입력받는다.
  2. 로또 구입개수를 출력한다.
  3. 로또를 구입개수만큼 발행한다.
  4. 발행된 로또 번호를 출력한다.
  5. 당첨 번호를 입력받는다.
  6. 보너스 번호를 입력받는다.
  7. 발행된 로또와 당첨 번호, 보너스 번호를 비교한다.
  8. 당첨 통계를 출력한다.
  9. 총 수익률을 구한다.
  10. 총 수익률을 출력한다.

✨ 기능구현목록

  • 로또 구입 금액을 입력받는 기능
    • 사용자가 구입 금액을 잘 못 입력하는 경우 throw문을 사용해 예외를 발생시키고, '[ERROR]'로 시작하는 에러 메시지를 출력하고 해당 부분부터 입력을 다시 받는 기능
      • 숫자가 아닌 문자를 입력한 경우
      • 로또 구입 금액이 1000원 미만인 경우
      • 로또 구입 금액이 1000원 단위가 아닌 경우
  • 로또 구입 개수를 구하고, 개수 만큼 로또를 발행하는 기능
  • 로또 개수와, 로또 발행 번호를 출력하는 기능
  • 당첨 번호를 입력 받는 기능
    • 사용자가 당첨 번호를 잘 못 입력할 경우 throw문을 사용해 예외를 발생시키고, '[ERROR]'로 시작하는 에러 메시지를 출력하고 해당 부분부터 입력을 다시 받는 기능
      • ','로 구분하지 않은 경우
      • 숫자 6개를 입력하지 않은 경우
      • 1~45 범위에 해당 하는 숫자가 아닌 경우
      • 숫자가 중복되는 경우
  • 보너스 번호를 입력 받는 기능
    • 사용자가 보너스 번호를 잘 못 입력할 경우 throw문을 사용해 예외를 발생시키고, '[ERROR]'로 시작하는 에러 메시지를 출력하고 해당 부분부터 입력을 다시 받는 기능
      • 숫자가 아닌 문자를 입력한 경우
      • 당첨 번호에 포함된 번호를 입력한 경우
  • 발행된 로또와 당첨 번호, 보너스 번호를 비교하여 당첨 동계를 구하는 기능
  • 총 수익률을 구하는 기능
  • 당첨 통계를 출력하는 기능
  • 총 수익률을 출력하는 기능

🔨 리팩토링목표

  • MVC 패턴에 따라 코드를 분리한다.
  • 메소드가 하나의 기능만 하도록 구현한다.
  • 불필요한 의존성이 없는 코드를 지향한다.
  • 상수화를 진행한다.

🧪 테스트구현목록

  • Lotto 클래스 테스트 코드 구현
    • 로또 번호의 개수가 6개가 넘어가면 예외가 발생한다.
    • 로또 번호에 중복된 숫자가 있으면 예외가 발생한다.
    • 로또 번호에 1 미만인 숫자가 있으면 예외가 발생한다.
    • 로또 번호에 45 초과인 숫자가 있으면 예외가 발생한다.
  • Purchaser 클래스 테스트 코드 구현
    • 구매 금액이 숫자가 아니면 예외가 발생한다.
    • 구매 금액이 1000원 보다 작으면 예외가 발생한다.
    • 구매 금액이 1000원 단위가 아니면 예외가 발생한다.
  • Bonus 클래스 테스트 코드 구현
    • 보너스 번호가 숫자가 아니면 예외가 발생한다.
    • 보너스 번호가 1보다 작으면 예외가 발생한다.
    • 보너스 번호가 45보다 크면 예외가 발생한다.
    • 보너스 번호가 로또 번호와 중복되면 예외가 발생한다.
  • Issuer 클래스 테스트 코드 구현
    • 구매한 로또 개수와 발행된 로또 개수가 같은지 확인한다.
    • 발행된 각 티켓이 6개의 요소를 가지고 있는지 확인한다.
    • 발행된 각 티켓의 요소가 전부 숫자인지 확인한다.
    • 발행된 각 티켓의 요소가 전부 숫자 1 ~ 45 범위인지 확인한다.
    • 발행된 각 티켓이 오름차순으로 정렬되었는지 확인한다.
  • Matcher 클래스 테스트 코드 구현
    • 3개 일치 개수가 정확한지 확인한다.
    • 4개 일치 개수가 정확한지 확인한다.
    • 5개 일치 개수가 정확한지 확인한다.
    • 5개 일치 개수와 보너스 볼 일치 개수가 정확한지 확인한다.
    • 6개 일치 개수가 정확한지 확인한다.
  • Calculator 클래스 테스트 코드 구현
    • 수익률이 정확한지 확인한다.(소수점 둘째자리에서 반올림)

🗂 디렉토리구조

📦__tests__
 ┣ 📜ApplicationTest.js
 ┣ 📜BonusTest.js - 보너스 번호의 유효성을 검사하는 로직을 테스트
 ┣ 📜CalculatorTest.js - 수익률 계산 로직을 테스트
 ┣ 📜IssuerTest.js - 로또 티켓 발급 로직을 테스트
 ┣ 📜LottoTest.js - 당첨 번호의 유효성을 검사하는 로직을 테스트
 ┣ 📜MatcherTest.js - 당첨 통계 생성 로직을 테스트
 ┗ 📜PurchaserTest.js - 구매 금액의 유효성을 검사하는 로직을 테스트
📦docs
 ┗ 📜README.md - 프로젝트에 대한 설명 및 사용 방법을 문서화
📦src
 ┣ 📂constants
 ┃ ┣ 📜Error.js - 에러 메시지 관리
 ┃ ┗ 📜Setting.js - 게임 설정과 관련된 상수 관리
 ┣ 📂utils
 ┃ ┗ 📜RandomNumberGenerator.js - 로또 번호를 무작위로 생성하는 함수
 ┣ 📜App.js - 애플리케이션의 시작점, Controller 클래스의 인스턴스를 생성하고 실행
 ┣ 📜Bonus.js - 사용자가 입력한 보너스 번호를 검증하고 저장
 ┣ 📜Calculator.js - 당첨 통계를 바탕으로 총 수익률을 계산
 ┣ 📜Controller.js - 프로그램의 주요 흐름을 제어하며, 각 단계별로 필요한 동작을 수행
 ┣ 📜index.js 
 ┣ 📜InputView.js - 사용자로부터 입력을 받는 역할
 ┣ 📜Issuer.js - 사용자가 구매한 만큼의 로또 티켓을 발급
 ┣ 📜Lotto.js - 사용자가 입력한 당첨 번호를 검증하고 저장
 ┣ 📜Matcher.js - 발급된 로또 티켓과 당첨 번호를 비교하여 당첨 통계를 생성
 ┣ 📜OutputView.js - 결과와 메시지를 사용자에게 출력
 ┗ 📜Purchaser.js - 사용자가 입력한 로또 구입 금액을 검증하고 저장

💌 리뷰어분들에게

  1. 요구 사항과 지난 2주차 피드백을 반영하였습니다. 적절히 반영이 되었는지 궁금합니다.
  2. 클래스 필드와 메소드의 접근 제어자를 적절히 활용하여 은닉화를 진행하였는데, 적절한 은닉화가 이루어졌는지 궁금합니다.
  3. MVC 패턴을 적용하여 코드의 구조를 개선하였습니다. MVC 패턴이 잘 적용되었는지, 추가 개선 사항이 있는지 궁금합니다.
  4. 코드의 가독성과 효율성을 높이기 위해 다양한 방법을 시도하였습니다. 이 부분에 대한 리뷰어님의 생각이 궁금합니다.
  5. 테스트 코드의 단위를 적절히 나누려 노력하였습니다. 테스트 코드의 단위나 방식에 대한 추가적인 조언 부탁드립니다.
  6. 이 외에도 어떤 점이 좋았다! (칭찬 너무 좋아합니다✨) 어떤 점에 이러한 개선이 필요한 것 같다! 등등 모든 코멘트 다 감사합니다 😊

로또 미션도 다들 고생많으셨어요! 소중한 리뷰 정말 감사드립니다 ✨🙇‍♂️ 남겨주신 리뷰 덕분에 코드에 대해 더 많이 생각하고 고민하면서 성장할 수 있는 것 같아요. 마지막까지 함께 으쌰으쌰해봐요 파이탱!!!!!!! 🔥🔥🔥💪

useon added 30 commits November 4, 2023 16:24
useon added 19 commits November 8, 2023 16:20
-string 형식에서 공백을 없애고, 콤마를 기준으로 각 요소가 숫자인 배열로 변환한다
- 발행된 각 티켓이 오름차순으로 정렬되었는지 확인한다.
Copy link

@hojkim77 hojkim77 left a comment

Choose a reason for hiding this comment

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

코드리뷰 남겼습니다! 좋은 코드 감사합니다. :)

Copy link

Choose a reason for hiding this comment

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

정말 좋은 README네요,, 배우고 갑니다. 저도 마지막주차는 더 상세한 기능 목록 작성해봐야겠어요!

Copy link
Author

Choose a reason for hiding this comment

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

도움이 되었다니 저도 기분이 좋습니다 😊👍

Comment on lines +29 to +30
this.#tickets = await this.#getLottoTicketList();
this.#displayLottoTicket(this.#tickets);
Copy link

Choose a reason for hiding this comment

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

저는 그냥 입력과 관련된 변수명을 지었는데 이렇게 프로그램과 맞는(ticket)변수명을 사용하는게 더 좋아보이네요! 좋은 코드 감사합니당

Comment on lines +85 to +89
#validateWinningNumbers(inputWinningNumbers) {
if (!inputWinningNumbers.includes(SYMBOL.separator)) {
throw new Error(WINNING_NUMBER.withoutComma);
}
}
Copy link

Choose a reason for hiding this comment

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

해당 validate는 Controller에 작성하신 이유가 있나용?

Copy link
Author

Choose a reason for hiding this comment

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

  // TODO: 이 테스트가 통과할 수 있게 구현 코드 작성
  test('로또 번호에 중복된 숫자가 있으면 예외가 발생한다.', () => {
    expect(() => {
      new Lotto([1, 2, 3, 4, 5, 5]);
    }).toThrow('[ERROR]');
  });

로또 번호 입력을 받을 시 무조건 string으로 들어오는데 이 테스트 코드에서 Lotto는 매개변수로 배열을 받는 것을 확인할 수 있습니다.
원래라면 Lotto에서 같이 검증을 했을 것 같은데 LottoTest와 같이 Lotto의 매개변수를 배열로 넘겨받고자 이 검증을 여기서 처리를 하게 되었습니다.
저도 만들면서 더 좋은 방법이 없을까 고민하고 있던 부분이라 궁금해 하실 것 같았어요 😂
혹시 더 좋은 방법이 있다거나 개선 방향을 추천해주셔도 좋아요 🤩👍

Comment on lines +96 to +102
async #getMatchStatus(tickets, winningNumbers, bonusNumber) {
return new Matcher(tickets, winningNumbers, bonusNumber).matchStatus;
}

async #getRateOfReturn(purchaseAmount, matchStatus) {
return new Calculator(purchaseAmount, matchStatus).rateOfReturn;
}
Copy link

Choose a reason for hiding this comment

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

이 부분들에 대해 static을 사용하지 않고 인스턴스를 생성하신 이유가 있나요?!

Copy link
Author

Choose a reason for hiding this comment

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

말씀대로 static을 사용해서 인스턴스를 생성하지 않는 방법으로 처리를 할 수있는 부분이라고 생각합니다 😂

Copy link

@teawon teawon left a comment

Choose a reason for hiding this comment

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

잘 짜여진 코드라고 생각합니다.
코드 보면서 많이 배우고갑니다~~

await method.call(this);
} catch (error) {
OutputView.printMessage(error.message);
await this.#handlerErrorAndProceed(method);
Copy link

Choose a reason for hiding this comment

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

에러가 발생했을 때 입력을 다시받는 로직을 따로 모듈화한 접근방식이 인상깊네요!

Copy link
Author

Choose a reason for hiding this comment

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

메소드 15줄 제한으로 분리에 대해 고민을 하다보니 이렇게 처리를 해볼 수 있겠더라고요! 좋게 봐주셔서 감사합니다 😊

@@ -0,0 +1,5 @@
import { Random } from '@woowacourse/mission-utils';

const RandomNumberGenerator = () => Random.pickUniqueNumbersInRange(1, 45, 6);
Copy link

Choose a reason for hiding this comment

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

함수명이 랜덤 정수를 생성한다인데 실제로는 로또 번호에 사용할 1~45 사이의 6자리수를 생성하는거니,
이름을 더 명확히 표현하는것도 좋을 것 같습니다!

Copy link
Author

Choose a reason for hiding this comment

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

말씀대로 이름을 더 정확히 표현하는 게 좋을 것 같아요 감사합니다 😊

const issuedLottoList = [];
while (issuedLottoList.length < this.#lottoCount) {
const sortedRandomNumbers = RandomNumberGenerator().sort((a, b) => a - b);
issuedLottoList.push(sortedRandomNumbers);
Copy link

Choose a reason for hiding this comment

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

여기서 각 LottoList들은 결국 각 Lotto 도메인 로직을 가지고 있는데
단순히 문자열을 저장하기보다, 반환받은 문자열로 Lotto인스턴스들을 생성후 반환하도록 하는 것도 좋을 것 같아요

Copy link
Author

Choose a reason for hiding this comment

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

이런 방법도 생각해볼 수 있군요! 다음 번 작성에는 이런 부분을 더 신경써볼게요 😊

const matchedNumberCount = ticket.filter((number) => winningNumbers.includes(number)).length;
const isBonusMatch = ticket.includes(bonusNumber);
const keyOfPrizeTable =
isBonusMatch && matchedNumberCount === 5 ? '5+bonus' : matchedNumberCount;
Copy link

Choose a reason for hiding this comment

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

5+bonus 부분은 상수로 분리해도 괜찮아 보이네요!

Copy link
Author

Choose a reason for hiding this comment

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

이 부분을 상수로 분리한다는 것이 .. 놓친 것 같아요 😂

#generate() {
const issuedLottoList = [];
while (issuedLottoList.length < this.#lottoCount) {
const sortedRandomNumbers = RandomNumberGenerator().sort((a, b) => a - b);
Copy link

Choose a reason for hiding this comment

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

저도 놓쳤던 부분인데 a b대신 좀더 구체적인 네이밍이 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

말씀대로 a, b 보다 구체적인 네이밍이 좋을 것 같네요!!

@@ -0,0 +1,19 @@
const PURCHASE_AMOUNT = Object.freeze({
notNumber: '[ERROR] 구매 금액은 숫자여야 합니다.',
Copy link

Choose a reason for hiding this comment

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

[ERROR]부분은 반복되는 부분이라 따로 상수화해줘도 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

ERROR 부분을 따로 상수로 분리하는 것도 좋은 방법인 것 같아요 👍

const inputValue = await Console.readLineAsync(INFO.inputBonusNumber);
return inputValue;
},
};
Copy link

Choose a reason for hiding this comment

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

저는 반복되는 부분이라 생각하고 하나의 함수로 통일했는데, 나눠 놔도 한 눈에 보기 좋네요

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.

4 participants