-
Notifications
You must be signed in to change notification settings - Fork 8
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
[김효리] 자동차 경주 미션 Step1 #3
Conversation
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.
안녕하세요 효리님..!
가장 먼저 눈에 띈 부분은 테스트 및 유효성 검사하는 부분이 상당히 꼼꼼하게 되어있다는 생각을 했습니다..! (저는 대충해서 더 비교되었다? 😇)
설계
저는 이번 미션을 하면서 (특히 테스트 코드를 작성하는 과정에서) 도메인에서 뷰를 구성할 필요가 있을까? 라는 생각을 했습니다.
리액트와 연결지어 생각하면 도메인은 비즈니스 로직을 담당하는 커스텀 훅 느낌? 뷰는 UI로 그려지는 부분들?
그렇게 생각하니 테스트 코드에서 console
이 찍히는지를 테스트하는게 적절하지는 않다는 생각을 한 것 같습니다..! 우승자 여부 메서드가 찍히는지 확인하는 것보다 우승자 목록이 예상한대로 잘 왔는지 확인하는게 중요하다?
Validator 클래스 관련
이 부분은 저도 명확한 경험이 없어서 모르겠네요.. 🫠
assert
와 관련해서는 몇몇 부분은 validate
가 붙여진게 눈에 띄는데.. 일관성만 유지하면 될 것 같습니다..!
테스트 코드
추상적일수록 동작? 상황에 초점? 이 잘 와닿지는 못했습니다..! 😅
저도 잘은 모르겠지만 최근에 느낀 점은 좋은 코드들을 보면 일관된 구성을 띄는 것 같더라구요.
경험이 답인 것 같습니다.. 🙃
체이닝
저는 좋습니다..! 혹시 나쁘다고 생각하는 부분은 어떤 부분이었을까요?? 🤔
수고 많으셨습니다..! 면접 화이팅 🙇♂️
"@eslint/js": "^9.5.0", | ||
"eslint": "^9.5.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"globals": "^15.6.0", |
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.
오 처음보는 라이브러리인데 eslint
와 관련된 것 같군요..!
|
||
test('참가자를 등록하면 현재 자동차들의 위치를 알 수 있다.', () => { | ||
expect(() => | ||
new CarRacingGame().register(['A', 'B', 'C']).printCurrentPositions(), |
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.
printCurrentPositions
가 에러를 발생시키지 않았다는 부분과 현재 자동차의 위치를 알 수 있다는 부분에서 해당 테스트의 목적이 일치하지 않다는 생각을 했어요..!
밑에 우승자 파악하는 테스트에서 printWinners
경우도 마찬가지?? 그래서 현재 테스트 코드들이 toThrow
의 에러 여부만으로 이루어진게 아닌가 라는 생각이 드네요.
이렇게 된 이유는 해당 테스트가 뷰와 관련된 로직을 테스트하려고 하기 때문에 발생한 것 같아요..!
지금같은 경우 현재 자동차들의 위치를 테스트하는 것이기 때문에 player.position
가 관심사로 보여집니다.
밑의 우승자의 경우에도 printWinners
가 아닌 winners
가 예상한 결과와 동일하게 나왔는지를 확인하는게 더 괜찮아보여요..!
).not.toThrow(); | ||
}); | ||
|
||
test('A와 B 중 A만 전진시킬 수 있다.', () => { |
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.
테스트의 상황에 맞는 조건이 없어서 문장이 이상한 것 같아요..!
지금 테스트하는 상황과 맞게 하려면 전진 조건에 해당했을 때만 전진시킬 수 있다
가 더 적절해보여요..! 🧐
전진 조건은 변경의 가능성이 높기 때문에?
{ | ||
value: '+_+', | ||
pattern: /^[a-zA-Z0-9ㄱ-힣 ]+$/, | ||
describe: '특수문자를 허용하지 않는 경우', |
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.
설명 감사합니다..! 👍
expect(race.runners.indexOf(A) > race.runners.indexOf(B)).toBe(true); | ||
}); | ||
|
||
test('B가 A보다 더 많이 이동했을 때 B 순위가 더 높지 않을 수도 있다.', () => { |
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.
높지 않을 수도 있다?
🤔
높지 않아야 한다.
가 테스트 목적에 더 적합하지 않을까 싶습니다..!
.filter( | ||
(player) => | ||
player.position === | ||
Math.max(...this.#players.map((player) => player.position)), |
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.
따로 함수로 빼면 더 가독성이 좋을 것 같아요..!
player.position === makeMaxPostion(players)
또는 미리 변수로 만들어놔서
player.position === maxPosition
같은 느낌?
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.
더불어 filter
안에서 매번 max 함수를 쓰면 O(N^2) 을 반복하는 거니까 빼주는 게 좋을 거 같네요...!
* // 5부터 15까지의 랜덤 숫자 반환 | ||
* getRandomNumber(5, 15); | ||
*/ | ||
function getRandomNumber(num1, num2) { |
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.
저는 주석을 좋아해서 좋네요..! 👍
NumberValidator.from(num1).lessThanOrEqual(num2); | ||
NumberValidator.from(num2); | ||
|
||
random = Math.floor(Math.random() * (num2 - num1 + 1)) + num1; |
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.
let
으로 할 필요가 있었을까요?? 🤔
} | ||
|
||
static #validateValue(value) { | ||
if (value === undefined || value === null) { |
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.
value == null
이렇게도 표현하기도 하더라구요..! 😂
isNil
키워드
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.
무척 창의적으로 미션을 진행해주신 것 같아요!
보면서 많이 놀랐습니다 ㅎㅎ
그리고 프로그래머스를 할 때 보다 확실히 많이 성장한게 느껴지네요!
테스트 코드를 먼저 작성하고 그 다음에 구현을 하려고 한 부분이 무척 인상깊어요!
이게 정말 쉽지 않다는걸 알고 있기 때문에 (지금 저도 잘 못하는 부분이기도 해요)
이러한 시도 자체가 유의미하다고 생각합니다.
일단 결과적으로 꽤 잘 작성해주셨어요!
테스트 케이스도 꼼꼼하게 작성해주셨네요 ㅎㅎ
특히 View에 해당하는 부분을 어떤 식으로 나눠야 할지 잘 모르겠습니다.
View는 리액트로 따지면 컴포넌트라고 생각해요.
가령, 경기의 결과를 한 번에 모아서 View에게 넘겨줄 수도 있을 것 같아요.
어쨌뜬 경기의 결과 데이터가 어딘가에 계속 누적이 되고, 이를 뷰에게 전달할 수 있는 형태가 되어야 하는거죠.
그래서 지금 상태에서 출력하는 부분만 잘 분리해주면 되지 않을까 싶어요!
Validator를 만든 게 괜찮은 발상이었는지부터 궁금합니다 ... 😿
저는 굉장히 좋은 발상이라고 생각합니다!
사실 실무에서는 zod 같은걸 많이 사용해요 ㅋㅋ
이 부분은 상세 리뷰에 남겨놓은 것 처럼 함수형 프로그래밍 쪽을 조금 더 공부해보시면 아마 효리님이 원하시는 스타일대로 구현하기가 더 수월하지 않을까 싶네요!
테스트 코드는 추상적일수록 동작에 초점을 맞춰서 작성했고, 도메인에 가까울수록 상황에 초점을 맞춰서 작성해봤는데 어떤 게 좋은 테스트 상황인지 궁금합니다.
오... 저는 이렇게 생각해본적은 없는데 좋은 발상이라고 생각합니다.
추상화가 되어있다는 것은 그만큼 다양한 상황이 존재한다는 이야기 같아요.
가령, 레이스 라는게 "자동차" 일 수도 있지만 저는 "사람"도 경주를 할 수 있고, "자전거"도 경주를 할 수 있다고 생각했어요.
혹은 토끼와 거북이가 경주를 할 수 도 있고?
그렇게 다양한 상황을 주입하는 방식으로 테스트를 해보면 이 코드를 사용하는 사람들의 생각이 확장될 수 있지 않을까 싶네요. 효리님 덕분에 인사이트를 얻어갑니다!
가독성과 사용성을 높이기 위해 클래스를 체이닝으로 사용할 수 있도록 구현했는데, 이런 방식이 괜찮은 것인지 궁금합니다.
좋은 방식이라고 생각해요! 안 좋을 이유는 없다고 생각합니다 ㅎㅎ 더 정확히는, 이에 대한 단점이 딱히 생각나지 않네요..!
전체적으로 잘 진행해주셨어요!
고민한 흔적이 많이 보여서 좋네요 😄
class Car extends Vehicle { | ||
static #NAME_SIZE_LIMIT = 5; | ||
static #NAME_PATTERN = /^[a-zA-Z0-9ㄱ-ㅎ가-힣\s]*$/; | ||
|
||
static #ERROR_MESSAGES = Object.freeze({ | ||
INVALID_NAME: '이름은 특수문자를 제외한 문자만 가능합니다.', | ||
EMPTY_NAME: '이름은 공백일 수 없습니다.', | ||
INVALID_NAME_SIZE: `이름은 ${this.#NAME_SIZE_LIMIT}자 이하여야 합니다.`, | ||
}); |
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.
저는 개인적으로 에러 코드만 Car가 가지고 있고, 이를 메세지로 만들어 주는 구간은 View에게 위임하면 좋다고 생각해요!
메세지를 클라이언트에서 다르게 사용할 수도 있고, 다국어로 처리할 수도 있으니까요!
import NumberValidator from '../utils/validators/NumberValidator.js'; | ||
import StringValidator from '../utils/validators/StringValidator.js'; | ||
|
||
class Car extends Vehicle { |
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.
Vehicle은 어떤 상황에서 쓰일 수 있을까요?
지금은 어떤 느낌이냐면
Car > 이름에 대한 검증을 담당함
Vehicle > 전진에 대해 담당함
그럼 이 때 아예 이런 관계로 구성할 수 있지 않을까요?
CarName > 자동차의 이름을 검증
Car > 자동차 자체
Car가 CarName을 소유
super(); | ||
Car.#validateName(name, nameSize); | ||
this.#name = name.trim(); | ||
this.mode; |
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.
mode는 뭘까요!?
@@ -0,0 +1,17 @@ | |||
class Vehicle { |
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.
이 친구도 domain 폴더에 있어야 자연스러울 것 같아요!
function getRandomNumber(num1, num2) { | ||
let random; | ||
|
||
NumberValidator.from(num1).lessThanOrEqual(num2); | ||
NumberValidator.from(num2); | ||
|
||
random = Math.floor(Math.random() * (num2 - num1 + 1)) + num1; | ||
return parseInt(random); | ||
} |
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 getRandomNumber(num1, num2) { | |
let random; | |
NumberValidator.from(num1).lessThanOrEqual(num2); | |
NumberValidator.from(num2); | |
random = Math.floor(Math.random() * (num2 - num1 + 1)) + num1; | |
return parseInt(random); | |
} | |
function getRandomNumber(num1, num2) { | |
NumberValidator.from(num1).lessThanOrEqual(num2); | |
NumberValidator.from(num2); | |
return Math.floor(Math.random() * (num2 - num1 + 1)) + num1; | |
} |
어차피 floor를 통해서 소숫점이 날라가서 이렇게 해도 되지 않을까 싶어요!
아마 num1과 num2가 소수일 수도 있는 상황을 가정한건가 싶기도 하고...
import ArrayValidator from '../utils/validators/ArrayValidator.js'; | ||
import NumberValidator from '../utils/validators/NumberValidator.js'; | ||
|
||
class CarRacingGame { |
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.
이 클래스는 도메인과 입출력이 뒤섞여있는 것 같아요!
() => | ||
getRandomNumber( | ||
CarRacingGame.#RANDOM_NUMBER_MIN, | ||
CarRacingGame.#RANDOM_NUMBER_MAX, | ||
) >= CarRacingGame.#RANDOM_NUMBER_CONDITION, |
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.
제 생각에도 분리하면 좋을 것 같아요! 가독성이 떨어지는 부분이었어요. ㅎㅎ
test('자동차의 이름이 문자가 아닌 경우 오류가 발생한다.', () => { | ||
expect(() => new Car({ name: 123 })).toThrow(); | ||
}); |
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.
문자열로 변환해주면 어떨까요?
test.each([ | ||
{ name: '자동차' }, | ||
{ name: '자동차123' }, | ||
{ name: 'ㅈㄷㅊ 123' }, | ||
{ name: 'car' }, | ||
{ name: 'car123' }, | ||
{ name: 'car 123' }, | ||
{ name: '자동차 CAR 123' }, | ||
])( | ||
'자동차의 이름이 영어 대소문자, 한글, 숫자, 공백문자로 이루어져 있다면 오류가 발생하지 않는다: $name', | ||
({ name }) => { | ||
expect(() => new Car({ name, nameSize: 20 })).not.toThrow(); | ||
}, | ||
); |
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.
이름에 대한 규칙 자체를 바깥에서 주입할 수 있도록 해주면 재밌겠네요!
지금은 길이만 주입할 수 있지만, 이름에 대한 규칙을 주입해주는거죠 ㅋㅋ
import { describe, test, expect } from 'vitest'; | ||
import CarRacingGame from '../app/CarRacingGame.js'; | ||
|
||
describe('CarRacingGame >', () => { |
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.
CarRacingGame이 입/출력도 담당하고 있어서 (아마 정확히는 출력이겠네요)
출력 부분을 아예 분리시키면 어떨까 싶어요!
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.
테스트랑 유효성 검사 코드를 보고 놀라웠어요..! 정말 꼼꼼하게 잘 구현하신!
test('B가 A보다 더 많이 이동했을 때 B 순위가 더 높다.', () => { | ||
const A = new Car({ name: 'A' }); | ||
const B = new Car({ name: 'B' }); | ||
const race = new Race([A, B]); | ||
|
||
expect(race.runners.indexOf(A) < race.runners.indexOf(B)).toBe(true); | ||
|
||
race | ||
.moveRunners((runner) => runner.name === 'B') | ||
.sortRunners((runner1, runner2) => runner2.position - runner1.position); | ||
|
||
expect(race.runners.indexOf(A) > race.runners.indexOf(B)).toBe(true); | ||
}); |
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개 들어가 있기 때문에
expect(race.runners.indexOf(A) < race.runners.indexOf(B)).toBe(true);
이 부분은 제거해주셔도 충분히 해당 테스트가 의도하는 바는 알 수 있을 것 같아요!
expect(randomNumber).toBeGreaterThanOrEqual(1); | ||
expect(randomNumber).toBeLessThanOrEqual(10); |
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.
이부분 체이닝으로 쓸 수 있어요!
expect(randomNumber)
.toBeGreaterThanOrEqual(1)
.toBeLessThanOrEqual(10)
#printHeader() { | ||
console.log(CarRacingGame.CONSOLE_MESSAGE.RESULT_HEADER); | ||
|
||
return this; | ||
} |
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.
return this;
는 제거해도 괜찮지 않나요??
() => | ||
getRandomNumber( | ||
CarRacingGame.#RANDOM_NUMBER_MIN, | ||
CarRacingGame.#RANDOM_NUMBER_MAX, | ||
) >= CarRacingGame.#RANDOM_NUMBER_CONDITION, |
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 repeatFn(fn, times = 1) { | ||
FunctionValidator.from(fn); | ||
NumberValidator.from(times).greaterThan(0).assertInteger(); | ||
|
||
for (let i = 0; i < times; i++) { | ||
fn(); | ||
} | ||
} |
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.
프로그래머스 선택강의 중 21주차 함수형 프로그래밍과 ES6+ 강의 유익하니까 꼭 보세요!
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.
안녕하세요 효리님~! 이렇게나마 같은 팀이 돼보네요 😄 저도 헷갈리고 모르는 부분에 대해선 확실히 조언을 해드릴 수 없어서 개인적으로 느낀 점을 서술해볼게요...!
설계
도메인과 비즈니스 로직을 분리하는 건 저도 부족해서 뭐라 말씀 드릴 순 없지만... 효리님이 확장성을 고려해서 moveCondition 이나 winConditon 같은 조건을 밖에서 주입할 수 있도록 하신 게 인상깊었어요. 규칙이 달라지면 밖에서 의존성을 주입하는 방법을 미처 생각못하고 있었던 것 같아요
Validator
개인적으로 Validator 클래스 자체를 만든 건 엄청 대단하신 것 같아요...! 하나의 라이브러리처럼 inRange, maLength 같은 메서드를 만드신 걸 보고 감탄했어요. 저도 유효성 검사를 어떻게 해야 더 편리하고 깔끔하게 쓸 수 있을지 고민했는데 효리님 코드가 참고가 많이 되었습니다!
메서드 체이닝
개인적으로 좋은 방법인 것 같아요! 저도 필요한 곳에 적용할 수 있도록 고려해봐야겠습니다!
.husky/pre-commit
Outdated
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.
허스키를 쓰셨군요...! 저도 허스키 한 번 써봐야겠어요!
const players = name.split(','); | ||
|
||
carRacingGame.register(players).play(5).printWinners(); |
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.
타입을 마구 제한한다고 좋은 건 아니겠군요...!
static #MOVEMENT_SYMBOL = '-'; | ||
static #RANDOM_NUMBER_MIN = 0; | ||
static #RANDOM_NUMBER_MAX = 9; | ||
static #RANDOM_NUMBER_CONDITION = 4; |
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.
특정 클래스에 종속되는 상수는 이렇게 정적 프로퍼티로 만드는 게 더 편한 관리 방법이겠군요...!
return this.#playCount; | ||
} | ||
|
||
register(players) { |
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.
개인적으로 register 가 어떤 걸 등록해주는 건지 사용하는 쪽에선 잘 모를 수 있겠다는 생각이 들어요...! registerPlayer 와 같이 좀 더 명시적으로 적는 것은 어떨까요?
.filter( | ||
(player) => | ||
player.position === | ||
Math.max(...this.#players.map((player) => player.position)), |
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.
더불어 filter
안에서 매번 max 함수를 쓰면 O(N^2) 을 반복하는 거니까 빼주는 게 좋을 거 같네요...!
CarRacingGame.#validatePlayers(this.#players); | ||
CarRacingGame.#validateCount(this.#playCount); | ||
|
||
const winners = this.#players |
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.
this.#player
는 매번 race 에서 라운드가 진행되거나 race 가 초기화될때 race.runner()
를 통해 가져오는데 this.#player
라는 프라이빗 속성을 CarRacingGame 에서 들고있어야 하는 이유는 무엇인지 궁금해요!
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.
해당 클래스를 App 폴더로 분류한 이유가 궁금해요! 효리님에게 App 폴더는 어떤 용도의 폴더인가요?
get runners() { | ||
return this.#runners; | ||
} |
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.
저도 한 실수같네요.... 부끄럽군여.. ㅠ
this.#runners.forEach((runner) => { | ||
if (moveCondition(runner)) { | ||
runner.move(); | ||
} | ||
}); |
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.
이동 조건을 밖에서 줄 수 있도록 했군요...! 👍
일단 혼자 해보고 싶어서 찬욱님 PR과 리뷰는 보지 않았습니다.
그래서 힘들었습니다. 😭 이제 참고하러 가보겠습니다 !!!
미션을 수행하면서 어려웠던 점
리뷰 받고 싶은 부분
제가 구현한 순서는 다음과 같습니다.
test('이러이러한 상황이다')
정도로 작성하고 구체적으로 작성하진 않았습니다.이렇게 작성하니 외부 의존성을 보다 낮출 수 있었던 것 같아요!
그리고 테스트 상황에서 확장성을 미리 고려해두고 작성하려고 노력했습니다...
(게임 참가자 추가, 게임 횟수 변경, 승리 조건 변경 등)
좀 더 고민할 부분을 알려주셨으면 좋겠습니다!
궁금한 점
설계
https://bespoyasov.me/blog/clean-architecture-on-frontend/ 아티클을 참고해서 아키텍처를 설계하려고 노력했으나 ......
적합하게 나누지 못한 것 같아요. 특히 View에 해당하는 부분을 어떤 식으로 나눠야 할지 잘 모르겠습니다.
Validator 클래스
Validator를 만든 게 괜찮은 발상이었는지부터 궁금합니다 ... 😿
Validator에 있는 메서드들은 전부 해당 조건을 만족하지 않으면 에러를 반환합니다. 그런데 메서드 이름만 보면 boolean을 반환한다고 생각할 수 있을 것 같아요. 앞에
assert
를 전부 붙이는 게 좋을까요?errorMessage를 throw 하는 등 불필요하게 반복되는 코드들이 있는데 이걸 어떻게 분리할 수 있을지 궁금합니다.
테스트 코드는 추상적일수록 동작에 초점을 맞춰서 작성했고, 도메인에 가까울수록 상황에 초점을 맞춰서 작성해봤는데 어떤 게 좋은 테스트 상황인지 궁금합니다.
체이닝
가독성과 사용성을 높이기 위해 클래스를 체이닝으로 사용할 수 있도록 구현했는데, 이런 방식이 괜찮은 것인지 궁금합니다.