174 lines
13 KiB
Markdown
174 lines
13 KiB
Markdown
# 단어를 맞춰라 · Find-Word v0.2.5
|
||
|
||
소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
|
||
|
||
**v0.2.5 exact 자산 릴리스 승인:** Oz의 현재 8 WAV 청취 인수로 릴리스 gate를 통과합니다. ASR 실패는 그대로이며 법적/privacy clearance는 없습니다. 배포하지 않았습니다.
|
||
|
||
**Designed by OZ at Heat-T Medical Inc.**
|
||
|
||
Canonical repository: [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word).
|
||
프로젝트 자체는 **private / all-rights-reserved**이며 `package.json`의
|
||
`license`는 `UNLICENSED`입니다. [LICENSE](LICENSE)를 참고하세요.
|
||
런타임 의존성 React, ReactDOM, scheduler의 MIT 전문은
|
||
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)에 보존하며,
|
||
`public/THIRD_PARTY_NOTICES.md`가 빌드 시 `dist/THIRD_PARTY_NOTICES.md`로 복사됩니다.
|
||
빌드 검증기는 설치된 의존성의 라이선스 전문·버전과 세 사본을 대조합니다.
|
||
|
||
React + TypeScript strict + Vite로 만든 정적 SPA입니다. 로그인, 개인정보 저장,
|
||
분석 도구, 외부 CDN, 런타임 TTS/API 요청이 없습니다. 청각적 주의·식별 게임이며
|
||
의료적 진단·치료나 효과를 주장하지 않습니다.
|
||
|
||
## 실행
|
||
|
||
Node.js 최소 버전은 **22.22.2** (`>=22.22.2`)입니다. 잠긴 jsdom 계열 의존성이
|
||
요구하는 지원 범위는 22.x의 22.22.2 이상, 24.x의 24.15.0 이상 또는 26.0.0 이상입니다.
|
||
이 중 지원되는 LTS 버전을 사용하세요. `package-lock.json`을
|
||
포함하므로 새 환경에서는 `npm ci`를 사용합니다. 이번 검증 환경은 Node 26.7.0입니다.
|
||
|
||
```bash
|
||
npm ci
|
||
npm run dev
|
||
```
|
||
|
||
표시되는 `http://127.0.0.1:5173`에서 사용합니다. 소리는 사용자가 **게임 시작 · 소리 듣기**를
|
||
누른 뒤에만 재생합니다. 낮은 음량부터 조절하고 헤드폰이나 스피커를 사용할 수 있습니다.
|
||
|
||
## 플레이
|
||
|
||
- 골고루 풀기, 보기에서 선택, 직접 입력 중 선택합니다.
|
||
- 8개 실제 현장 녹음 환경 중 하나를 지정하거나 라운드마다 섞어 세 곳을 방문합니다.
|
||
- 에스의 안내가 배경에 묻힌 뒤 목표 문장이 나옵니다. 재생 후 질문에 답합니다.
|
||
- 시작 난이도 1~5를 고르고 정확히 **3문항**을 같은 단계에서 풉니다. 단계 기본값은 **SNR +6, +3, 0, −3, −6 dB**입니다.
|
||
- 목표 음성 SNR을 **−12~+12 dB, 1 dB 간격**으로 직접 조절할 수 있습니다. 재생 중 변경은 다음 재생·재청취와 남은 문제부터 적용합니다.
|
||
배경 크기는 고정되고 양수는 음성을 기준보다 크게, 0은 기준 크기로, 음수는 작게 재생합니다.
|
||
표시 dB는 음성 기준 gain의 변화이며 환경별 실측 RMS 비율이나 물리적 음압이 아닙니다.
|
||
- 다시 듣기는 감점하지 않습니다. 정답은 `단계 × 100점`, 연속 정답 보너스는 문항별 최대 20점, 라운드 합계 최대 30점입니다 (0 + 10 + 20점, 1단계 만점 330점).
|
||
- 정상 결과에서 다음 단계의 새 3문항을 바로 시작합니다. 5단계에서는 같은 단계 재도전과 홈 복귀를 제공합니다. 수동 SNR은 다음 라운드에도 유지되고, 홈 복귀 시 선택한 시작 단계의 기본값으로 돌아갑니다.
|
||
- 언제든 **문제 풀이 종료**를 누르면 즉시 오디오를 취소하고 **응답 수·정답 수**를 보여 줍니다. 무응답은 오답으로 세지 않고 정답률은 `— / 응답 없음`, 답하지 않은 단계는 `미진행`으로 표시합니다. 결과에서 **처음 화면으로** 돌아갑니다.
|
||
- 결과의 SNR은 실제 마지막 청취값을 기록합니다. 점수·정답률·최고 연속 정답·단계별 결과를 확인합니다.
|
||
- 새로고침하면 기록이 사라집니다. 결과를 브라우저나 서버에 저장하지 않습니다.
|
||
|
||
10개 유형 × 10개 단어의 100개 목표 문장을 저장했습니다. 각 세션은 유형과
|
||
단어를 섞어 출제하며 정답 위치도 섞습니다. 사전 표제어와 변형 규칙은
|
||
[WORDS.md](docs/WORDS.md)에 설명했습니다.
|
||
청취 과제는 직접 목표 식별, 연습 암호와 진짜 암호 구별, 앞 단어를 잊고 다음
|
||
단어 기억, 첫째·마지막·가운데 선택, 반복 찾기, 정정 반영, 신호 조건 적용,
|
||
오른쪽 표지 선택의 10가지입니다. 문장에 실제 보기의 오답도 포함됩니다.
|
||
|
||
## 검증과 정적 빌드
|
||
|
||
```bash
|
||
npm run lint
|
||
npm test
|
||
python3 -m unittest discover -s tests/python -v
|
||
npm run build
|
||
npm audit
|
||
npm run preview
|
||
```
|
||
|
||
`lint`는 ESLint와 TypeScript 검사, `test`는 unit/component/자산/빌드 검증기
|
||
테스트를 실행합니다. `build`는 `dist/`를 만들고 음성 101개와 현장음 8개가 manifest 해시 그대로
|
||
포함되었는지 확인합니다. 앱 콘텐츠의 예상 101개 목록과 실제 파일 집합을 독립 비교해
|
||
manifest와 폴더를 함께 줄이거나 파일을 추가한 경우도 거부합니다. 빌드에는 음성 합성 도구가 필요하지 않습니다.
|
||
런타임 MIT notice와 현장음 attribution/license notice가 `dist/`에 전문 그대로 포함되는지도 검증합니다.
|
||
|
||
실제 오디오·360px/1280px 화면·키보드·접근성·오류 재시도 검증:
|
||
|
||
```bash
|
||
# Chromium이 없는 개발 환경에서 한 번만 실행합니다.
|
||
npx playwright install chromium
|
||
npm run test:browser
|
||
# 이미 설치된 Chrome을 사용할 때의 대안입니다.
|
||
CHROME_PATH=/usr/bin/google-chrome npm run test:browser
|
||
```
|
||
|
||
Chromium sandbox는 기본으로 켭니다. sandbox를 지원하지 않는 격리된 개발
|
||
환경에서만 `PLAYWRIGHT_NO_SANDBOX=1`을 명시해 끌 수 있습니다. `0`, `false`,
|
||
빈 값이나 미설정은 sandbox를 유지합니다. `CHROME_PATH` 지정은 sandbox를 끄지 않습니다.
|
||
|
||
브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
|
||
실제 음성 재생이 끝나기를 기다려 몇 분이 걸립니다. 로컬 4173 포트를
|
||
테스트 중에만 열고 종료합니다. 스크린샷과 109개 WAV의 query URL·해시·디코드
|
||
결과는 `test-results/`에 저장합니다. 현재 구현·검증 기록은
|
||
`.agent/evidence-v0.2.5/review-fixes/`와 [.agent/RESULT-v0.2.5.md](.agent/RESULT-v0.2.5.md)에 보존합니다.
|
||
이전 리뷰 인수 기록은 `.agent/evidence-v0.2.3/review-fixes/`에 있습니다. 기존 v0.1/v0.1.1 및 v0.2.3 증거는 덮어쓰지 않습니다.
|
||
|
||
## 음성과 문서
|
||
|
||
한국어 WAV 101개는 직접 쓴 문장을 Microsoft Edge online TTS의
|
||
`ko-KR-HyunsuMultilingualNeural`로 v0.1.1에서 생성했으며 WAV 바이트는 그대로 보존했습니다. edge-tts **7.3.1**,
|
||
rate `-6%`, pitch `-4Hz`이며 FFmpeg로 **24kHz mono PCM16**으로 변환합니다.
|
||
생성 시 네트워크가 필요하며 앱은 저장된 WAV만 재생합니다. 환경음은 공개 라이선스의 실제 현장 녹음 8개를 각 12초로 변환해 번들합니다. 생성 음성의 독점 권리를 주장하지 않습니다.
|
||
|
||
intro와 목표 WAV는 모두 `/audio/파일명.wav?v=0.2.5`로 요청합니다.
|
||
`src/audio/assets.ts`의 자산 버전 한 곳을 사용하며, 같은 파일명으로 음성을
|
||
교체할 때 이 값을 갱신해 기존 7일 캐시와 구분합니다. 배경은 `/ambience/환경.wav?v=0.2.5`입니다. 제품 버전은 0.2.5입니다.
|
||
|
||
Linux에서 Python 3.11 이상과 FFmpeg를 준비한 뒤 다음 명령을 사용합니다.
|
||
클라이언트 버전을 고정해도 원격 모델 변경으로 동일 바이트 재생성은 보장하지 않습니다.
|
||
|
||
```bash
|
||
python3 -m venv .venv-audio
|
||
.venv-audio/bin/python -m pip install -r requirements-audio.txt
|
||
.venv-audio/bin/python -m pip check
|
||
npm run audio:generate
|
||
```
|
||
|
||
고정 venv의 Python이 101개 모두 임시 생성·검증하고 디렉터리를 원자 교체합니다.
|
||
요청별 60초 제한과 최대 3회 시도를 적용하며, 실패 시 기존 자산을 보존합니다.
|
||
청취 샘플: [안내](public/audio/intro.wav), [행운의 사과](public/audio/luck-apple.wav).
|
||
|
||
현장음은 원본·저작자·라이선스·12초 선택 구간을 유지하고 **−32.5 LUFS**로 맞췄습니다.
|
||
전용 FFmpeg 6.1.1 `ebur128=peak=true`가 최종 WAV의 릴리스 계기입니다. 8개 모두 **−32.5 LUFS,
|
||
spread 0.0 LU**, 최대 true peak **−6.2 dBTP**입니다. 수용 조건은 목표 ±0.2 LU,
|
||
spread ≤0.4 LU, true peak ≤−6 dBTP이며 생성기의 내부 ceiling은 −6.2 dBTP입니다.
|
||
정확한 faded PCM을 측정해 `volume=<gain_db>dB:precision=double`만 적용하고 PCM16 뒤 새 프로세스로 재측정합니다.
|
||
독립 pyloudnorm **0.2.0**/soundfile **0.13.1**도 −32.6062~−32.5291 LUFS, spread **0.0772 LU**로 통과했습니다.
|
||
일반 `npm test`는 표준 Python과 FFmpeg로 검사하며 네트워크나 전역 pyloudnorm 패키지를 요구하지 않습니다.
|
||
고정 버전·wheel 해시·독립 스크립트는 [review-fixes 증거](.agent/evidence-v0.2.5/review-fixes/README.md)에 있습니다.
|
||
이전 loudnorm의 0.01 LU 주장은 전용 ebur128 2.0 LU / pyloudnorm 1.925 LU 결과로 **거부한 역사 기록**입니다.
|
||
|
||
배경은 브라우저에서 RMS 재정규화하지 않습니다. 모든 환경/SNR의 고정 배경 gain은 **0.4198884518664126**이며
|
||
SNR은 목표 음성 상대 gain만 바꿉니다. voice/intro RMS 0.03과 master 동작은 유지합니다.
|
||
수학적 상한과 선형 재현 절차는 [설계](docs/ARCHITECTURE.md#오디오)와
|
||
[provenance](docs/AUDIO_PROVENANCE.md#v025-loudness-정규화)에 있습니다.
|
||
|
||
mall 36–48초, market 24–36초, cafe 0–12초를 포함한 8개 구간과 기존 실패/no-VAD 증거를 보존합니다.
|
||
새 exact WAV의 동일 faster-whisper 1.2.1 tiny/VAD 검사에서 **market 1 segment, mall/cafe 0개,
|
||
requiredEmptyPassed=false**이며 자동 empty-ASR 조건은 여전히 실패입니다.
|
||
[실패 원문](.agent/evidence-v0.2.5/review-fixes/final-asr.json)을 보존했습니다. ASR 텍스트는 검증된 전사가 아닙니다.
|
||
제품 소유자 Oz는 현재 8 WAV·manifest·notice 검수 ZIP을 받은 뒤 **“승인합니다. 이대로 진행하세요”**라고
|
||
2026-10-14T11:29:51Z / 2026-10-14 20:29:51 KST에 회신했습니다(사용자가 전달한 회신).
|
||
[기계 판독 승인 기록](.agent/RELEASE-APPROVAL-v0.2.5.json)은 이 정확한 문구·소유자·시각·범위와
|
||
ZIP SHA-256 `bb2e974b652df43db4a1f2a59716836e2c521839c724b34d4b71c1b40c3322e5`,
|
||
manifest SHA-256 `6433bf23af35f24031c6bbe784dd0abf9330f276da6b8705e47c7a1144a285b7`,
|
||
8개 WAV 및 notice 해시를 연결합니다. `productOwnerReleaseApproval=true`, `humanListeningAcceptance=true`,
|
||
**`legalPrivacyClearance=false`**이며 과거 v0.2.3 승인을 재사용한 것이 아닙니다.
|
||
|
||
`python3 scripts/release_approval.py`와 `npm test`의 릴리스 계약은 **동일 설정의 자동 empty-ASR 조건 OR
|
||
유효한 exact-byte 소유자 청취 승인**을 검사합니다. 현재 통과 경로는 후자뿐입니다. 승인 기록 누락,
|
||
stale WAV/manifest, 다른 ZIP, 문구/범위/시각 누락·변경, 법적 clearance 과장은 실패합니다.
|
||
원래 실패 테스트와 로그는 [승인 검증 증거](.agent/evidence-v0.2.5/release-approval/README.md)에 보존합니다.
|
||
|
||
생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
|
||
참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
|
||
|
||
현장음 원본은 `/tmp/findword-ambience-sources`에서만 보관하고 배포하지 않습니다.
|
||
`python3 scripts/ambience_audio.py`는 승인 계획의 `expectedSourceSha256`·`sourceBytes`를 전수 확인한 뒤에만 변환합니다.
|
||
직접 미디어 길이는 `directMediaDurationSeconds`, 검증 상태는 `sourceVerification`에 보존합니다.
|
||
8 WAV·manifest·고지를 같은 파일시스템에서 staging/검증한 뒤 Linux `renameat2(RENAME_EXCHANGE)`로
|
||
폴더 전체를 교체합니다. symlink 출력은 거부하며 commit 전 실패/중단은 이전 폴더를 보존합니다.
|
||
PD·CC0·CC BY 4.0·CC BY-SA 4.0의 정확한 출처·저작자·변환·해시는
|
||
[현장음 manifest](public/ambience/manifest.json), [배포 고지](public/ambience/AMBIENCE_NOTICES.md)에 있습니다.
|
||
시장 파생본은 CC BY-SA 4.0을 유지합니다. 외부 링크는 출처 확인용이며 앱은 이를 요청하지 않습니다.
|
||
|
||
- [변경 이력](CHANGELOG.md)
|
||
- [v0.2.5 구현·검증 결과](.agent/RESULT-v0.2.5.md)
|
||
- [v0.2.3 역사 구현·검증 결과](.agent/RESULT-v0.2.3.md)
|
||
- [설계와 상태 전이](docs/ARCHITECTURE.md)
|
||
- [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
|
||
- [v0.1.1 RED/GREEN 및 실제 검증 결과](.agent/RESULT-v0.1.1.md)
|
||
- [v0.1 역사 기록](.agent/RESULT.md)
|
||
|
||
이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
|