-
Notifications
You must be signed in to change notification settings - Fork 6
[차세진] 로또 미션 Step1 #3
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
base: chasj0326
Are you sure you want to change the base?
Conversation
suyeon1218
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 세진님...! 비록 팀은 아니지만 세진님이 이번엔 어떻게 설계하셨나 싶어 슬쩍 구경하러 왔어요...! TDD 를 기반으로 설계를 하신 걸까요? 아니면 설계를 먼저 한 뒤 TDD 로 로직을 작성하시려고 한 건가요?! 어느 쪽이든 LottoSystem (LottoMatcher, LottoPayment) 로 역할을 분리해준 게 깔끔하단 생각이 들었어요. 그리고 차츰 세진님이 RacingCar 에서 사용했던 코드들을 재사용하시는 걸 보고 자신만의 스타일을 잡아간다는 느낌도 들어요. 저는 아직 방황하는 중이지만... 세진님 코드를 보고 많이 배웁니다 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
일반 텍스트로 타입을 구분한 것과 Symbol 을 사용해서 구분하는 것의 차이점이 무엇인가요...?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 독립성을 보장해준다?
디테일이 돋보이네요..!
'lottoTicket' === 'lottoTicket' // true
Symbol('lottoTicket') === Symbol('lottoTicket') // false| lottoType: { | ||
| check: ({ type }) => type === LOTTO_TYPE.WINNING || type === LOTTO_TYPE.TICKET, | ||
| errorMessage: '로또는 ticket, winning 두 가지 유형이어야 합니다.', | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
세진님의 이런 유효성 검사 방식이 깔끔하다고 느끼면서 앞에 구문이 통과하지 못하면 || 연산자의 마지막 에러 메세지를 반환해주는 react-hook-form 이 생각났어요...! 세진님의 방법도 좋은데 이런 방법도 괜찮지 않을까 싶어 적어봅니다...!!
lottoType: ({type}) => type === LOTTO_TYPE.WINNING || type === LOTTO_TYPE.TICKET || 'errorMessage'There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런 게 있군요...충격... ㅋㅋㅋㅋㅋ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 ko-KR도 인자로 받으면 좋을 것 같네요..!
지금 함수는 formatCurrency와 달리 한국만 지원하는 느낌?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 이 부분을 고민하다가 번호를 랜덤으로 생성해주는 유틸함수를 만들고, LottoRule 이라는 클래스에서 한번 더 감싸서 호출할 수 있도록 했거든요...! 세진님은 요 함수를 LottoSystem 에도 넣지 않고, util 함수에도 넣지 않으신 이유가 있으실까요?!
seeyoujeong
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 잘읽었어요!
객체 마스터의 길은 대단한듯합니다 ㅋㅋㅋ
테스트 코드에서 메소드의 동작여부 보다는 validate 를 올바르게 하는지...
validate를 통과하면 메서드가 잘 동작한다고 생각이 들어서 괜찮지 않을까요..? 테스트 코드도 정답이 없는듯합니다... ㅎㅎ
요런 요구사항이 있었는데, 잘 지켜졌는지 모르겠습니다 .. !
제가 봤을땐 잘지켜진거같아요!
| new Lotto({ | ||
| type: LOTTO_TYPE.WINNING, | ||
| numbers: [1, 2, 3, 4, 5, 45], | ||
| bonusNumber: 46, | ||
| }), | ||
| ).toThrow(lottoValidations.lottoRange.errorMessage); | ||
|
|
||
| expect( | ||
| () => | ||
| new Lotto({ | ||
| type: LOTTO_TYPE.TICKET, | ||
| numbers: [0, 1, 2, 3, 4, 5], | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중복을 줄이는 용도로 팩토리 메서드를 만들어도 괜찮을듯합니다!
function createWinningLotto(numbers, bonusNumbers) { ... }
function createLotto(numbers) { ... }| const LOTTO_RANKING_DATA = [ | ||
| { | ||
| rank: 1, | ||
| matchCount: 6, | ||
| profit: 2000000000, | ||
| }, | ||
| { | ||
| rank: 2, | ||
| matchCount: 5, | ||
| bonusMatch: true, | ||
| profit: 30000000, | ||
| }, | ||
| { | ||
| rank: 3, | ||
| matchCount: 5, | ||
| bonusMatch: false, | ||
| profit: 1500000, | ||
| }, | ||
| { | ||
| rank: 4, | ||
| matchCount: 4, | ||
| profit: 50000, | ||
| }, | ||
| { | ||
| rank: 5, | ||
| matchCount: 3, | ||
| profit: 5000, | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오호! 저도 이런 데이터를 만들어놓고 시작해야겠어요!
| constructor({ rankingRule = LOTTO_RANKING_RULE } = {}) { | ||
| LottoSystem.#validate({ rankingRule }); | ||
|
|
||
| this.#rankingRule = rankingRule.sort((a, b) => b.rank - a.rank); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sortRank 요런 함수로 분리해도 괜찮을지도..?! 아닐지도..
| this.printer = new ConsolePrinter({ | ||
| paidCount: '%{1}개를 구매했습니다.', | ||
| lottoTicket: '%{1} | %{2} | %{3} | %{4} | %{5} | %{6}', | ||
| line: '---------------------------------------------', | ||
| ranking: '%{1}등 : %{2}개 일치 (%{3}원) - %{4}개', | ||
| rankingWithBonus: '%{1}등 : %{2}개 일치, 보너스볼 일치 (%{3}원) - %{4}개', | ||
| profitRatio: '총 수익률은 %{1}% 입니다.', | ||
| error: '⚠️ %{1}', | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
최고! 👍
| read(message, validations = []) { | ||
| const processInput = async () => { | ||
| const input = await this.askQuestion(message); | ||
| const errorMessage = this.findInputError(input, validations); | ||
|
|
||
| if (errorMessage) { | ||
| return processInput(); | ||
| } | ||
| return input; | ||
| }; | ||
| return processInput(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이러면 read 메서드가 동기인지 비동기인지 헷갈릴거 같아요!
| payAmountUnit1000: { | ||
| check: ({ payAmount }) => payAmount % 1000 === 0, | ||
| errorMessage: '로또 결제는 1000원 단위로만 가능합니다.', | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
생각해보니 1000원 단위로만 사야겠군요! 제 코드 수정해야겠군요 ㅎㅎ
아. 아니면 수익률 계산할때 로또 개수로 해도 되니깐 1000원 단위로만 안받게 해도 괜찮지 않을까요?
| errorMessage: '로또 랭킹 규칙에 유효하지 않은 값이(rank) 포함되어 있습니다.', | ||
| }, | ||
| validMatchCounts: { | ||
| check: ({ rankingRule }) => | ||
| rankingRule.every(({ matchCount }) => Number.isInteger(matchCount) && 0 < matchCount && matchCount <= 6), | ||
| errorMessage: '로또 랭킹 규칙에 유효하지 않은 값이(matchCount) 포함되어 있습니다.', | ||
| }, | ||
| validBonusMatches: { | ||
| check: ({ rankingRule }) => rankingRule.every(({ bonusMatch }) => bonusMatch === undefined || typeof bonusMatch === 'boolean'), | ||
| errorMessage: '로또 랭킹 규칙에 유효하지 않은 값이(bonusMatch) 포함되어 있습니다.', | ||
| }, | ||
| validProfits: { | ||
| check: ({ rankingRule }) => rankingRule.every(({ profit }) => Number.isInteger(profit) && profit > 0), | ||
| errorMessage: '로또 랭킹 규칙에 유효하지 않은 값이(profit) 포함되어 있습니다.', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 에러메시지 만들어주는 함수가 있어도 괜찮을지도..?
| printWithTemplate(templateKey, messages) { | ||
| if (this.template.hasOwnProperty(templateKey)) { | ||
| console.log(this.format(templateKey, messages)); | ||
| } else { | ||
| console.log(...messages); | ||
| } | ||
| } | ||
|
|
||
| lineBreak() { | ||
| console.log(''); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
console.log 를 print로 대체해도 될듯합니다!
| static #validate(lottoPaymentProps) { | ||
| validateLottoPayment({ | ||
| target: lottoPaymentProps, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
바로 validateLottoPayment를 안쓰고 한번더 감싼 이유가 있을까요??
jgjgill
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수고 많으셨습니다..! 🙇♂️
Matcher와 Payment와 같은 부분 도우미로 정의하신 부분이 기억에 남네요..!
테스트 코드
보통 에러에 대한 체크 테스트도 같이 두는 것 같습니다..!
그래서 테스트만 보고도 올바르게 사용한 케이스, 잘못 사용한 케이스를 확인할 수 있는게 장점으로 느껴집니다.
그래서 제 생각에는 사용자 입장에서 생각하면 좋을 것 같아요. 다른 사람이 봤을 때 해당 함수를 어떻게 하면 쉽게 이해할 수 있을까? 이 테스트 구문은 없는게 오히려 좋지 않을까? 등등...
저같은 경우 사용자가 잘못된 입력을 하는 것을 테스트하기 보다는 로직에 집중해서 테스트를 구성한 것 같습니다. (핑계입니다.. 😅)
이렇게 하면 잘못된 값에 대한 테스트도 조금은 덜어낼 수 있지 않을까 싶네요..!
| lottoSystem.setWinningLotto([1, 2, 3, 4, 5, 6], 7); | ||
| lottoSystem.payLottoTicket(2000); | ||
|
|
||
| expect(lottoSystem.profitAmount).toBe(2000050000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2_000_050_000 더 보기 편할 것 같아요..!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 독립성을 보장해준다?
디테일이 돋보이네요..!
'lottoTicket' === 'lottoTicket' // true
Symbol('lottoTicket') === Symbol('lottoTicket') // false| @@ -0,0 +1,27 @@ | |||
| import { validateLottoMatcher } from '../validations/lottoMatcher.js'; | |||
|
|
|||
| export default class LottoMatcher { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 Matcher라는 역할을 구성했군요..!
| while (lottoNumbers.size < 6) { | ||
| lottoNumbers.add(getRandomNumber(1, 45)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정말 최악의 경우로 1이 100억 번 나온다면?! 🤪 (딴지 걸기)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 이 함수는 믿고 쓸 수 있겠죠..!
여기에 추가해도 괜찮을까요? 🧐
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오오 ko-KR도 인자로 받으면 좋을 것 같네요..!
지금 함수는 formatCurrency와 달리 한국만 지원하는 느낌?
| test: { | ||
| globals: true, | ||
| }, | ||
| }) No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EOF!
AI가 해주는 리뷰입니다!알겠습니다. 포맷팅을 개선하여 각 섹션을 더 명확하게 구분하고 제목을 강조하겠습니다. 코드 분석:제공된 Pull Request는 로또 게임 구현을 위한 기본적인 구조와 기능을 추가하고 있습니다. 주요 변경사항은 다음과 같습니다:
강점:
개선점:
구체적 제안:
import { describe, test, expect, vi } from 'vitest';
import LottoController from '../domain/LottoController';
describe('LottoController', () => {
test('getLottos should return correct number of lottos', async () => {
const mockView = {
inputMoney: vi.fn().mockResolvedValue(5000),
};
const controller = new LottoController({ view: () => mockView, lotto: Lotto, winLotto: WinLotto });
const lottos = await controller.getLottos();
expect(lottos).toHaveLength(5);
});
});
class LottoError extends Error {
constructor(message) {
super(message);
this.name = 'LottoError';
}
}
// 에러 발생 시
throw new LottoError('유효하지 않은 로또 번호입니다.');
// 에러 처리
try {
// 로직
} catch (error) {
if (error instanceof LottoError) {
console.error('로또 게임 오류:', error.message);
} else {
console.error('알 수 없는 오류:', error);
}
}
// config.js
export default {
lottoPrice: 1000,
limitPrice: 10000000,
minNumber: 1,
maxNumber: 45,
defaultLength: 6,
winLength: 7,
// ... 기타 설정
};
// LottoRule.js
import config from './config.js';
const LottoRule = {
...config,
// ... 기타 메서드
};
export default LottoRule;
generateLottoNumbers: () => {
const numbers = new Set();
while (numbers.size < LottoRule.defaultLength) {
numbers.add(Math.floor(Math.random() * LottoRule.maxNumber) + LottoRule.minNumber);
}
return Array.from(numbers);
},학습 포인트:
추가될 수 있는 요구사항 예시와 대응:
이러한 변경사항들은 기존 코드의 구조를 크게 해치지 않으면서 새로운 요구사항을 수용할 수 있도록 합니다. 설정의 중앙화와 객체지향적 설계 덕분에 새로운 기능 추가나 규칙 변경이 비교적 쉽게 이루어질 수 있습니다. |
미션을 수행하면서 어려웠던 점
모든 결과를 추합해서 출력하기 위해서는, 로또를 작동시키는 클래스가 너무 많은 일을 해야 했어요. (금액 받아서 로또 만들기, 구매한 로또 등수매기기, 등수마다 개수세기, 수익률 계산하기 등등) 그래서 어려웠습니다 🥲
따라서 전체적인 동작은 LottoSystem 이라는 모델에서 하되, 로또 결제(LottoPayment)와 당첨로또와의 비교(LottoMatcher)를 도와주는 클래스로 나누었습니다. 말그대로 도와주는 친구들이라서, 순수함수인 static 메소드로 작성했습니다
아래와 같은 구조입니다
(사실 ... 뭔가 대놓고 컨트롤러!뷰! 이름을 붙이기는 조금 싫었지만 ... 마땅히 생각나지 않았습니다 ㅎ.ㅎ)
리뷰 받고 싶은 부분 & 궁금한 점
요런 요구사항이 있었는데, 잘 지켜졌는지 모르겠습니다 .. ! LottoSystemController에서 LottoSystemViewer에게 LottoSystem 을 통째로 넘기고, 뷰어에서는 LottoSystem 의 프로퍼티를 꺼내서 출력합니다. 이런건 조작은 아니구 참조이기 때문에 무관한 거.. 겠죠 !?
이 외에도 validation, 전체적인 구조 등등ㅇ..등.. 자유롭게 피드백 주세요 ! 🙇🏻♀️