Files
Find-Word/README.md
T

13 KiB
Raw Permalink Blame History

단어를 맞춰라 · 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. 프로젝트 자체는 private / all-rights-reserved이며 package.json의 license는 UNLICENSED입니다. LICENSE를 참고하세요. 런타임 의존성 React, ReactDOM, scheduler의 MIT 전문은 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입니다.

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에 설명했습니다. 청취 과제는 직접 목표 식별, 연습 암호와 진짜 암호 구별, 앞 단어를 잊고 다음 단어 기억, 첫째·마지막·가운데 선택, 반복 찾기, 정정 반영, 신호 조건 적용, 오른쪽 표지 선택의 10가지입니다. 문장에 실제 보기의 오답도 포함됩니다.

검증과 정적 빌드

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 화면·키보드·접근성·오류 재시도 검증:

# 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/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를 준비한 뒤 다음 명령을 사용합니다. 클라이언트 버전을 고정해도 원격 모델 변경으로 동일 바이트 재생성은 보장하지 않습니다.

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회 시도를 적용하며, 실패 시 기존 자산을 보존합니다. 청취 샘플: 안내, 행운의 사과.

현장음은 원본·저작자·라이선스·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 증거에 있습니다. 이전 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 동작은 유지합니다. 수학적 상한과 선형 재현 절차는 설계와 provenance에 있습니다.

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 조건은 여전히 실패입니다. 실패 원문을 보존했습니다. ASR 텍스트는 검증된 전사가 아닙니다. 제품 소유자 Oz는 현재 8 WAV·manifest·notice 검수 ZIP을 받은 뒤 **“승인합니다. 이대로 진행하세요”**라고 2026-10-14T11:29:51Z / 2026-10-14 20:29:51 KST에 회신했습니다(사용자가 전달한 회신). 기계 판독 승인 기록은 이 정확한 문구·소유자·시각·범위와 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 과장은 실패합니다. 원래 실패 테스트와 로그는 승인 검증 증거에 보존합니다.

생성 방법, 도구 라이선스, 합성 음질의 한계는 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, 배포 고지에 있습니다. 시장 파생본은 CC BY-SA 4.0을 유지합니다. 외부 링크는 출처 확인용이며 앱은 이를 요청하지 않습니다.

이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.