75 lines
3.8 KiB
Markdown
75 lines
3.8 KiB
Markdown
# 정적 배포 인계
|
|||
|
|
|
||
|
|
이 문서는 **Neo가 검토 후 수행할 절차**다. Codex는 서버, DNS, 인증정보에
|
||
|
|
접근하지 않았고 배포·서비스 생성·Git commit/push도 하지 않았다.
|
||
|
|
|
||
|
|
## 산출물
|
||
|
|
|
||
|
|
Node.js 최소 **22.22.2** (`>=22.22.2`)의 지원 LTS 환경에서 `npm ci`, `npm run lint`, `npm test`,
|
||
|
|
`npm run build`를 실행한다. `dist/` 전체를 하나의 릴리스로 전달한다.
|
||
|
|
`dist/audio/`의 WAV 101개와 manifest가 반드시 포함되어야 한다. 런타임은
|
||
|
|
Nginx 정적 파일만 필요하며 Node 서버·TTS 엔진·DB·환경변수·API 키는 필요 없다.
|
||
|
|
잠긴 jsdom 계열 의존성 때문에 24.x는 24.15.0 이상, 26.x는 26.0.0 이상이어야 한다.
|
||
|
|
브라우저 검증의 Chromium sandbox는 기본 활성화다. sandbox를 지원하지 않는
|
||
|
|
격리된 개발 환경에서만 `PLAYWRIGHT_NO_SANDBOX=1`로 명시적으로 끈다.
|
||
|
|
|
||
|
|
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과
|
||
|
|
음성의 재검증 정책을 유지한다.
|