Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
6490d2d
docs(README): 기능 구현 목록 작성
janghw0126 Mar 4, 2026
825e3bb
docs(README): 기능 목록 수정
janghw0126 Mar 4, 2026
a2c38a8
test(InputConsoleTest): 로또 구입 금액 입력 테스트
janghw0126 Mar 4, 2026
a475a50
feat(InputConsole): 로또 구입 금액 입력 구현
janghw0126 Mar 4, 2026
8e92530
fix(InputConsole): 에러메시지 출력 기능 추가
janghw0126 Mar 4, 2026
df15a56
test(validatePurchasePrice): 로또 구입 금액 검증 테스트 추가
janghw0126 Mar 4, 2026
ad6236e
feat(Validator): 로또 구입 금액 검증 기능 추가
janghw0126 Mar 4, 2026
1da8c56
test(InputConsoleTest): 당첨 번호 입력 테스트 추가
janghw0126 Mar 4, 2026
a9cdbb9
feat(InputConsole): 당첨 번호 입력 기능 추가
janghw0126 Mar 4, 2026
c87acae
docs(README): 당첨 번호 입력 목록 체크
janghw0126 Mar 5, 2026
74ce231
feat(App): 구입금액과 당첨번호 실행 로직 추가
janghw0126 Mar 5, 2026
756b358
test(ValidatorTest): 당첨번호 검증 테스트 추가
janghw0126 Mar 5, 2026
cae3a60
feat(Validator): 당첨번호 검증 기능 추가
janghw0126 Mar 5, 2026
1e9ca22
test(InputConsoleTest): 당첨번호 파싱에 의한 테스트 수정
janghw0126 Mar 5, 2026
0a50f9a
test(DomainTest): 로또 클래스 객체 생성 테스트 추가
janghw0126 Mar 5, 2026
aa3684f
feat(Domain): 로또 객체 생성 기능 추가
janghw0126 Mar 5, 2026
4b8628f
docs(README): 로또 실행 로직 목록 수정 및 당첨 번호 검증 목록 체크
janghw0126 Mar 5, 2026
e4311ef
test(DomainTest): 로또 발행 테스트 추가
janghw0126 Mar 5, 2026
8aa9d3a
feat(LottoMachine): 로또 발행 기능 추가
janghw0126 Mar 5, 2026
becac35
docs(README): 로또 발행 기능 목록 체크
janghw0126 Mar 5, 2026
9120a54
test(InputConsoleTest): 보너스 입력 테스트 추가
janghw0126 Mar 5, 2026
2cf3e34
feat(InputConsole): 보너스 번호 입력 기능 추가
janghw0126 Mar 5, 2026
4e1f7b3
test(ValidatorTest): 보너스 번호 검증 테스트 추가
janghw0126 Mar 5, 2026
15e6c29
feat(Validator): 보너스 번호 검증 기능 추가
janghw0126 Mar 5, 2026
a9f3627
test(InputConsoleTest): 보너스번호 검증에 의한 테스트 수정
janghw0126 Mar 5, 2026
237bda3
test(Domain): 등수 집계 테스트
janghw0126 Mar 5, 2026
c9fb3df
feat(WinningNumbers): 당첨번호와 보너스번호 관리를 위한 클래스 추가
janghw0126 Mar 5, 2026
d98f9e0
feat(LottoResult): 등수 집계 기능 추가
janghw0126 Mar 5, 2026
5850d76
test(DomainTest): 수익률 검증 테스트 추가
janghw0126 Mar 5, 2026
e031f94
feat(Constants): 등수에 따른 상금 상수화 추가
janghw0126 Mar 5, 2026
4ae0f19
feat(LottoResult): 총 수익률 계산 기능 추가
janghw0126 Mar 5, 2026
3628f2b
test(OutputConsoleTest): 로또 번호 출력 테스트 추가
janghw0126 Mar 5, 2026
597e827
feat(OutputConsole): 로또 목록 출력 기능 추가
janghw0126 Mar 5, 2026
111872e
test(OutputConsoleTest): 당첨 내역 출력 검증 테스트 추가
janghw0126 Mar 5, 2026
32331dc
feat(Connstants): 등수 출력 형식 상수화 추가
janghw0126 Mar 5, 2026
4f385e1
feat(OutputConsole): 당첨 통계 기능 추가
janghw0126 Mar 5, 2026
fe8a73d
fix(LottoResult): 수익률 계산 방식 수정
janghw0126 Mar 6, 2026
998b00a
test(OutputConsoleTest): 총 수익률 출력 검증 테스트 추가
janghw0126 Mar 6, 2026
57d0d89
feat(OutputConsole): 총 수익률 출력 기능 추가
janghw0126 Mar 6, 2026
b8c912a
style(LuckyNumbers): 보너스 번호와 당첨번호를 합친 객체 이름 변경
janghw0126 Mar 6, 2026
0784a9e
style(WinningNumber): 파일명 변경에 의한 파일 삭제
janghw0126 Mar 6, 2026
22320c4
test(InputConsoleTest): 재시작 입력 검증 테스트 추가
janghw0126 Mar 6, 2026
d4e3737
feat(InputConsole): 재시작 입력 기능 추가
janghw0126 Mar 6, 2026
f981ef4
feat(App): 로또 실행 로직 추가
janghw0126 Mar 6, 2026
6e9d5a1
refactor(App): 출력 형식에 맞게 코드 수정
janghw0126 Mar 6, 2026
7093e1c
refactor(LottoResult): let 변수 선언을 const 선언으로 변경
janghw0126 Mar 6, 2026
8d50e38
refactor(Validator): 로또 번호 검증 중복 로직 제거
janghw0126 Mar 6, 2026
4348327
docs(README.md): 기능 목록 체크
janghw0126 Mar 6, 2026
c65dc27
docs(pull_request_template.md): 제출용 리드미 작성
janghw0126 Mar 6, 2026
93b1f2d
docs(pull_request_template): 논의하고 싶은 부분 추가
binggwa Mar 6, 2026
f50e442
refactor(LottoMachine): 불필요한 클래스 제거 및 실행흐름 수정
binggwa Mar 9, 2026
6be25c3
refactor(LottoMachine) : 불변성 유지를 위해 createLotto()의 sort()를 toSorted()…
binggwa Mar 9, 2026
4d046e1
chore: package-lock.json 업데이트
binggwa Mar 9, 2026
9c0693a
refactor(Validator) : 엄격한 형변환을 위해 Number.isNaN 적용
binggwa Mar 9, 2026
221c69f
feat(OutputConsole) : 에러메시지 출력을 위한 메서드 추가
binggwa Mar 9, 2026
ddd7d34
refactor(InputConsole,LottoMachine,Validator,App) : 구입금액 검증기능을 inputC…
binggwa Mar 9, 2026
d172531
refactor(Lotto,Validator) : 로또 숫자 검증을 Lotto의 도메인 규칙으로 변경, 그에 따른 Valid…
binggwa Mar 9, 2026
428da2a
refactor(LuckyNumbers,Validator) : 당첨번호와 보너스번호 검증 기능을 LuckyNumbers로 이…
binggwa Mar 9, 2026
2cedfad
feat(Lotto) : winningLotto객체가 번호를 조회할 수 있게 getNumbers() 메서드 추가
binggwa Mar 9, 2026
b86332b
test(DomainTest) : 코드변경에 따른 테스트코드 변경
binggwa Mar 9, 2026
dd51092
fix(LuckyNumbers) : 타입이 달라 중복검증 기능이 작동하지 않던 버그 수정
binggwa Mar 9, 2026
aad5639
fix(Lotto) : 당첨번호 검증이 문자열을 잡아내지 못하던 버그 수정
binggwa Mar 9, 2026
5b1e19f
refactor(InputConsole,App) : InputConsole은 표시만, 핵심 로직인 보너스번호 검증을 Luck…
binggwa Mar 9, 2026
f0d2285
refactor(Lotto,OutputConsole) : 자바스크립트 내장메서드 혼선방지를 위한 toString() 메서드명…
binggwa Mar 9, 2026
5926ed3
refactor(LottoMachine,App) : 모킹 없이 테스트하기 위해 의존성 주입
binggwa Mar 9, 2026
258a38a
test(DomainTest) : 의존성 주입을 통한 LottoMachine 테스트 모킹 없이 변경
binggwa Mar 9, 2026
a4f4968
refactor(LottoResult,App) : LottoResult를 클래스에서 const로 변경
binggwa Mar 9, 2026
a0eac70
test(OutputConsoleTest) : toString 메서드명 변경에 의한 테스트코드 변경
binggwa Mar 9, 2026
4f7ff9f
refactor(LuckyNumbers) : 보너스번호를 검증한뒤 바로 받게 변경
binggwa Mar 9, 2026
62d9b89
test(DomainTest) : 리팩토링에 따른 테스트코드 변경
binggwa Mar 9, 2026
f4b8aac
test(InputConsoleTest) : 리팩토링에 따른 테스트코드 변경
binggwa Mar 9, 2026
7d842d8
test(ValidationTest) : 리팩토링에 따른 테스트코드 변경
binggwa Mar 9, 2026
0d85564
feat(index) : 웹 UI 기본 HTML 틀 작성-header,main,footer
binggwa Mar 11, 2026
8b2a62f
feat(index) : 로또구입금액 입력창 틀 작성 및 네이밍
binggwa Mar 11, 2026
564a661
feat(index) : 구매한 로또 목록 출력용 공간 추가
binggwa Mar 11, 2026
03567c1
feat(index) : luckynumbers 입력창 추가 및 결과확인 버튼 추가
binggwa Mar 11, 2026
e01ee59
feat(index) : 당첨결과 모달창 및 재시작 버튼 추가
binggwa Mar 11, 2026
e1b6b6b
Merge branch 'temp-step2' into step2
binggwa Mar 12, 2026
eeee9c5
feat(step2-index) : WebApp 진입점 추가
binggwa Mar 12, 2026
52b55ce
feat(WebApp) : 로또 금액 입력 폼 및 티켓 목록 렌더링 기능 구현
binggwa Mar 12, 2026
1062346
refactor(WebApp) : 오류메시지 출력기능 alert로 변경
binggwa Mar 12, 2026
f124d88
design(style) : 로또구입금액 입력 및 출력 디자인
binggwa Mar 12, 2026
6441788
design(style) : luckynumbers 입력창 및 결과 확인 버튼 디자인
binggwa Mar 12, 2026
70d5e2d
feat(WebApp) : luckyNumbers 제출 및 결과 계산, 출력 기능 추가
binggwa Mar 12, 2026
2a0a128
feat(WebApp) : lucky-numbers-form 에 hidden 속성 추가
binggwa Mar 13, 2026
38cb483
feat(WebApp) : 모달 창 닫기 버튼 및 재시작 버튼 기능 추가
binggwa Mar 13, 2026
08621c3
design(style) : 모달 창 전체 디자인 추가
binggwa Mar 13, 2026
f2a32ee
design(style) : app을 추가해 main-container 초기화면이 화면을 세로로 꽉 채우도록 개선
binggwa Mar 13, 2026
94ad1e2
refactor(LottoWebView) : DOM요소를 가져오는 역할을 LottoWebView클래스로 분리
binggwa Mar 13, 2026
4e55d74
refactor(LottoWebView) : rendorLottos로 로또 목록 HTML 업데이트 기능 리팩토링
binggwa Mar 13, 2026
a21c023
refactor(LottoWebView) : 모달 창 렌더링 기능 리팩토링
binggwa Mar 13, 2026
3186e2d
refactor(LottoWebView) : 모달 창 닫기 기능 리팩토링
binggwa Mar 13, 2026
e7172a3
refactor(LottoWebView) : 로또 다시 시작 기능 리팩토링
binggwa Mar 13, 2026
de3a78e
refactor(LottoWebView) : 오류 출력 기능 리팩토링
binggwa Mar 13, 2026
23eedca
refactor(LottoWebView) : 구입 폼 관련 바인딩 책임을 View로 리팩토링
binggwa Mar 14, 2026
97c25c8
refactor(LottoWebView) : luckyNumbers 제출 폼 관련 바인딩 책임을 View로 리팩토링
binggwa Mar 14, 2026
391bd6d
refactor(LottoWebView) : 모달 창 닫기 버튼 관련 바인딩 책임 View로 리팩토링
binggwa Mar 14, 2026
25ec207
refactor(LottoWebView) : 재시작 버튼 관련 바인딩 책임 View로 리팩토링
binggwa Mar 14, 2026
ba5f970
refactor(LottoWebView) : hidden 속성 표시,숨김 도우미 메서드로 직관성 확보
binggwa Mar 14, 2026
5907668
refactor(Constants) : 매직넘버 Constants로 리팩토링
binggwa Mar 14, 2026
c45b7e2
refactor(LottoWebView) : renderResultModal 하드코딩 리팩토링 > Constants를 사용하…
binggwa Mar 14, 2026
e3b87f7
style : CSS 속성 정렬 컨벤션 사용
binggwa Mar 14, 2026
6fa9148
style : input box의 화살표 스피너 제거로 디자인 수정
binggwa Mar 14, 2026
c9e612c
fix(Lotto) : Constants.js import 수정
binggwa Mar 14, 2026
1e5c4c2
chore(package) : 배포를 위한 package.json homepage 설정
binggwa Mar 14, 2026
23186e1
docs(README) : README 업데이트
binggwa Mar 14, 2026
c9adc9d
docs(pull_request_template2) : PR 템플릿 작성
binggwa Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/pull_request_template2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## 학습 목표
이번 미션을 통해 다음과 같은 학습 경험들을 쌓는 것을 목표로 합니다.
- UI와 도메인 영역을 분리할 수 있는 설계를 고민해보고, 목적에 맞게 객체와 함수를 활용
- TDD 방식으로 개발하며, 단위 테스트 기반으로 점진적인 리팩터링

## 제출 전 체크 리스트
<!-- 리뷰를 요청하기 전, 다음 항목들을 기본적으로 확인해 주세요. -->
- [x] 기능 요구 사항을 모두 구현했고, 정상적으로 동작하는지 확인했나요?
- [x] 기본적인 프로그래밍 요구 사항을 준수하고 있는지 확인했나요?
- [x] 테스트 코드는 모두 정상적으로 실행되나요?
- [x] (해당하는 경우) 배포한 데모 페이지에 정상적으로 접근할 수 있나요?
- 배포한 링크 기입: https://binggwa.github.io/javascript-lotto

## 리뷰 요청 & 논의하고 싶은 내용
<!-- PR 작성자로서, 코드만으로는 알기 어려운 작성자의 의도와 문제 해결 과정에 대해 공유해 주세요.
이 PR에서 달성해야 하는 학습 목표를 잘 달성하고 있는지 스스로 확인하며, 피드백 받을 수 있는 내용일지 점검해 보세요.
리뷰어에게 정답을 묻기보다 고민하고 의사 결정한 과정에 대해 공유하고 이에 대한 피드백을 받으며 대화해보기를 권장합니다. -->

### 1) 이번 단계에서 가장 많이 고민했던 문제와 해결 과정에서 배운 점
<!-- 구현 과정에서 가장 어려웠던 점이나 많이 고민한 점은 무엇인가요?
이를 해결하기 위해 어떤 방법들을 검토하고 시도했으며, 그 과정에서 새롭게 배운 점이 있나요? -->
1단계 미션에서 작성한 Domain 로직을 단 1도 수정하지 않고 진행하는 것이 주 목표였고, 실제로 매직넘버를 조금 분리한 것 외에 핵심적인 도메인 로직은 하나도 변경하지 않고 웹 UI를 만들어서 연결할 수 있었다. 관심사 분리가 잘 되었던 것 같아 다행이었다. 또, Web에서도 MVC 패턴을 어떻게 적용하는 지에 대해 고민했었는데, 처음에는 일단 WebApp 컨트롤러에 모든 로직을 담아 일단 돌아가는 코드를 만들고, 잘 돌아가는 것을 확인한 뒤에 LottoWebView를 따로 만들어 View의 역할을 담당하는 로직을 분리했다. 수업을 통한 이전 기수들의 리뷰를 통해 View가 담당할 규칙에 대해 다음과 같은 사실을 배울 수 있었다.
- Model은 View를 알지 못하므로, DOM 접근 코드가 존재해서는 안된다.
- View는 화면에 보여주는 역할만 수행해야 한다는 관점이었지만, 프론트엔드 환경에서 View의 역할에는 사용자와의 상호작용(이벤트)을 감지하는 것도 포함된다.
- Controller의 DOM 의존도를 제거하기 위해 View 로 eventhandler를 모두 넘긴다.
해당 리뷰들을 통해서 Web 환경에서의 View 분리에 대해 더 잘 알게 되었다고 생각한다.

### 2) 이번 리뷰를 통해 논의하고 싶은 부분
<!-- 구현한 코드와 학습 목표와 관련해 피드백을 받고 싶은 부분이나, 함께 논의해보고 싶은 점 -->
이번 단계에서 고민했던 문제는 리팩토링 수준과 figma 시안의 준수정도였습니다.
- figma에 나와있는 크기를 모두 반영해서 픽셀 단위로 UI를 만들면 수치를 넣을 때는 편할 수 있었지만, 현재 제 모니터에서 보이는 UI가 굉장히 보기 힘든 크기로 배치되기도 했으며, 웹을 제작시 픽셀로 크기를 고정한다면 모바일과 같은 다른 플랫폼에서 보면 혹시 이상하게 표시되지는 않을까..? 하는 고민과 함께 rem을 사용하는 것으로 일부 바꿨으나, css 파일 전체적으로 단위가 통일되지 않는 점과 함께 figma 시안의 수치를 정확하게 지키지 못했던 점이 조금 아쉬웠습니다. 혹시 리뷰어님도 실무를 진행하실 때에 figma 시안을 보고 작성하는 일이 많으실까요? 보신다면 figma 시안의 참고정도는 어떤 정도가 적당한지 궁금합니다. 사용자 편의성에 맞춘다는 생각은 있어도 임의로 정해진 시안을 조금 변경하는 것이 과연 괜찮은 것인지 의문이 들었습니다.
- 또, JS의 경우 컨트롤러인 WebApp과 View인 LottoWebView의 경우 개인적으로는 잘 나누었다고 생각했는데, 더 잘게 나누는 방법도 좋으려나 싶은 고민과 함께 제가 보지 못한 리팩토링 거리가 더 있는지 여쭙고 싶습니다.
- html과 css의 경우도 다루는 것이 익숙하지 않다보니 꽤 더럽게 짰다고 생각되어, 리팩토링을 어떤 식으로 하는 게 좋을지 감이 잡히지 않아 리뷰어분이 사용하시는 컨벤션이나 규칙이 따로 있는지 궁금합니다! css를 만들 때, html의 초기 태그에 스타일 클래스는 많이 붙이지 않았었지만, 만들면서 점점 어떤 스타일이 이미 적용되어있는지 헷갈리고 포장되어 있는 박스가 헷갈려 결국 대부분의 태그에 class를 하나하나 이름붙여서 css를 속성별로 나열하는 스타일이 되어버렸습니다.. 이런 방식이 분명 효율적이진 않아보여 어떤 원칙을 세우고 만드는 것이 더 효율이 잘나오는지 궁금합니다!!
- css의 경우 속성 순서를 준수하라는 요구사항이 있어 1. 레이아웃 관련 2. 박스 모델 3. 시각적 장식(배경/테두리) 4.타이포그래피 의 순서로 나누려고 했는데, 잘 되었는지, 혹시 다른 방법도 있는지 궁금합니다.
---

<details>
<summary>✅ 리뷰어 체크 포인트</summary>
<!-- 리뷰어가 이 PR을 검토할 때 중점적으로 확인할 사항입니다.-->

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

### 2단계
- [ ] 도메인 로직에 불필요한 영향을 주지 않고 UI 변경에 대응했는가?
- [ ] DOM 조작과 이벤트 활용을 JavaScript의 개념에 맞게 이해하고 적절하게 적용했는가?
- [ ] 웹 표준을 준수하는 마크업을 활용하며, 스타일 작성에 일관성이 있는가?
</details>
62 changes: 62 additions & 0 deletions README2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# 🚀 2단계 - 웹 기반 로또 게임
## 요구 사항
### 웹 UI를 사용해 로또 게임의 주요 기능을 사용할 수 있어야 한다.
- [x] 로또 구매
- [x] 구매한 로또 목록 확인
- [x] 당첨 번호 및 보너스 번호 지정
- [x] 당첨 통계 확인
- [x] 게임 재시작

### UI
- [x] figma 시안을 기준으로 구현한다.
- [x] 주어진 디자인은 시각적으로 그대로 구현한다.
- [x] 구체화한 요구 사항에 따라 추가로 필요한 UI가 있다면 스스로 적절히 판단하여 구현한다.

### 프로그래밍 요구 사항
- [x] 모듈화에 대해 고민한다. - 도메인과 UI 관심사의 분리
- [x] 1단계에서 구현한 도메인 로직을 (최대한) 수정하지 않고, UI만 변경한다.
- [x] 일관성 있고 의도가 드러나는 마크업을 작성하기 위해 노력한다.
- [x] 목적에 맞는 HTML 태그를 사용한다.
- [x] CSS 속성 선언 순서의 일관성을 고려한다.
- [x] CSS 문법 사용에 익숙해진다.
- [x] CSS 속성은 가능하면 축약형(shorthand)을 사용한다.
- [x] flexbox를 활용해 레이아웃을 구성한다.

## 구현 기능 목록
### 기본 UI
- [x] 파란 배경 헤더, 헤더 타이틀 "🎱 행운의 로또"
- [x] 메인 컨테이너
- [x] 구입 금액 입력창
- 메인 컨테이너 타이틀 "🎱 내 번호 당첨 확인 🎱"
- 입력 라벨 "구입할 금액을 입력해주세요."
- 구입 금액 입력칸
- 구입 버튼
- [x] 숨김 창
- 구입 완료 문구 "총 n개를 구입하셨습니다."
- 로또 리스트 출력
- luckyNumbers 입력 요구 문구 "지난 주 당첨번호 6개와 보너스 번호 1개를 입력해주세요."
- 당첨 번호 및 보너스 번호 입력 칸
- 결과 확인 버튼
- [x] 모달 창
- 모달 창 닫기 버튼
- 모달 창 타이틀 "🏆 당첨 통계 🏆"
- 결과 출력 테이블
- 수익률 출력 "당신의 총 수익률은 n%입니다."
- 다시 시작 버튼
- [x] 푸터 저작권 문구

### 로또 구매 및 발행 기능
- [x] 구입 금액을 입력받는 기능
- [x] 발행 로또 수량 계산
- [x] 발행된 로또를 로또 리스트에 렌더링
- 로또 목록이 너무 길어지지 않도록 높이를 고정해 스크롤바로 확인

### 당첨 번호 및 보너스 번호 지정 기능
- [x] 당첨번호와 보너스번호를 입력받는 기능

### 결과 확인 기능(모달 창)
- [x] 결과 확인하기 버튼으로 모달 창으로 넘어가기
- [x] 번호 일치 개수, 당첨금, 당첨 개수 계산 및 출력
- [x] 총 수익률 계산 및 문구에 포함하여 출력
- [x] X 버튼을 통해 모달 창만 닫는 기능
- [x] 다시 시작하기 버튼을 통해 모든 칸을 초기화하고 처음부터 시작하는 기능
87 changes: 83 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,95 @@
<!DOCTYPE html>
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>🎱 행운의 로또</title>
<title>로또 게임</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>

<body>
<div id="app">
<h1>🎱 행운의 로또</h1>
<header>
<p>🎱 행운의 로또</p>
</header>

<!--로또 main content box를 만들 틀 -->
<main class="main-container">
<p class="lotto-subtitle">🎱 내 번호 당첨 확인 🎱</p>
<section class="purchase-price-section">
<form id="purchase-price-form">
<label for="purchase-price">구입할 금액을 입력해주세요.</label>

<div class="purchase-price-input-box">
<input type="number" id="purchase-price" placeholder="금액" min="1000" step="1000" required>
<button type="submit" class="purchase-button">구입</button>
</div>

</form>
</section>

<section id="lotto-list-section" class="hidden">
<p id="lotto-count-text">총 n개를 구입하셨습니다.</p>

<div id="lotto-list" class="lotto-scroll-box">

</div>

</section>

<form id="lucky-numbers-form" class="hidden">
<p class="lucky-numbers-input-text">지난 주 당첨번호 6개와 보너스 번호 1개를 입력해주세요.</p>
<div class="lucky-numbers-input-box">

<div class="winning-numbers">
<p class="winning-numbers-text">당첨 번호</p>
<input type="number" class="winning-number" min="1" max="45" required>
<input type="number" class="winning-number" min="1" max="45" required>
<input type="number" class="winning-number" min="1" max="45" required>
<input type="number" class="winning-number" min="1" max="45" required>
<input type="number" class="winning-number" min="1" max="45" required>
<input type="number" class="winning-number" min="1" max="45" required>
</div>
Comment on lines +44 to +52
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

구입 금액 입력에는 <label for="purchase-price">를 잘 사용했는데, 당첨번호와 보너스번호에는 <p> 태그를 사용하고 있어요. 이렇게 작성하면 스크린 리더가 입력 필드와 레이블 텍스트의 연관성을 인식하지 못해요. 2단계 포인트 중 "목적에 맞는 HTML 태그를 사용한다"과도 관련되는 부분이에요.

<fieldset> + <legend>로 그룹을 감싸고, 개별 input에는 aria-label을 부여하는 방식을 시도해보면 좋을 것 같아.

<fieldset>
  <legend>당첨 번호</legend>
  <input type="number" aria-label="당첨 번호 1" ...>
  ...
</fieldset>

스크린 리더가 이 영역에 진입하면 "당첨 번호 그룹"이라고 읽어주고, 각 input에서는 "당첨 번호 1, 입력 필드"라고 읽어줘요.

현재 비비빙의 코드에서는 <p class="winning-numbers-text">당첨 번호</p>로 되어 있어서, 시각적으로는 제목처럼 보이지만 스크린 리더는 <p>와 아래 <input>들이 연관되어 있다는 걸 모르거든요.


<div class="bonus-number">
<p class="bonus-number-text">보너스 번호</p>
<input type="number" class="bonus-number" min="1" max="45" required>
</div>

</div>
<button type="submit" class="result-button">결과 확인하기</button>
</form>
</main>

<div id="result-modal" class="modal hidden">
<div class="modal-box">
<button id="modal-close-button" class="close-button">&times;</button>
<p class="result-text">🏆 당첨 통계 🏆</p>

<table class="result-table">
<thead>
<tr>
<th>일치 갯수</th>
<th>당첨금</th>
<th>당첨 갯수</th>
</tr>
</thead>

<tbody id="result-table-body">

</tbody>
</table>

<h3 id="profit-rate-text">당신의 총 수익률은 n%입니다.</h3>
<button id="restart-button">다시 시작하기</button>
</div>
</div>

<footer>
<p>Copyright 2026. woowacourse</p>
</footer>
</div>

<script type="module" src="./src/step2-index.js"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"start-step2": "vite",
"deploy": "vite build --base=/javascript-lotto/ --minify=false && npx gh-pages -d dist"
},
"homepage": "https://{username}.github.io/javascript-lotto",
"homepage": "https://binggwa.github.io/javascript-lotto",
"devDependencies": {
"@babel/cli": "^7.28.6",
"@babel/core": "^7.29.0",
Expand Down
10 changes: 6 additions & 4 deletions src/Domain/Lotto.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LOTTO_RULES } from "../Utils/Constants.js";

class Lotto {
#numbers;

Expand All @@ -13,13 +15,13 @@ class Lotto {
throw new Error("[ERROR] 각 번호가 숫자가 아닙니다!");
}

const isOutRange = nums.some((n) => n < 1 || n > 45);
const isOutRange = nums.some((n) => n < LOTTO_RULES.MIN_NUMBER || n > LOTTO_RULES.MAX_NUMBER);
if (isOutRange) {
throw new Error(`[ERROR] 당첨 번호는 1~45 범위여야 합니다!`);
throw new Error(`[ERROR] 당첨 번호는 ${LOTTO_RULES.MIN_NUMBER}~${LOTTO_RULES.MAX_NUMBER} 범위여야 합니다!`);
}

if (nums.length !== 6) {
throw new Error(`[ERROR] 번호는 6개여야 합니다!`);
if (nums.length !== LOTTO_RULES.LENGTH) {
throw new Error(`[ERROR] 번호는 ${LOTTO_RULES.LENGTH}개여야 합니다!`);
}

if (new Set(nums).size !== nums.length) {
Expand Down
7 changes: 4 additions & 3 deletions src/Domain/LottoMachine.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Lotto from "./Lotto.js";
import { LOTTO_RULES } from "../Utils/Constants.js";

const LottoMachine = {
issueLottos(purchasePriceStr, generateRandomNumber) {
const purchasePrice = validatePurchasePrice(purchasePriceStr);
const ticketsCount = purchasePrice / 1000;
const ticketsCount = purchasePrice / LOTTO_RULES.PRICE;

return Array.from({ length: ticketsCount }, () => createLotto(generateRandomNumber));
},
Expand All @@ -15,8 +16,8 @@ const validatePurchasePrice = (purchasePriceStr) => {
if (Number.isNaN(numPrice)) {
throw new Error("[ERROR] 구입 금액이 숫자가 아닙니다!");
}
if (numPrice < 1000) {
throw new Error("[ERROR] 구입 최소 금액은 1000원 입니다!");
if (numPrice < LOTTO_RULES.PRICE) {
throw new Error(`[ERROR] 구입 최소 금액은 ${LOTTO_RULES.PRICE}원 입니다!`);
}

return numPrice;
Expand Down
5 changes: 3 additions & 2 deletions src/Domain/LuckyNumbers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Lotto from "./Lotto.js";
import { LOTTO_RULES } from "../Utils/Constants.js";

class LuckyNumbers {
#winningLotto;
Expand All @@ -12,8 +13,8 @@ class LuckyNumbers {
#validateBonusNumber(bonusNumber) {
const bonus = Number(bonusNumber.trim());

if (Number.isNaN(bonus) || bonus < 1 || bonus > 45) {
throw new Error("[ERROR] 보너스 번호는 1~45 범위의 숫자여야 합니다!");
if (Number.isNaN(bonus) || bonus < LOTTO_RULES.MIN_NUMBER || bonus > LOTTO_RULES.MAX_NUMBER) {
throw new Error(`[ERROR] 보너스 번호는 ${LOTTO_RULES.MIN_NUMBER}~${LOTTO_RULES.MAX_NUMBER} 범위의 숫자여야 합니다!`);
}

const winningNumbersNum = this.#winningLotto.getNumbers().map(Number);
Expand Down
15 changes: 15 additions & 0 deletions src/Utils/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,19 @@ export const RANK_MAP = {
THIRD: '5개 일치',
FOURTH: '4개 일치',
FIFTH: '3개 일치',
};

export const LOTTO_RULES = {
PRICE: 1000,
MIN_NUMBER: 1,
MAX_NUMBER: 45,
LENGTH: 6,
};

export const RANK_MAP_WEB = {
FIRST: '6개',
SECOND: '5개+보너스 볼',
THIRD: '5개',
FOURTH: '4개',
FIFTH: '3개',
};
85 changes: 85 additions & 0 deletions src/WebApp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import LottoMachine from "./Domain/LottoMachine.js";
import LottoResult from "./Domain/LottoResult.js";
import LuckyNumbers from "./Domain/LuckyNumbers.js";
import LottoWebView from "./WebView/LottoWebView.js";
import { LOTTO_RULES } from "./Utils/Constants.js";

class WebApp {
constructor() {
this.lottos = [];
this.view = new LottoWebView();
Comment on lines +9 to +10
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this.lottos = [];
this.view = new LottoWebView();

현재 외부에서 webApp.lottos = []이나 webApp.view = null처럼 직접 조작이 가능한데요. 1단계 도메인에서는 #numbers처럼 private field를 사용했는데, 여기서는 적용하지 않은 이유가 있을까요?

}

run() {
// 구입 폼 관련
this.view.bindPurchaseForm(this.#handlePurchaseForm.bind(this));
// luckyNumbers 폼 관련
this.view.bindLuckyNumbersForm(this.#handleLuckyNumbersForm.bind(this));
// 모달 창 닫기 버튼
this.view.bindModalCloseButton(this.#handleModalCloseButton.bind(this));
// 재시작 버튼
this.view.bindRestartButton(this.#handleRestartButton.bind(this));
}

// 랜덤 숫자 생성기
#generateRandomNumber() {
const nums = new Set();
while (nums.size < LOTTO_RULES.LENGTH) {
nums.add(Math.floor(Math.random() * LOTTO_RULES.MAX_NUMBER) + LOTTO_RULES.MIN_NUMBER);
}

return [...nums];
}

// 구입 폼 관련
#handlePurchaseForm(purchasePriceStr) {
try {
// 가격에 맞추어 로또 발행
const lottos = LottoMachine.issueLottos(
purchasePriceStr,
this.#generateRandomNumber,
);

this.lottos = lottos;

// 불러온 로또를 기반으로 로또 목록 출력 필요
this.view.renderLottos(lottos);
} catch (e) {
this.view.showError(e.message);
}
}

// luckyNumbers 폼 관련
#handleLuckyNumbersForm(winningNumbers, bonusNumber) {
try {
const luckyNumbers = new LuckyNumbers(winningNumbers, bonusNumber);
const winningResult = LottoResult.calculateWinningResult(
this.lottos,
luckyNumbers,
);

const purchasePrice = this.lottos.length * LOTTO_RULES.PRICE;
const profitRate = LottoResult.calculateProfitRate(
winningResult,
purchasePrice,
);

this.view.renderResultModal(winningResult, profitRate);
} catch (e) {
this.view.showError(e.message);
}
}

// 모달 창 닫기 버튼 관련
#handleModalCloseButton() {
this.view.closeModal();
}

// 재시작 버튼 관련
#handleRestartButton() {
this.lottos = [];
this.view.resetView();
}
}

export default WebApp;
Loading