[2단계 - 웹 기반 로또 게임] 포도 미션 제출합니다.#468
[2단계 - 웹 기반 로또 게임] 포도 미션 제출합니다.#468goodsmell wants to merge 113 commits intowoowacourse:goodsmellfrom
Conversation
- 여러 개의 mock 데이터를 테스트 할 수 있게 하기 위함
- test-> moneyString
There was a problem hiding this comment.
2단계 과제도 고생 많으셨습니다!
PR 디스크립션을 봤을 땐 아직 본인의 코드에 대한 확신이 덜하신 것 같았는데, 코드를 보니 전반적으로 잘 구현해주신 것 같습니다.👍👍
배운 점에 적어주셨듯 View 클래스를 잘 쪼개주셔서 책임 구분이 깔끔했고, 중간 서비스 계층을 추가해서 도메인 순수성을 지킨 부분이 특히 좋았다고 생각됩니다. 모듈간 의존성 이슈를 해결할 때 이런식으로 중간 레이어를 두는 방식은 충분히 자연스럽고 실무에서도 자주 쓰이는 접근이라고 생각되어요.
step1에서 도메인 로직이 이미 완성되어서 이번에 코드레벨에서 리뷰할 포인트는 많지 않았던 것 같아요. 아래에 주신 질문에 대한 답변 드리도록 하겠습니다.
- 현재 콘솔용(step1-app.js)과 웹용(step2-app.js) 컨트롤러가 완전히 분리되어 있습니다. "돈을 입력받아 로또를 사고 당첨 번호를 맞춰본다"는 핵심 비즈니스 프로세스는 동일함에도 불구하고, 입출력 방식의 차이 때문에 로직을 공유하지 못했습니다. 이런 상황에서 환경(UI)에 의존하지 않는 '핵심 실행 흐름'만 따로 추출하여 재사용할 수 있는 설계적 접근이 있을까요? 아니면 현재처럼 환경별로 컨트롤러를 따로 두는 것이 더 일반적인가요?
지금처럼 환경별로 컨트롤러를 분리하는 것은 자연스러운 접근입니다. 콘솔은 순차적 async/await 흐름이고, 웹은 이벤트 기반이라 제어 흐름 자체가 근본적으로 다르다고 생각해요. 이런 환경 차이를 하나의 추상화로 통합하려 하면 오히려 자연스럽지 못한 구조가 됩니다.
근데 보통은 그렇긴한데, 사실 이미 잘 추상화해주신 부분이 있어요. step2에서 추가된 LottoMachine을 보면, step1의 컨트롤러가 직접 도메인을 참조해서 수행하던 로직을 잘 분리해두었다고 볼 수 있을 것 같거든요. 이 서비스를 이용해서 다시 step1의 컨트롤러를 작성해보면, 어떤 부분이 공통이고 어떤 부분이 환경별로 달라지는지 더 선명하게 보일 것 같아요.
step1에서 이런 추상화를 생각지 못한 건 당연합니다. step1 시점에서는 콘솔 하나만 있었고, 앞으로 웹 UI가 붙을지, 인터페이스가 어떻게 달라질지 알 수 없었으니까요. step2를 진행하면서 두 환경의 공통점/차이점이라는 데이터가 쌓였기 때문에, 이번엔 LottoMachine 같은 서비스 레이어를 만들어낼 수 있었던 거라고 생각해요.
제가 다른 리뷰에서도 이런 경험의 중요성을 계속 강조하는데요. 추상화라는 건 결국엔 경험을 통해 예측 가능한 범위 안에서, 그 중에서도 합리적인 수준에서 미리 구조화하는 거거든요. 근데 경험이 부족한 상태에서 무리하게 추상화하게 되면 추후에 변경될 스펙과 완전히 동떨어진 구조가 되거나 대부분 쓰이지 않는 기능을 만들게 됩니다. 그러니 지금은 경험한 범위 안에서 최소한의 구조를 유지하되, 더 많은 경험을 쌓아가면서 그 범위를 점점 넓혀나가는 방향으로 성장하시면 좋겠습니다.
- 현재 각 View 클래스에서 직접 DOM을 쿼리하고 조작하고 있습니다. 처음 DOM을 다뤄보다 보니 이 방식이 최선인지, 혹은 컴포넌트 간의 의존성을 더 줄이거나 요소를 더 효율적으로 관리하는 일반적인 패턴이 있는지 궁금합니다.
사실 웹을 바닐라 JS로 개발하는 것 자체가 일반적인 상황은 아니어서, 제가 아는 선에서 dom 조작에 대한 권장 패턴은 딱히 없는 것 같습니다. 백지에서 이 정도 view 구조를 잡으신 것만 해도 괜찮은 접근이라고 생각합니다. 프레임워크 없이 작업할 때 이보다 더 나은 방법을 찾으려면 결국 미니 라이브러리를 만들어야 하는데, 요정도 미션 수준에서는 오버엔지니어링일 수 있다고 생각합니다.
좀 더 발전된 형태의 미니 라이브러리를 만들어보고 싶으시다면 본인에게 익숙한 프레임워크의 인터페이스를 모방해보는 것도 좋은 연습이 될 수 있을 것 같습니다. 저같은 경우는 지금 다니는 회사의 인턴 과제를 수행할 때 react의 createElement()의 인터페이스를 참고하고, 내부적으로는 간단하게 직접 구현한 가상돔을 적용해서 유사 리액트처럼 작동하는 작은 라이브러리를 만들어서 이용했었거든요. 당장 이런 것까지 해보실 필요는 없지만, 관심이 있으시다면 한번 도전해보시는 것도 좋다고 생각합니다. 직접 구현해보는 과정에서 DOM 조작과 프레임워크 실행 흐름에 대한 이해가 더 깊어질 수 있다고 생각되어요.
- 현재 index.html에 서비스의 초기 뼈대를 모두 작성해 두었습니다.현재 사용한 태그들이 시맨틱하게 적절하게 사용되었는지 궁금합니다.
사실 저희는 퍼블리셔분이 따로 계셔서, 요런 마크업 경험이 적어 질문에 답을 확실히 드릴 수 없을 것 같아요.. 다만 전반적으로 봤을 때 header/main/footer 구조나, fieldset/legend, aria-label 적용한 부분 등 적절히 써주시지 않았나 생각합니다.
요런 내용들은 MDN에서 괜찮은 가이드를 제공하기 때문에, mdn 문서로 학습해보는 것도 좋을 것 같습니다.
https://developer.mozilla.org/en-US/curriculum/core/semantic-html/
- 앱이 커질수록 index.html이 너무 비대해질 것 같다는 우려가 생겼습니다. HTML 파일 자체를 더 작게 쪼개거나(컴포넌트화), JS에서 템플릿을 통해 생성하는 비중을 어느 정도로 가져가는 것이 유지보수 측면에서 유리할까요?
사실 이 질문을 좀 더 보편적 관점에서 생각해보면, "어느 정도로 추상화를 진행해야 할까요?"라고도 해석할 수 있겠는데요. 1번 답변에서 말씀드렸듯 요것은.. 정말 경험의 영역이지 않을까 생각됩니다.
그래도 제가 개발할 때를 생각해보면 우선, 그 마크업이 항상 존재하는지 vs 동적으로 생성/제거되는지를 첫째 기준으로 삼는 것이 좋지 않을까 생각되구요. 지금 코드에서는 그 구분이 자연스럽게 되어 있는 것으로 보입니다.
그 다음으로는 같은 구조가 반복되는지를 보면 될 것 같아요. 로또 아이템 하나하나를 미리 작성해둘 수는 없으니 템플릿으로 찍어내는 게 맞고, 반대로 하나만 존재하는 금액 입력 폼같은 경우는 html에 두는 게 자연스럽지 않나 싶습니다.
좀 더 모던 프레임워크적인 측면에서 생각해보면, UI가 얼마나 자주 변경되는지, 상태에 얼마나 의존하는지와 같은 기준을 중요하게 볼 수도 있을 것 같습니다.
- 도메인 로직은 TDD로 구현했지만, 뷰 영역은 방법이 낯설어 시도하지 못했습니다. ui는 어디서부터 어디까지 테스트해야하는지 감이 잘 잡히지 않습니다.. 저와 같은 입문자가 UI TDD를 시작한다면 어떤 동작(렌더링 결과 vs 사용자 인터랙션)부터 어떤 과정으로 테스트 코드로 검증하는 것이 좋을지 가이드를 얻고 싶습니다.
UI 테스트를 처음 시작한다면, 저는 일단 초기 렌더링 결과나 DOM 구조 자체를 검증하는 것보다 사용자 인터랙션 이후 화면이 어떻게 바뀌는지 먼저 확인하는 쪽을 더 추천드릴 것 같아요.
아마 이후에 리액트를 사용하면서 UI 테스트를 작성해보실 것 같은데요. 테스트를 짜보면 느끼시겠지만, 렌더링 결과에 대한 단순 테스트는 의도치 않게 구현 디테일에 가까워지는 경우가 종종 있었습니다. 그래서 처음에는 값을 입력했을 때 값이 반영되는지, 버튼을 눌렀을 때 결과가 나타나는지, 잘못 입력했을 때 에러 문구가 보이는지처럼, 사용자 행동 이후 화면에 나타나는 변화를 기준으로 테스트를 작성해보시는 쪽이 더 감을 잡기 좋다고 생각합니다.
그리고 이건 살짝 오지랖이고 우테코에서 지향하는 바와 다를 수도 있는 말인데요. 테스트 자체에 익숙하지도 않고 개발 경험도 적은 상태에서 UI에 대한 TDD를 동시에 진행하려하면 꽤 많이 버거울 수 있을 거라고 생각해요.
TDD가 좋은 도구인 건 맞지만, 처음부터 TDD에 맞추려 하기 보다는 일단 사용자 입장에서 화면 동작을 검증하는 테스트에 먼저 익숙해져보시고, 그 후에 TDD로 확장해보시는 편이 좀 더 수월하지 않을까 생각합니다.
- 도메인을 건드리지 않기 위해 LottoMachine 서비스가 도메인 객체들을 생성 및 조합하고, 로직의 실행 흐름을 제어하도록 설계했습니다. 이것이 서비스 레이어로서 적절한 책임 범위였는지, 혹은 step2-app.js 컨트롤러만 있어도 충분했을지에 대해 궁금합니다.
요부분은 윗쪽 답변들에서 언급했었는데요. 잘 해주셨다고 생각합니다. step1 컨트롤러도 LottoMachine 이용해서 수정해보시면 좋을 것 같아요👍
| const moneyValue = this.$moneyInput.value; | ||
| handler(moneyValue); |
There was a problem hiding this comment.
[권장]
string 타입의 금액값을 그대로 handler에 전달하는데요, 값이 전달되는 로직을 쭉 타고 가보니 명시적으로 타입 변환을 처리하는 곳이 없는 것으로 보여요. 아마 타입스크립트였다면 미리 알아채고 잘 처리해주셨을 것 같지만, 이번 과제는 JS로 진행되는 만큼 좀 더 일관된 타입 처리를 할 수 있도록 하면 좋을 것 같습니다👍
| getReturnOnInvestment(amount) { | ||
| const roi = (this.getPrize() / amount) * 100; | ||
| return formatROI(roi); | ||
| } |
There was a problem hiding this comment.
[권장]
step1과 다르게 LottoResult 도메인에서 roi의 포맷을 함꼐 다루고 있어요. 근데 로또 시뮬레이션이라는 도메인 관점에서 생각해보면, roi값을 소수 몇자리 값으로 설정할지에 대한 처리가 정말 이 모델에서 다뤄야 할 관심사인가? 하는 의문이 드는 부분입니다.
요것과 유사한 맥락에서 다른 분의 PR에 달았던 코멘트가 있어 링크를 함께 첨부드립니다. 한번 읽어보셔도 좋을 것 같아요. ( #445 (comment) )
전반적으로 설계해주신 코드를 보면 요런 도메인의 책임 범위에 대해서는 이미 잘 이해하고 계신다는 생각이 들어요. 아마 이번 코드에서도 이런 관점을 모르셨다기 보다는, number 타입을 유지하면서 소수 둘째자리 밑으로 정리하는 단순한 처리이기에 도메인 내에 있어도 괜찮을 것이라 판단하고 넣으신 게 아닐까 싶습니다. 물론 저도 요 방식이 틀렸다고 생각하지는 않고, 기획 요구사항과 서비스 설계에 따라 충분히 가능한 선택지라고 생각합니다. 저도 실무에서 비슷하게 도메인 구조를 설계했던 경험이 있구요.
다만 이번 과제 정도의 사이즈를 고려해보면, 이런 값의 표현 책임은 도메인보다 view에서 담당하는 편이 좀더 자연스럽다고 생각이 들어 코멘트 드리게 되었습니다.
| <title>🎱 행운의 로또</title> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Document</title> | ||
| <link rel="stylesheet" href="./src//styles/main.css" /> |
There was a problem hiding this comment.
[필수]
경로에 슬래시가 두 개 들어있습니다. 수정이 필요할 것 같아요.
학습 목표
이번 미션을 통해 다음과 같은 학습 경험들을 쌓는 것을 목표로 합니다.
제출 전 체크 리스트
리뷰 요청 & 논의하고 싶은 내용
안녕하세요 데구리! 리뷰 요청이 좀 늦어졌습니다 😅
제가 이번 단계에서 고민하고 던진 질문들이 적절한 수준인지, 혹은 제가 놓치고 있는 더 중요한 본질이 있는지는 아닌지 걱정이 되기도 합니다. 데구리님이 보시기에 답변 가능한 부분 위주로 편하게 피드백 주시면 감사하겠습니다. 전체적으로 제 코드가 어떤 방향으로 나아가고 있는지, 그리고 앞으로 제가 한 단계 더 성장하기 위해 어떤 점을 보완하면 좋을지 따끔하고 아낌없는 조언 부탁드립니다!
1) 이번 단계에서 가장 많이 고민했던 문제와 해결 과정에서 배운 점
이번 단계의 가장 큰 도전 과제는 Step 1에서 공들여 작성한 도메인 로직을 단 한 줄도 수정하지 않으면서 웹 UI를 구현하는 것이었습니다. Step 1의 step1-app.js는 콘솔 환경에 강하게 결합되어 있어, 이를 웹 환경으로 확장하려 할 때 인터페이스의 차이를 어떻게 극복해야 할지 고민이 많았습니다. 결국 기존 로직을 수정하는 대신, 도메인과 View 사이에서 데이터 흐름을 중재하고 가공하는 LottoMachine 서비스를 새롭게 도입하여 도메인 모델의 순수성을 유지했습니다. 결과적으로, 도메인로직 수정 없이, step1, step2 모두 정상동작하는 결과물을 만들었습니다.
바닐라 JS로 웹을 처음 구현하며 **"어디까지 HTML에 미리 작성하고, 어디서부터 JS로 동적 생성할 것인가"**에 대한 균형을 잡는 것이 어려웠습니다. 초기에는 모든 뼈대를 index.html에 담으려 했으나, 앱이 복잡해짐에 따라 관리가 어려워질 수 있다는 점을 인지했습니다. 이를 해결하기 위해 주요 골격은 시맨틱하게 유지하되, 반복되거나 상태에 따라 변하는 UI는 독립적인 View 클래스로 쪼개어 관리하며 구조적인 안정감을 찾으려 노력했습니다. 이 과정에서 BEM 네이밍 규칙과 CSS 계층화를 적용하며 유지보수 가능한 UI 설계의 중요성을 배웠습니다.
2) 이번 리뷰를 통해 논의하고 싶은 부분
앱이 커질수록 index.html이 너무 비대해질 것 같다는 우려가 생겼습니다. HTML 파일 자체를 더 작게 쪼개거나(컴포넌트화), JS에서 템플릿을 통해 생성하는 비중을 어느 정도로 가져가는 것이 유지보수 측면에서 유리할까요?
✅ 리뷰어 체크 포인트
1단계
2단계