Skip to content

[2단계 - 웹 기반 로또 게임] 안톨리니 미션 제출합니다.#462

Open
Antoliny0919 wants to merge 71 commits intowoowacourse:antoliny0919from
Antoliny0919:step2
Open

[2단계 - 웹 기반 로또 게임] 안톨리니 미션 제출합니다.#462
Antoliny0919 wants to merge 71 commits intowoowacourse:antoliny0919from
Antoliny0919:step2

Conversation

@Antoliny0919
Copy link

@Antoliny0919 Antoliny0919 commented Mar 14, 2026

학습 목표

이번 미션을 통해 다음과 같은 학습 경험들을 쌓는 것을 목표로 합니다.

  • UI와 도메인 영역을 분리할 수 있는 설계를 고민해보고, 목적에 맞게 객체와 함수를 활용
  • TDD 방식으로 개발하며, 단위 테스트 기반으로 점진적인 리팩터링

제출 전 체크 리스트

  • 기능 요구 사항을 모두 구현했고, 정상적으로 동작하는지 확인했나요?
  • 기본적인 프로그래밍 요구 사항을 준수하고 있는지 확인했나요?
  • 테스트 코드는 모두 정상적으로 실행되나요?
  • (해당하는 경우) 배포한 데모 페이지에 정상적으로 접근할 수 있나요?

리뷰 요청 & 논의하고 싶은 내용

1) 이번 단계에서 가장 많이 고민했던 문제와 해결 과정에서 배운 점

리팩토링 과정에서 어떤 방식으로 전환할지 고민이 많이 되었다.
기존에는 document.createElement를 통해 노드를 하나씩 만드는 구조였다.
해당 구조보다 문자열을 사용하여 태그를 렌더링하는게 더 가독성적인 면에서 좋을거라고 판단했다.
예상한대로 문자열인 상태로 전환했을때 가독성 적인 부분이 크게 개선되었다.

하지만 한 가지 고민이 존재하다면 현재 입력값이 렌더링될 컴포넌트의 일부가 되는데 지금 상태는 검증기와 값을 변환하는 과정이 있어 괜찮지만 만약 존재하지 않다면 XSS와 같은 공격에 취약하지 않을까라는 생각도 들었다.

리팩토링 전 코드는 index에 Component를 제외한 로직이 전부 존재했다.
이 로직을 Controller와 View로 전환하면서 각 부분에 어떤 로직이 들어가는게 좋을지 고민해본거 같다.
Controller에는 각 부분의 전체적인 흐름인 유효성 검사, 도메인 객체 생성, View 호출 순서 관리등을 담았다.
View에는 DOM 조작과 렌더링만 하도록 구현했다.
index에는 이벤트리스너에 대한 내용만 담았다.
이렇게 분리했을때 정말 지저분했던 index가 깔끔하게 분리될 수 있었다.

2) 이번 리뷰를 통해 논의하고 싶은 부분

  1. View에서 DOM조작이 이루어지고 있습니다. 어디서 DOM조작이 이루어지면 좋을지 고민이 많았는데 적절한지 궁금합니다.

  2. 현재 구조에서는 문제 없지만 만약 form이 추가로 확장되었을때 확장 될 때마다 ErrorMessage와 관련된 View로직이 추가되어야할걸로 예상됩니다. 이 부분은 어쩌면 확장을 두려워하게 만들 수 있다고 느껴집니다. Form과 관련된 컴포넌트를 추가하는게 좋을까요?

  3. 입력된 값이 컴포넌트의 일부가 됩니다. 현재는 검증과 값의 변환로직이 있어 괜찮지만 만약 단순한 입력이 존재했을때 XSS같은 공격으로부터 취약하지 않을까? 라는 생각이 듭니다. 다른 프레임워크의 경우에는 이스케이핑을 통해 문제를 해결하는걸 봐왔는데 만약 이러한 상황에 놓인다면 어떤 처리를 통해 보안적으로 더 탄탄하게 만들 수 있을까요?

  4. 개인적으로 파일을 잘게 나누기보다 한 곳에서 흐름을 읽을 수 있는 코드를 선호합니다. 파일 분리는 각 파일을 단순하게 만들지만, 전체 흐름 파악을 위한 컨텍스트 이동 비용이 생긴다고 느끼기 때문입니다. 이 부분은 개인차가 있을 것 같기는 하지만... 리뷰어분은 어떤 기준으로 파일 분리 여부를 판단하시는지 궁금합니다.


✅ 리뷰어 체크 포인트

1단계

  • TDD를 활용해 기능을 구현하는 과정에서 적절한 테스트 우선 접근 방식을 적용했는가? 단위 테스트의 커버리지는 충분한가?
  • 도메인과 UI의 관심사를 분리하여 적절한 모듈화가 이루어졌는가? 하나의 객체나 모듈이 너무 많은 책임을 가지고 있지는 않은가?
  • 객체의 프로퍼티를 직접 조작하기보다 메시지를 던지고 있는가?
  • 불필요한 클래스를 사용하지 않고, 함수를 적극적으로 활용하여 JavaScript다운 방식으로 로직을 구현했는가?

2단계

  • 도메인 로직에 불필요한 영향을 주지 않고 UI 변경에 대응했는가?
  • DOM 조작과 이벤트 활용을 JavaScript의 개념에 맞게 이해하고 적절하게 적용했는가?
  • 웹 표준을 준수하는 마크업을 활용하며, 스타일 작성에 일관성이 있는가?

@Antoliny0919 Antoliny0919 changed the base branch from main to antoliny0919 March 14, 2026 15:41
@Antoliny0919 Antoliny0919 marked this pull request as ready for review March 14, 2026 16:01
Copy link

@JUDONGHYEOK JUDONGHYEOK left a comment

Choose a reason for hiding this comment

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

안녕하세요 안톨리니! 이번 2단계도 깔끔하게 구현해주셨군요 테스트까지 추가로 꼼꼼하게 작성해주셔서 감명깊게 보았습니다!

현재 구조에서 모듈간의 역할이 명확하게 나뉘어져 있고 1단계 도메인코드를 거의 수정없이 잘사용해주셔서 좋았던 것 같아요! 다만 View가 기능이 늘어날수록 비대해질 여지가 있어보여서 요부분을 의식하시면서 리팩토링 해보시면 좋을 것 같아요!

Q&A

View에서 DOM조작이 이루어지고 있습니다. 어디서 DOM조작이 이루어지면 좋을지 고민이 많았는데 적절한지 궁금합니다.

넵 View에서 DOM조작은 본질적으로 화면을 어떻게 바꿀것인가에 대한 관심사 이기 때문에 해당 책임을 View에 모은 것은 자연스럽다고 생각합니다!

현재 구조에서는 문제 없지만 만약 form이 추가로 확장되었을때 확장 될 때마다 ErrorMessage와 관련된 View로직이 추가되어야할걸로 예상됩니다. 이 부분은 어쩌면 확장을 두려워하게 만들 수 있다고 느껴집니다. Form과 관련된 컴포넌트를 추가하는게 좋을까요?

제 생각에 지금 renderPurchaseAmountErrorMessage와 renderWinningLottoNumberErrorMessage는 사실상 같은 일을 하고 있는데 대상 컨테이너만 달라져서 그렇게 느끼신 것이 아닐까 생각이 됩니다! 말씀하신대로 Form자체를 하나의 단위로 묶어서 캡슐화 해도 좋은 시도가 될 수 있을 것 같아요 하지만 역시나 말씀하신대로 현재 구조에서는 오히려 추상화 비용이 더 클 수 있어서 안톨리니가 고려하시면서 개선해보면 좋을 것 같네요!

입력된 값이 컴포넌트의 일부가 됩니다. 현재는 검증과 값의 변환로직이 있어 괜찮지만 만약 단순한 입력이 존재했을때 XSS같은 공격으로부터 취약하지 않을까? 라는 생각이 듭니다. 다른 프레임워크의 경우에는 이스케이핑을 통해 문제를 해결하는걸 봐왔는데 만약 이러한 상황에 놓인다면 어떤 처리를 통해 보안적으로 더 탄탄하게 만들 수 있을까요?

입력값이 템플릿 리터럴에 바로 들어가면 말씀하신대로 문제가 생길 수 있죠. 가장 간단한 해결책은 createElement와 textContent를 쓰는 방식들이 해결책이 될 수 있을 것 같아요

개인적으로 파일을 잘게 나누기보다 한 곳에서 흐름을 읽을 수 있는 코드를 선호합니다. 파일 분리는 각 파일을 단순하게 만들지만, 전체 흐름 파악을 위한 컨텍스트 이동 비용이 생긴다고 느끼기 때문입니다. 이 부분은 개인차가 있을 것 같기는 하지만... 리뷰어분은 어떤 기준으로 파일 분리 여부를 판단하시는지 궁금합니다.

가장 간단하게 판단하는 기준은 변경의 이유가 다르면 분리하고 항상 같이 바뀌면 함께 두는 것 같아요. 예를 들어 지금 구조에서 component.js와 view.js는 화면이 바뀌면 같이 수정된다는 점에서 합쳐도 될 수 있다고 생각한느데 controller와 view는 도메인 흐름이 바뀔때와 렌더링 방식이 바뀔 떄 라는 서로 다른 이유로 수정되기 때문에 분리되는 것이 맞다고 생각해요!

현재 재시도 로직에서 버그가 발생하고 있습니다! 요걸 우선 해결해보시면 좋을 것 같아요! 수고많으셨습니다 안톨리니!


renderPurchaseAmountErrorMessage(message) {
const errorMessageComponent = Component.errorMessage(message);
this.purchaseAmountErrorMessageContainer.innerHTML = errorMessageComponent;

Choose a reason for hiding this comment

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

재시도시 여기서 에러가 나고 있어요 확인이 필요해보입니다!

color: var(--error-fg);
}

.hidden {

Choose a reason for hiding this comment

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

hidden을 opacity로 조절하신 이유가 있으실까요?

Validator.validateLottoNumber(winningLottoNumber);
Validator.validateBonusNumber(winningLottoNumber, bonusNumber);
const winningLotto = new WinningLotto(winningLottoNumber, bonusNumber);
this.lottoMachine.calculateMatchResult(

Choose a reason for hiding this comment

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

여기서는 lottoMachine의 유무를 체크하지 않네요? 의도가 있으신것일까요?

this.matchResultDialog.close();
},

convertHiddenState(targets) {

Choose a reason for hiding this comment

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

명시적인 show, hide대신에 toggle로 구성해줏니 이유가 있으실까요?

@@ -5,12 +5,75 @@
<title>🎱 행운의 로또</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>

Choose a reason for hiding this comment

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

안톨리니가 구현한 코드는 아니지만 title이 두개 있어요 수정되면 좋을 것 같습니다

<div id="purchase-amount-input-container">
<label for="purchase-amount" class="text-body">구입할 금액을 입력해주세요.</label>
<div class="row-line-between">
<input id="purchase-amount" class="font-placeholder" placeholder="금액" required>

Choose a reason for hiding this comment

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

font-placeholder라는 클래스는 정의해주신 이유가 있으실까요?


#purchase-lotto-content {
height: 250px;
overflow: scroll;

Choose a reason for hiding this comment

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

overflow: scroll로 지정해주신 이유가 있으실까요?

Comment on lines +28 to +33
const expected = '<table id="lotto-match-result">'
+ '<thead><th>일치 갯수</th><th>당첨금</th><th>당첨 갯수</th></thead>'
+ '<tbody><tr><td>3개 일치</td><td>500</td><td>3</td></tr>'
+ '<tr><td>4개 일치</td><td>1,000</td><td>100</td></tr>'
+ '</tbody></table>'
expect(lottoMatchResultComponent).toEqual(expected);

Choose a reason for hiding this comment

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

꼼꼼하게 테스트 코드를 추가해주셨네요 좋습니다! 다만 문자열 완전일치만 검증해서 리팩토링 내성이 낮은데요. 테스트하는 주요 DOM 구조 중심 검증으로 바꾸면 유지보수성이 좋아질 것 같은데 어떻게 생각하세요?

View.openModal();
View.renderMatchResultModal(
Converter.matchResultSummary(this.lottoMachine.getMatchResultSummary()),
this.lottoMachine.getRateOfReturn(),

Choose a reason for hiding this comment

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

제출 후에 다른 form들을 disabled하는 것은 어떠세요?

@@ -0,0 +1,11 @@
const Converter = {

Choose a reason for hiding this comment

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

요 Convert를 분리해주신 의도가 궁금해요!

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.

2 participants