Skip to content

[로또] 이유정 미션 제출합니다.#379

Open
IAGREEBUT wants to merge 31 commits intowoowacourse-precourse:mainfrom
IAGREEBUT:IAGREEBUT2
Open

[로또] 이유정 미션 제출합니다.#379
IAGREEBUT wants to merge 31 commits intowoowacourse-precourse:mainfrom
IAGREEBUT:IAGREEBUT2

Conversation

@IAGREEBUT
Copy link

[3주차] 로또

1. 구현할 기능 목록

1. 입력값 받기 : 로또 구입 금액 입력받기

2. 구매한 로또의 총 갯수를 구하고, 문구를 출력한다.

3. 구매한 로또들의 랜덤 번호 생성

  • 구매한 로또번호들의 6가지 랜덤 번호를 생성한다
  • 오름차순으로 정렬하여 저장한다
  • 구매한 로또 번호를 출력한다.

4. 당첨 번호를 입력받는다

5. 보너스 번호를 입력받는다.

6. 당첨 통계를 구한다

  • 각각의 index가 일치한 숫자의 갯수를 의미하는 길이가 8인 배열을 만든다. (index = 6은 2등을, index = 7은 1등을 의미한다.)
  • 구매한 로또들을 순회
    • 하나의 로또에서 몇개의 번호가 맞았는지 구한다.
    • 배열[일치한 갯수] 에 값을 증가시킨다.
    • 만약 5개 맞았다면, 보너스 번호 일치 여부를 확인한다
    • 만약 6개 맞았다면, index = 7에 저장한다.

7. 수익률을 계산한다

  • 배열을 사용하여 (index = 7 은 1등 ... index = 3 은 5등) 당첨금의 합을 구한다
  • 당첨금/지불한 값으로 수익률을 계산한다
  • 소수점 둘째 자리에서 반올림한다

2. 예외 케이스

1. 로또 구입 금액 입력받기

  • 1000원 단위 금액이 아닌 경우
  • 양의 정수를 입력했는가(/^[1-9]\d*$/)
  • 최대값 제한 (SAFE INTEGER 범위)

3. 구매한 로또들의 랜덤번호 생성

  • 로또 번호의 수가 6개인지 확인
  • 로또 번호에 중복이 없는지 확인

4. 로또 당첨 번호 입력

  • 당첨번호가 6개가 아닌 경우
  • 양의 정수를 입력했는가 (/^[1-9]\d*$/)
  • 당첨번호가 유효범위 내에 있지 않은 경우 (1~45의 정수가 아님)
  • 중복된 수를 입력한 경우

5. 보너스 번호 입력

  • 양의 정수를 입력했는가
  • 당첨번호가 유효범위 내에 있지 않은 경우
  • 당첨 번호와 중복되는지 여부

3. 고민한 부분

  • 지난 회차에서 미흡하다고 생각했던 Unit Test를 상세히 추가했습니다.
    특히, validation Check를 꼼꼼하게 하지 못했다고 생각해서, 검사하는 부분 및 순서에 시간을 많이 사용하였습니다. 또한, 해당 부분을 중점으로 unit test를 좀 더 꼼꼼하게 작성하였습니다.
  • 상수화에 대한 고민. 상수화를 최대한 하되, 구분이 되도록 constant.js 파일 내에서도 관련 그룹으로 묶어 용도를 명확히 하고자 노력했습니다.
  • 책임의 분리. 어떤 객체, 클래스에 책임을 두어야 할지 고민하면서, 객체가 너무 많아지지 않도록 여러번의 refactoring을 거쳤습니다.

이번 과제는 구현하면서 세세한 부분이 생각보다 많아, 제가 생각한 것보다 코드를 깔끔하게 작성하기 어려웠습니다.

또한, 코드와 함수가 많아지니 책임의 분리가 어려웠습니다. 부족한 코드지만 많은 피드백 부탁드립니다.

##4. 클래스 방식 추가

docs(README.md): 구현기능 목록 작성

README.md 구현기능 목록 작성
- 구현 기능목록 (구매가능 금액 최대값 SAFE INTEGER로 변경)
feat : 로또 구입금액 입력

- 로또 구입 금액 입력받기
- 로또 구입 금액 validation 체크
   - 숫자(양의 정수)값을 입력했는가?
   - 최대값 범위 내의 정수를 입력했는가?
   - 1000원 단위의 값을 입력했는가?
test : 로또 구입금액 단위 테스트

- 로또 구입 금액 validation 체크에 대한 단위테스트
   - 숫자(양의 정수)값을 입력했는가?
   - 최대값 범위 내의 정수를 입력했는가?
   - 1000원 단위의 값을 입력했는가?
refactor(purchasePrice) : 로또 구입 금액 유효체크 utils로 변경

- 로또 구입금액 유효체크 purchasePriceUtils 내 구현으로 변경
feat : 구매한 로또의 총 갯수를 구하고, 문구를 출력

- 구매 금액으로 구매한 로또 갯수를 구함
- 구매한 갯수를 출력
docs(README.md): 구현기능 에러 케이스 추가 (로또 번호 생성)

- 로또 번호가 6개가 아닌 경우
- 로또 번호에 중복이 있는 경우
feat : 구매한 로또들의 랜덤 번호 생성

- 구매한 로또번호들의 6개 랜덤 번호를 생성
- 로또번호 6개 갯수 확인
- 로또번호 중복여부 확인
- 오름차순으로 정렬
- 구매한 로또 번호를 출력
test(Lotto) : 로또 클래스 테스트 추가

- 로또 번호가 6개인지 확인
- 로또 번호에 중복이 없는지 확인
- 로또 번호를 오름차순에 맞게 출력하는지 확인
refactor : 로또 유효성 검사 validationCheck 객체 사용

- 로또 유효성 검사 validationCheck객체 사용으로 변경
- ERROR_CODE이름 변경
- Error발생은 validate함수에서 발생시킴
- validationCheck는 boolean형 리턴으로 변경(함수명을 더 직관적으로 표현)
feat : 로또 당첨 번호를 입력 및 유효성 검증

- 양의 정수(문자, 음수 등 금지)체크
- 1~45 까지의 정수 입력 체크
- 번호 갯수 6개 체크
- 중복값 여부
test(winningNumber) : 당첨 번호 유효성 테스트

 양의 정수(문자, 음수 등 금지)체크
- 1~45 까지의 정수 입력 체크
- 번호 갯수 6개 체크
- 중복값 여부
feat : 보너스 번호를 입력

- 보너스 번호 입력
- 보너스 번호 유효성 검사
  - 양의 정수가 아닌 값 여부
  - 1~45사이 범위에 있는지 여부
  - 당첨번호와의 중복여부
test(bonusNumber) : 보너스번호 유효성 검사

  - 양의 정수가 아닌 값 여부
  - 1~45사이 범위에 있는지 여부
  - 당첨번호와의 중복여부
fixed : 당첨번호 number array로 변경

- 당첨 번호 string array to number array
feat : 당첨 통계구하기

- index가 당첨 번호 수를 의미하는 배열을 생성
- 배열에 저장된 값은 해당 갯수로 당첨된 로또의 수
test : 당첨 통계 테스트

- index가 당첨 번호 수를 의미하는 배열을 생성
- 배열에 저장된 값은 해당 갯수로 당첨된 로또의 수
해당 배열을 제대로 생성해내는지 테스트
feat : 수익률 계산

- 당첨금의 합을 구함
- 수익률 계산
- 소수점 둘째 자리에서 반올림
test : 수익률 계산 테스트
fixed : 양의 정수 체크시 공백 오류 해결

- 양의 정수 체크시 공백 오류 문제 해결을 위해 trim추가
fixed : 테스트 형식을 위해 array출력 형식 변경

- 테스트에서 array 출력시 오류
- string으로 변경하여 출력
chore : 공백, 줄 제거 등

chore : 공백, 라인 제거, 불필요한 라이브러리 제거 등

chore : 공백, 라인 제거, 불필요한 라이브러리 제거 등
refactor : validation 파일 통합

- 여러개의 utils 중 validate 파일통합
- validationCheck -> validator로 파일명 변경
refactor : output(출력) 관련 분리

- App, lottoUtils에서 출력관련 IOHandler로 책임 분리
- InputHandler -> IOHandler로 변경
refactor : 로또 결과 계산 및 출력 부분 함수화

- 로또 결과 계산 및 수익률계산 함수
- 수익률 출력시 형식 지정 (소수점 1번째 까지 출력)
refactor : 출력은 IOHandler에서만 담당하도록 변경

- Lotto class의 출력 부분을 변경하고 IOHandler가 담당하도록 변경
refactor : default 값 사용대신 값이 있는지 체크

- default 값을 정의하지 않고, 값이 있을때만 수행하도록 변경
refactor : depth 2 제한 수정

- validator에서 depth 2를 넘어 function으로 수정
refactor(Lotto.js) : private method로 변경

- 클래스 내부에서만 사용되어야 하는 함수 private으로 변경
Copy link

@MyungJiwoo MyungJiwoo left a comment

Choose a reason for hiding this comment

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

리뷰가 좀 부족할 수 있지만 최선을 다해봤습니다..!!
3주차 미션도 고생 많으셨습니다! 🍀

Comment on lines +12 to +19
#validate(numbers) {
if (!validator.isCorrectSize(numbers, LOTTO.SIZE)) {
throw new Error(ERROR_CODE.SIZE_OUT_OF_RANGE(LOTTO.SIZE));
}
if (validator.hasDuplicates(numbers)) {
throw new Error(ERROR_CODE.NUMBER_DUPLICATE)
}
}

Choose a reason for hiding this comment

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

numbers를 여기에서 검사하는 것도 좋지만 로또 번호 검사는 동일하다 생각해서, 직접 생성하신 validation/validator에서 을 적용시키는 것은 어떨까요?

Comment on lines +14 to +29
getPrize(matchNumber) {
switch (matchNumber) {
case 7:
return LOTTO.FIRST_PRIZE
case 6:
return LOTTO.SECOND_PRIZE
case 5:
return LOTTO.THIRD_PRIZE
case 4:
return LOTTO.FOURTH_PRIZE
case 3:
return LOTTO.FIFTH_PRIZE
default:
return 0
}
},

Choose a reason for hiding this comment

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

matchNumber 값을 키로 하는 객체를 만들고, 해당 객체에서 값을 조회하는 방식을 사용하면 조금 더 간결한 코드 작성이 가능할 것 같아요!

const prizeMap = {
    7: LOTTO.FIRST_PRIZE,
    6: LOTTO.SECOND_PRIZE,
    5: LOTTO.THIRD_PRIZE,
    4: LOTTO.FOURTH_PRIZE,
    3: LOTTO.FIFTH_PRIZE,
};

const prize = prizeMap[matchNumber] || 0;

Comment on lines +14 to +23
export const LOTTO = {
SIZE: 6,
MIN_NUMBER: 1,
MAX_NUMBER: 45,
FIRST_PRIZE: 2000000000,
SECOND_PRIZE: 30000000,
THIRD_PRIZE: 1500000,
FOURTH_PRIZE: 50000,
FIFTH_PRIZE: 5000,
}

Choose a reason for hiding this comment

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

내부 프로퍼티는 외부에서 수정될 수 있기 때문에 Object.freeze를 사용해 객체를 수정하지 못하도록 접근 제어하는 것은 어떨까요?

Choose a reason for hiding this comment

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

다양한 테스트 케이스가 예외 상황을 많이 고민하신게 느껴져요! 고생 많으셨습니다 👏🏻

Choose a reason for hiding this comment

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

우와...🙊 정말 많이 고민하셨네요!! 👍🏻

Copy link

@MinSungJe MinSungJe left a comment

Choose a reason for hiding this comment

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

안녕하세요! 코드 너무 잘봤습니다.
여러 경우에 대해 테스트 코드를 이용해 철저하게 검사된 코드라고 생각이 들었습니다.👍

코드에 대한 전반적인 리뷰를 남깁니다.
어느덧 프리코스가 끝나가고 있네요, 마지막 제출까지 화이팅입니다!😄😄

Comment on lines +58 to +152
describe("당첨 케이스 테스트", () => {
test("1등 당첨 케이스", () => {
const lottos = [
new Lotto([1, 3, 5, 8, 11, 38])
]
const winningNumbers = [1, 3, 5, 8, 11, 38]
const bonusNumber = 39

const lottoGame = new LottoGame(winningNumbers, bonusNumber, lottos);
const lottoResult = [0, 0, 0, 0, 0, 0, 0, 1] //배열의 index는 일치하는 갯수를 위미( but, 6은 2등(5개 + 보너스)/ 7은 1등)

expect(
lottoGame.getLottoMatchResultArray()
).toStrictEqual(lottoResult);
});

test("2등 당첨 케이스", () => {
const lottos = [
new Lotto([1, 3, 5, 8, 11, 39])
]
const winningNumbers = [1, 3, 5, 8, 11, 38]
const bonusNumber = 39

const lottoGame = new LottoGame(winningNumbers, bonusNumber, lottos);

const lottoResult = [0, 0, 0, 0, 0, 0, 1, 0] //배열의 index는 일치하는 갯수를 위미( but, 6은 2등(5개 + 보너스)/ 7은 1등)

expect(
lottoGame.getLottoMatchResultArray()
).toStrictEqual(lottoResult);
});

test("3등 당첨 케이스", () => {
const lottos = [
new Lotto([1, 3, 5, 8, 11, 39])
]
const winningNumbers = [1, 3, 5, 8, 11, 38]
const bonusNumber = 40

const lottoGame = new LottoGame(winningNumbers, bonusNumber, lottos);

const lottoResult = [0, 0, 0, 0, 0, 1, 0, 0] //배열의 index는 일치하는 갯수를 위미( but, 6은 2등(5개 + 보너스)/ 7은 1등)

expect(
lottoGame.getLottoMatchResultArray()
).toStrictEqual(lottoResult);
});

test("4등 당첨 케이스", () => {
const lottos = [
new Lotto([1, 3, 5, 8, 11, 39])
]
const winningNumbers = [1, 3, 5, 8, 14, 38]
const bonusNumber = 40

const lottoGame = new LottoGame(winningNumbers, bonusNumber, lottos);

const lottoResult = [0, 0, 0, 0, 1, 0, 0, 0] //배열의 index는 일치하는 갯수를 위미( but, 6은 2등(5개 + 보너스)/ 7은 1등)

expect(
lottoGame.getLottoMatchResultArray()
).toStrictEqual(lottoResult);
});

test("5등 당첨 케이스", () => {
const lottos = [
new Lotto([1, 3, 5, 8, 11, 39])
]
const winningNumbers = [1, 3, 5, 10, 14, 38]
const bonusNumber = 40

const lottoGame = new LottoGame(winningNumbers, bonusNumber, lottos);

const lottoResult = [0, 0, 0, 1, 0, 0, 0, 0] //배열의 index는 일치하는 갯수를 위미( but, 6은 2등(5개 + 보너스)/ 7은 1등)

expect(
lottoGame.getLottoMatchResultArray()
).toStrictEqual(lottoResult);
});

test("0개 일치 케이스", () => {
const lottos = [
new Lotto([1, 3, 5, 8, 11, 39])
]
const winningNumbers = [2, 4, 6, 10, 14, 38]
const bonusNumber = 40

const lottoGame = new LottoGame(winningNumbers, bonusNumber, lottos);

const lottoResult = [1, 0, 0, 0, 0, 0, 0, 0] //배열의 index는 일치하는 갯수를 위미( but, 6은 2등(5개 + 보너스)/ 7은 1등)

expect(
lottoGame.getLottoMatchResultArray()
).toStrictEqual(lottoResult);
});

Choose a reason for hiding this comment

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

당첨 케이스의 n개 일치 케이스는 반복되는 부분이 많아, test.each()를 이용하면 테스트 코드의 양을 줄일 수 있을 것 같아요!
또 winningNumbers와 bonusNumber가 다 같아보이는데, 이 경우에는 test() 안보다 describe()안에 작성하면 중복을 줄일 수 있어보이네요! 😊

if (!validator.isPositiveNumber(purchasePrice))
throw new Error(ERROR_CODE.NOT_POSITIVE_NUMBER);
if (!validator.isInRange(purchasePrice)) {
throw new Error(ERROR_CODE.OUT_OF_RANGE(1, Number.MAX_SAFE_INTEGER));

Choose a reason for hiding this comment

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

Number.MAX_SAFE_INTEGER가 Number에서 제공하는 가장 큰 integer 값인가 보네요🤔
max 값을 정할 때 유용할 것 같아요!!

Choose a reason for hiding this comment

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

contants 안에 상수를 철저하게 분리해두셨네요 👍

Comment on lines +40 to +46
calculateTotalPrize() {
let totalPrize = 0;
this.getLottoMatchResultArray().forEach((amount, index) => {
totalPrize += amount * lottoUtils.getPrize(index);
})
return totalPrize;
}

Choose a reason for hiding this comment

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

이 코드는 reduce()를 이용해 간단하게 만들 수 있을 것 같아요! reduce는 어떤 배열을 이용해 특정한 값을 계산하고 싶을 때 사용하는 함수입니다🙂

calculateTotalPrize() {
    return this.getLottoMatchResultArray().reduce((totalPrize, amount, index) => 
        totalPrize + amount * lottoUtils.getPrize(index), 0);
}

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