439 lines
21 KiB
Plaintext
439 lines
21 KiB
Plaintext
|
||
> find-word@0.1.0 test
|
||
> vitest run tests/release.test.ts -t Node 최소
|
||
|
||
|
||
RUN v5.0.4 /home/hermes/worktrees/find-word-v0.1
|
||
|
||
❯ tests/release.test.ts (4 tests | 1 failed | 3 skipped) 27ms
|
||
❯ 릴리스 문서 계약 (4)
|
||
× Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다 25ms
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
|
||
|
||
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
|
||
AssertionError: expected '>=22.12.0' to be '>=22.22.2' // Object.is equality
|
||
|
||
Expected: ">=22.22.2"
|
||
Received: ">=22.12.0"
|
||
|
||
❯ tests/release.test.ts:11:40
|
||
9| const metadata = JSON.parse(readFileSync("package.json", "utf8"));
|
||
10| const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
|
||
11| expect.soft(metadata.engines.node).toBe(">=22.22.2");
|
||
| ^
|
||
12| expect.soft(lock.packages[""].engines.node).toBe(">=22.22.2");
|
||
13| expect(lock.packages["node_modules/jsdom"].engines.node).toContain…
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/6]⎯
|
||
|
||
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
|
||
AssertionError: expected '>=22.12.0' to be '>=22.22.2' // Object.is equality
|
||
|
||
Expected: ">=22.22.2"
|
||
Received: ">=22.12.0"
|
||
|
||
❯ tests/release.test.ts:12:49
|
||
10| const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
|
||
11| expect.soft(metadata.engines.node).toBe(">=22.22.2");
|
||
12| expect.soft(lock.packages[""].engines.node).toBe(">=22.22.2");
|
||
| ^
|
||
13| expect(lock.packages["node_modules/jsdom"].engines.node).toContain…
|
||
14| for (const path of ["README.md", "docs/DEPLOYMENT.md"]) {
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/6]⎯
|
||
|
||
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
|
||
AssertionError: README.md: expected '# 단어를 맞춰라 · Find-Word v0.1\n\n소음 속에 숨…' to contain '22.22.2'
|
||
|
||
- Expected
|
||
+ Received
|
||
|
||
- 22.22.2
|
||
+ # 단어를 맞춰라 · Find-Word v0.1
|
||
+
|
||
+ 소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
|
||
+
|
||
+ **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.12 이상**의 지원되는 LTS 버전을 권장합니다. `package-lock.json`을
|
||
+ 포함하므로 새 환경에서는 `npm ci`를 사용합니다. 이번 검증 환경은 Node 26.7.0입니다.
|
||
+
|
||
+ ```bash
|
||
+ npm ci
|
||
+ npm run dev
|
||
+ ```
|
||
+
|
||
+ 표시되는 `http://127.0.0.1:5173`에서 사용합니다. 소리는 사용자가 **게임 시작 · 소리 듣기**를
|
||
+ 누른 뒤에만 재생합니다. 낮은 음량부터 조절하고 헤드폰이나 스피커를 사용할 수 있습니다.
|
||
+
|
||
+ ## 플레이
|
||
+
|
||
+ - 골고루 풀기, 보기에서 선택, 직접 입력 중 선택합니다.
|
||
+ - 8개 환경을 순환하거나 하나를 지정합니다.
|
||
+ - 에스의 안내가 배경에 묻힌 뒤 목표 문장이 나옵니다. 재생 후 질문에 답합니다.
|
||
+ - 10문제 동안 2문제마다 난이도가 올라갑니다: **SNR +6, +3, 0, −3, −6 dB**.
|
||
+ 양수는 음성이 배경보다 크고, 0은 같고, 음수는 더 작다는 뜻입니다.
|
||
+ - 다시 듣기는 감점하지 않습니다. 정답은 `단계 × 100점`, 연속 정답은 최대 40점의 보너스를 받습니다.
|
||
+ - 결과에서 점수·정답률·최고 연속 정답·단계별 결과를 확인하고 다시 시작할 수 있습니다.
|
||
+ - 새로고침하면 기록이 사라집니다. 결과를 브라우저나 서버에 저장하지 않습니다.
|
||
+
|
||
+ 10개 유형 × 10개 단어의 100개 목표 문장을 저장했습니다. 각 세션은 유형과
|
||
+ 단어를 섞어 출제하며 정답 위치도 섞습니다. 사전 표제어와 변형 규칙은
|
||
+ [WORDS.md](docs/WORDS.md)에 설명했습니다.
|
||
+
|
||
+ ## 검증과 정적 빌드
|
||
+
|
||
+ ```bash
|
||
+ npm run lint
|
||
+ npm test
|
||
+ npm run build
|
||
+ npm run preview
|
||
+ ```
|
||
+
|
||
+ `lint`는 ESLint와 TypeScript 검사, `test`는 unit/component/자산/빌드 검증기
|
||
+ 테스트를 실행합니다. `build`는 `dist/`를 만들고 음성 101개가 원본 해시 그대로
|
||
+ 포함되었는지 확인합니다. 빌드에는 음성 합성 도구가 필요하지 않습니다.
|
||
+
|
||
+ 실제 오디오·360px/1280px 화면·키보드·접근성·오류 재시도 검증:
|
||
+
|
||
+ ```bash
|
||
+ # Chromium이 없는 개발 환경에서 한 번만 실행합니다.
|
||
+ npx playwright install chromium
|
||
+ npm run test:browser
|
||
+ # 이미 설치된 Chrome을 사용할 때의 대안입니다.
|
||
+ CHROME_PATH=/usr/bin/google-chrome npm run test:browser
|
||
+ ```
|
||
+
|
||
+ 브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
|
||
+ 실제 음성 재생이 끝나기를 기다려 약 1–2분이 걸립니다. 로컬 4173 포트를
|
||
+ 테스트 중에만 열고 종료합니다. 스크린샷은 `.agent/evidence/`에 남습니다.
|
||
+
|
||
+ ## 음성과 문서
|
||
+
|
||
+ 한국어 WAV는 직접 쓴 문장을 로컬 eSpeak NG로 합성했습니다. 환경음은
|
||
+ 브라우저에서 고정 seed로 합성합니다. 외부 음원이나 신경망 음성 모델을 사용하지 않습니다.
|
||
+ 생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
|
||
+ 참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
|
||
+
|
||
+ - [설계와 상태 전이](docs/ARCHITECTURE.md)
|
||
+ - [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
|
||
+ - [RED/GREEN 및 실제 검증 결과](.agent/RESULT.md)
|
||
+
|
||
+ 이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
|
||
+
|
||
|
||
❯ tests/release.test.ts:16:35
|
||
14| for (const path of ["README.md", "docs/DEPLOYMENT.md"]) {
|
||
15| const document = readFileSync(path, "utf8");
|
||
16| expect.soft(document, path).toContain("22.22.2");
|
||
| ^
|
||
17| expect.soft(document, path).not.toContain("22.12");
|
||
18| }
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/6]⎯
|
||
|
||
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
|
||
AssertionError: README.md: expected '# 단어를 맞춰라 · Find-Word v0.1\n\n소음 속에 숨…' not to contain '22.12'
|
||
|
||
- Expected
|
||
+ Received
|
||
|
||
- 22.12
|
||
+ # 단어를 맞춰라 · Find-Word v0.1
|
||
+
|
||
+ 소음 속에 숨은 한국어 단어를 듣고 찾는 반응형 웹 게임입니다.
|
||
+
|
||
+ **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.12 이상**의 지원되는 LTS 버전을 권장합니다. `package-lock.json`을
|
||
+ 포함하므로 새 환경에서는 `npm ci`를 사용합니다. 이번 검증 환경은 Node 26.7.0입니다.
|
||
+
|
||
+ ```bash
|
||
+ npm ci
|
||
+ npm run dev
|
||
+ ```
|
||
+
|
||
+ 표시되는 `http://127.0.0.1:5173`에서 사용합니다. 소리는 사용자가 **게임 시작 · 소리 듣기**를
|
||
+ 누른 뒤에만 재생합니다. 낮은 음량부터 조절하고 헤드폰이나 스피커를 사용할 수 있습니다.
|
||
+
|
||
+ ## 플레이
|
||
+
|
||
+ - 골고루 풀기, 보기에서 선택, 직접 입력 중 선택합니다.
|
||
+ - 8개 환경을 순환하거나 하나를 지정합니다.
|
||
+ - 에스의 안내가 배경에 묻힌 뒤 목표 문장이 나옵니다. 재생 후 질문에 답합니다.
|
||
+ - 10문제 동안 2문제마다 난이도가 올라갑니다: **SNR +6, +3, 0, −3, −6 dB**.
|
||
+ 양수는 음성이 배경보다 크고, 0은 같고, 음수는 더 작다는 뜻입니다.
|
||
+ - 다시 듣기는 감점하지 않습니다. 정답은 `단계 × 100점`, 연속 정답은 최대 40점의 보너스를 받습니다.
|
||
+ - 결과에서 점수·정답률·최고 연속 정답·단계별 결과를 확인하고 다시 시작할 수 있습니다.
|
||
+ - 새로고침하면 기록이 사라집니다. 결과를 브라우저나 서버에 저장하지 않습니다.
|
||
+
|
||
+ 10개 유형 × 10개 단어의 100개 목표 문장을 저장했습니다. 각 세션은 유형과
|
||
+ 단어를 섞어 출제하며 정답 위치도 섞습니다. 사전 표제어와 변형 규칙은
|
||
+ [WORDS.md](docs/WORDS.md)에 설명했습니다.
|
||
+
|
||
+ ## 검증과 정적 빌드
|
||
+
|
||
+ ```bash
|
||
+ npm run lint
|
||
+ npm test
|
||
+ npm run build
|
||
+ npm run preview
|
||
+ ```
|
||
+
|
||
+ `lint`는 ESLint와 TypeScript 검사, `test`는 unit/component/자산/빌드 검증기
|
||
+ 테스트를 실행합니다. `build`는 `dist/`를 만들고 음성 101개가 원본 해시 그대로
|
||
+ 포함되었는지 확인합니다. 빌드에는 음성 합성 도구가 필요하지 않습니다.
|
||
+
|
||
+ 실제 오디오·360px/1280px 화면·키보드·접근성·오류 재시도 검증:
|
||
+
|
||
+ ```bash
|
||
+ # Chromium이 없는 개발 환경에서 한 번만 실행합니다.
|
||
+ npx playwright install chromium
|
||
+ npm run test:browser
|
||
+ # 이미 설치된 Chrome을 사용할 때의 대안입니다.
|
||
+ CHROME_PATH=/usr/bin/google-chrome npm run test:browser
|
||
+ ```
|
||
+
|
||
+ 브라우저 테스트는 기존 `dist/`를 사용하므로 소스 변경 후 먼저 빌드하세요.
|
||
+ 실제 음성 재생이 끝나기를 기다려 약 1–2분이 걸립니다. 로컬 4173 포트를
|
||
+ 테스트 중에만 열고 종료합니다. 스크린샷은 `.agent/evidence/`에 남습니다.
|
||
+
|
||
+ ## 음성과 문서
|
||
+
|
||
+ 한국어 WAV는 직접 쓴 문장을 로컬 eSpeak NG로 합성했습니다. 환경음은
|
||
+ 브라우저에서 고정 seed로 합성합니다. 외부 음원이나 신경망 음성 모델을 사용하지 않습니다.
|
||
+ 생성 방법, 도구 라이선스, 합성 음질의 한계는 [AUDIO_PROVENANCE.md](docs/AUDIO_PROVENANCE.md)를
|
||
+ 참고하세요. 실제 음량은 출력 장치에 따라 달라지며 표기 dB는 디지털 상대값입니다.
|
||
+
|
||
+ - [설계와 상태 전이](docs/ARCHITECTURE.md)
|
||
+ - [Neo를 위한 정적 배포 안내](docs/DEPLOYMENT.md)
|
||
+ - [RED/GREEN 및 실제 검증 결과](.agent/RESULT.md)
|
||
+
|
||
+ 이번 작업에서는 서버 배포와 Git commit/push를 실행하지 않습니다.
|
||
+
|
||
|
||
❯ tests/release.test.ts:17:39
|
||
15| const document = readFileSync(path, "utf8");
|
||
16| expect.soft(document, path).toContain("22.22.2");
|
||
17| expect.soft(document, path).not.toContain("22.12");
|
||
| ^
|
||
18| }
|
||
19| });
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/6]⎯
|
||
|
||
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
|
||
AssertionError: docs/DEPLOYMENT.md: expected '# 정적 배포 인계\n\n이 문서는 **Neo가 검토 후 수행할 절…' to contain '22.22.2'
|
||
|
||
- Expected
|
||
+ Received
|
||
|
||
- 22.22.2
|
||
+ # 정적 배포 인계
|
||
+
|
||
+ 이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
|
||
+ 접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
|
||
+
|
||
+ ## 산출물
|
||
+
|
||
+ Node.js 22.12 이상 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
|
||
+ `npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
|
||
+ `dist/audio/`의 WAV 101개와 manifest가 반드시 포함되어야 한다. 런타임은
|
||
+ Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
|
||
+
|
||
+ Canonical repository는 [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word)다.
|
||
+ 프로젝트 자체는 private / all-rights-reserved이며 [LICENSE](../LICENSE)를 따른다.
|
||
+ React, ReactDOM, scheduler의 MIT 전문을 담은 `dist/THIRD_PARTY_NOTICES.md`도
|
||
+ 반드시 배포한다. `build` 검증기는 고지 파일이 없거나 축약·변조되면 실패한다.
|
||
+
|
||
+ 현재 앱은 도메인 루트(`/`) 배포 기준이다. 하위 경로 배포 시 Vite base뿐 아니라
|
||
+ 음성 URL도 함께 변경하고 테스트해야 한다. 예정 도메인은 `findword.heart-m.com`이다.
|
||
+
|
||
+ ## Nginx 예시
|
||
+
|
||
+ 아래 root는 예시다. 실제 경로·기존 서버 블록·인증서는 Neo가 확인한다.
|
||
+ 기존 서비스 설정을 덮어쓰지 않고 검토 후 적용한다.
|
||
+
|
||
+ ```nginx
|
||
+ # 정적 릴리스 디렉터리를 가리킨다. 인증서와 HTTPS 설정은 운영자가 별도 추가한다.
|
||
+ server {
|
||
+ listen 80;
|
||
+ server_name findword.heart-m.com;
|
||
+ root /var/www/findword/current;
|
||
+ index index.html;
|
||
+
|
||
+ # 기본 MIME 정의로 JS/CSS/WAV가 올바른 타입으로 제공되게 한다.
|
||
+ include /etc/nginx/mime.types;
|
||
+ add_header X-Content-Type-Options nosniff always;
|
||
+ add_header Referrer-Policy no-referrer always;
|
||
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
|
||
+
|
||
+ # 파일명이 해시로 바뀌는 번들만 장기 캐시한다.
|
||
+ location /assets/ {
|
||
+ try_files $uri =404;
|
||
+ expires 1y;
|
||
+ }
|
||
+
|
||
+ # 음성 파일명은 재생성해도 같으므로 재검증한다. 누락 시 index.html 대신
|
||
+ # 404를 반환해야 앱이 명확한 음성 로드 오류와 재시도를 제공할 수 있다.
|
||
+ location /audio/ {
|
||
+ try_files $uri =404;
|
||
+ expires -1;
|
||
+ }
|
||
+
|
||
+ # SPA의 직접 진입·새로고침을 index.html로 받되 HTML은 재검증한다.
|
||
+ location / {
|
||
+ try_files $uri $uri/ /index.html;
|
||
+ expires -1;
|
||
+ }
|
||
+ }
|
||
+ ```
|
||
+
|
||
+ ## 인수 확인
|
||
+
|
||
+ 1. 서버에 적용하기 전 `nginx -t`, 기존 서비스 충돌, 파일 읽기 권한을 확인한다.
|
||
+ 2. TLS/HTTPS와 DNS를 운영자가 확인한다. 실제 도메인에서 시작 후 음성이 들리는지 확인한다.
|
||
+ 3. `audio/intro.wav`와 목표 WAV가 정상 응답하고 존재하지 않는 오디오는 404인지 확인한다.
|
||
+ 4. 모바일 Safari/Chrome, 데스크톱에서 음량·중단·다시 듣기·10문제 완주를 직접 확인한다.
|
||
+ 5. 원자적으로 릴리스 디렉터리를 전환하고 이전 디렉터리를 보관해 되돌릴 수 있게 한다.
|
||
+
|
||
+ 오프라인 캐시를 제공하는 PWA는 아니다. 정적 자산의 최초 로드에는 해당 서버
|
||
+ 연결이 필요하다. 페이지·오디오 캐시가 서로 다른 버전으로 섞이지 않게 HTML과
|
||
+ 음성의 재검증 정책을 유지한다.
|
||
+
|
||
|
||
❯ tests/release.test.ts:16:35
|
||
14| for (const path of ["README.md", "docs/DEPLOYMENT.md"]) {
|
||
15| const document = readFileSync(path, "utf8");
|
||
16| expect.soft(document, path).toContain("22.22.2");
|
||
| ^
|
||
17| expect.soft(document, path).not.toContain("22.12");
|
||
18| }
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/6]⎯
|
||
|
||
FAIL tests/release.test.ts > 릴리스 문서 계약 > Node 최소 버전은 잠긴 의존성이 요구하는 22.22.2와 일치한다
|
||
AssertionError: docs/DEPLOYMENT.md: expected '# 정적 배포 인계\n\n이 문서는 **Neo가 검토 후 수행할 절…' not to contain '22.12'
|
||
|
||
- Expected
|
||
+ Received
|
||
|
||
- 22.12
|
||
+ # 정적 배포 인계
|
||
+
|
||
+ 이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
|
||
+ 접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
|
||
+
|
||
+ ## 산출물
|
||
+
|
||
+ Node.js 22.12 이상 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
|
||
+ `npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
|
||
+ `dist/audio/`의 WAV 101개와 manifest가 반드시 포함되어야 한다. 런타임은
|
||
+ Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
|
||
+
|
||
+ Canonical repository는 [OZ-Group/Find-Word](https://source.ibit.kr/OZ-Group/Find-Word)다.
|
||
+ 프로젝트 자체는 private / all-rights-reserved이며 [LICENSE](../LICENSE)를 따른다.
|
||
+ React, ReactDOM, scheduler의 MIT 전문을 담은 `dist/THIRD_PARTY_NOTICES.md`도
|
||
+ 반드시 배포한다. `build` 검증기는 고지 파일이 없거나 축약·변조되면 실패한다.
|
||
+
|
||
+ 현재 앱은 도메인 루트(`/`) 배포 기준이다. 하위 경로 배포 시 Vite base뿐 아니라
|
||
+ 음성 URL도 함께 변경하고 테스트해야 한다. 예정 도메인은 `findword.heart-m.com`이다.
|
||
+
|
||
+ ## Nginx 예시
|
||
+
|
||
+ 아래 root는 예시다. 실제 경로·기존 서버 블록·인증서는 Neo가 확인한다.
|
||
+ 기존 서비스 설정을 덮어쓰지 않고 검토 후 적용한다.
|
||
+
|
||
+ ```nginx
|
||
+ # 정적 릴리스 디렉터리를 가리킨다. 인증서와 HTTPS 설정은 운영자가 별도 추가한다.
|
||
+ server {
|
||
+ listen 80;
|
||
+ server_name findword.heart-m.com;
|
||
+ root /var/www/findword/current;
|
||
+ index index.html;
|
||
+
|
||
+ # 기본 MIME 정의로 JS/CSS/WAV가 올바른 타입으로 제공되게 한다.
|
||
+ include /etc/nginx/mime.types;
|
||
+ add_header X-Content-Type-Options nosniff always;
|
||
+ add_header Referrer-Policy no-referrer always;
|
||
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; media-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;
|
||
+
|
||
+ # 파일명이 해시로 바뀌는 번들만 장기 캐시한다.
|
||
+ location /assets/ {
|
||
+ try_files $uri =404;
|
||
+ expires 1y;
|
||
+ }
|
||
+
|
||
+ # 음성 파일명은 재생성해도 같으므로 재검증한다. 누락 시 index.html 대신
|
||
+ # 404를 반환해야 앱이 명확한 음성 로드 오류와 재시도를 제공할 수 있다.
|
||
+ location /audio/ {
|
||
+ try_files $uri =404;
|
||
+ expires -1;
|
||
+ }
|
||
+
|
||
+ # SPA의 직접 진입·새로고침을 index.html로 받되 HTML은 재검증한다.
|
||
+ location / {
|
||
+ try_files $uri $uri/ /index.html;
|
||
+ expires -1;
|
||
+ }
|
||
+ }
|
||
+ ```
|
||
+
|
||
+ ## 인수 확인
|
||
+
|
||
+ 1. 서버에 적용하기 전 `nginx -t`, 기존 서비스 충돌, 파일 읽기 권한을 확인한다.
|
||
+ 2. TLS/HTTPS와 DNS를 운영자가 확인한다. 실제 도메인에서 시작 후 음성이 들리는지 확인한다.
|
||
+ 3. `audio/intro.wav`와 목표 WAV가 정상 응답하고 존재하지 않는 오디오는 404인지 확인한다.
|
||
+ 4. 모바일 Safari/Chrome, 데스크톱에서 음량·중단·다시 듣기·10문제 완주를 직접 확인한다.
|
||
+ 5. 원자적으로 릴리스 디렉터리를 전환하고 이전 디렉터리를 보관해 되돌릴 수 있게 한다.
|
||
+
|
||
+ 오프라인 캐시를 제공하는 PWA는 아니다. 정적 자산의 최초 로드에는 해당 서버
|
||
+ 연결이 필요하다. 페이지·오디오 캐시가 서로 다른 버전으로 섞이지 않게 HTML과
|
||
+ 음성의 재검증 정책을 유지한다.
|
||
+
|
||
|
||
❯ tests/release.test.ts:17:39
|
||
15| const document = readFileSync(path, "utf8");
|
||
16| expect.soft(document, path).toContain("22.22.2");
|
||
17| expect.soft(document, path).not.toContain("22.12");
|
||
| ^
|
||
18| }
|
||
19| });
|
||
|
||
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[6/6]⎯
|
||
|
||
|
||
Test Files 1 failed (1)
|
||
Tests 1 failed | 3 skipped (4)
|
||
Start at 05:54:57
|
||
Duration 357ms (setup 64%, transform 18%, tests 14%, worker 2%, import 2%)
|