웹앱 업데이트
감지·안내·갱신 프로세스
새 배포를 감지하고 사용자에게 안내한 뒤 오래된 브라우저·PWA·WebView 캐시를 안전하게 교체하는 재사용 설계서입니다.
1. 설계 목표
일관된 릴리스
번들 빌드 ID와 공개 webVersion이 같은 commit SHA를 가리킵니다.
안전한 전환
앱 시작·복귀 시 새 버전을 알리고 선택 또는 필수 정책에 따라 갱신합니다.
이중 버전 모델
앱스토어 바이너리와 서버에서 교체되는 웹 콘텐츠 버전을 따로 관리합니다.
| 값 | 역할 |
|---|---|
webVersion | 운영 웹 결과물의 고유 SHA |
currentWebVersion | 실행 중인 JS 번들의 빌드 ID |
latestVersion | 앱스토어 최신 버전 |
minimumVersion | 계속 사용할 수 있는 최소 앱 버전 |
2. 전체 처리 흐름
native version outdated? ─ yes → optional/required store update
└ no → webVersion differs? ─ yes → web refresh prompt
└ no → keep current session3. 배포와 manifest
배포 차단 조건
- 현재 브랜치 main
- 작업 트리 clean
- HEAD = origin/main
- Production 대상
동일 식별자
commit SHA를 번들 상수와 app-version.json 양쪽에 주입합니다. 네이티브 정책은 별도 파일에서 합칩니다.
{
"webVersion": "full-git-commit-sha",
"android": { "latestVersion": "1.3.4", "minimumVersion": "1.3.1", "storeUrl": "..." },
"ios": { "latestVersion": "1.0.0", "minimumVersion": "1.0.0", "storeUrl": "..." }
}운영에서는 package 버전이 아니라 배포 SHA를 사용합니다.
4. 캐시 정책
| 리소스 | 정책 | 목적 |
|---|---|---|
/, /index.html | no-cache, no-store | 새 자산 경로 즉시 반영 |
/app-version.json | no-cache, no-store | 판정값 캐시 방지 |
| 해시 JS/CSS | 1년 immutable | 재다운로드 방지 |
HTML을 장기 캐시하면 새 JS를 가리키지 못하고, 해시 자산을 무캐시로 만들면 성능이 낭비됩니다.
5. 감지와 판정
검사 이벤트
- 최초 마운트
- 탭 visible 복귀
- pageshow·BFCache 복귀
- 네이티브 appStateChange 활성화
무캐시 요청
cache: no-store와 timestamp query를 함께 씁니다. 실패하면 앱을 막지 않고 검사를 건너뜁니다.
1. current native < minimum → required native update 2. current native < latest → optional native update 3. manifest webVersion != bundle build ID → optional web update 4. otherwise → no prompt
6. 사용자 안내와 적용
| 상태 | 기본 동작 | 나중에 |
|---|---|---|
| 웹 업데이트 | 캐시 버스터 URL로 location.replace | 허용 |
| 네이티브 선택 | 앱스토어 외부 열기 | 허용 |
| 네이티브 필수 | 지원 종료 안내·스토어 이동 | 불가 |
const url = new URL(window.location.href);
url.searchParams.set('_app_update', String(Date.now()));
window.location.replace(url.toString());일부 설치형 PWA·WebView는 업데이트 후 앱을 완전히 종료하고 다시 실행해야 프로세스 캐시까지 교체됩니다.
idle → checking → up-to-date / web-update / native-optional / native-required / check-failed
7. 장애·보안·운영
안전한 실패
- manifest 5xx·JSON 오류는 skip
- 버전 값 누락 시 추측 금지
- 오프라인에서 모달 강제 금지
- 버튼 연타와 중복 요청 방지
운영 원칙
- manifest에 비밀정보 금지
- 대상 프로젝트·브랜치 고정
- main·배포 SHA·공개 버전 대조
- 필수 업데이트 최소 사용
Service Worker 앱은 registration.update(), skipWaiting, controllerchange 정책을 별도로 설계하고, 다중 탭은 BroadcastChannel을 검토합니다.
8. 테스트 시나리오
| ID | 조건 | 기대 결과 |
|---|---|---|
| U-01 | 웹 버전 동일 | 모달 없음 |
| U-02 | webVersion 변경 | 웹 업데이트 안내 |
| U-03 | 업데이트 선택 | 새 캐시 버스터로 교체 |
| U-04 | 앱 최신 미만 | 선택 스토어 안내 |
| U-05 | 앱 최소 미만 | 필수 스토어 안내 |
| U-06 | manifest 실패 | 앱 정상 사용 |
| U-07 | 탭·앱 복귀 | 최신 manifest 재검사 |
| U-08 | 배포 후 | HEAD = origin/main = webVersion |
9. 다른 프로젝트 적용 체크리스트
- 번들과 manifest에 같은 빌드 ID 주입
- 플랫폼별 latest/minimum/store URL 분리
- HTML·manifest와 해시 자산 캐시 분리
- 최초 실행·탭 복귀·앱 복귀 연결
- 선택/필수 업데이트 UI 구분
- 웹 갱신과 앱스토어 이동 분기
- 오류 시 정상 사용 폴백
- main·clean tree·remote SHA 배포 검증
- 배포 후 공개 manifest 자동 대조
- Service Worker 활성화 전략 검토
10. Moodify 구현 매핑
| 역할 | 파일 |
|---|---|
| 버전 비교·manifest·갱신 | src/utils/appUpdate.js |
| 검사 이벤트·안내 모달 | src/components/AppUpdatePrompt.jsx |
| 전역 마운트 | src/App.jsx |
| 빌드 ID | vite.config.js |
| manifest 생성 | scripts/generate-update-manifest.js |
| 운영 배포 | scripts/deploy-production.js |
| 네이티브 정책 | public/update-policy.json |
| 캐시 | public/_headers |
후속 개선
dismiss 버전 저장, 버튼 로딩, manifest 런타임 검증, 실제 번들 ID smoke test, Service Worker 전환 계획을 권장합니다.