REUSABLE RELEASE PATTERN

웹앱 업데이트
감지·안내·갱신 프로세스

새 배포를 감지하고 사용자에게 안내한 뒤 오래된 브라우저·PWA·WebView 캐시를 안전하게 교체하는 재사용 설계서입니다.

문서 v1.0Moodify 사례Web · PWA · Capacitor2026-09-08

1. 설계 목표

일관된 릴리스

번들 빌드 ID와 공개 webVersion이 같은 commit SHA를 가리킵니다.

안전한 전환

앱 시작·복귀 시 새 버전을 알리고 선택 또는 필수 정책에 따라 갱신합니다.

이중 버전 모델

앱스토어 바이너리와 서버에서 교체되는 웹 콘텐츠 버전을 따로 관리합니다.

역할
webVersion운영 웹 결과물의 고유 SHA
currentWebVersion실행 중인 JS 번들의 빌드 ID
latestVersion앱스토어 최신 버전
minimumVersion계속 사용할 수 있는 최소 앱 버전

2. 전체 처리 흐름

01 RELEASEmain·원격 SHA 확정
02 BUILDSHA를 번들에 주입
03 MANIFEST동일 SHA로 JSON 생성
04 DEPLOY원자적 운영 배포
05 DETECT앱 시작·복귀 검사
06 APPLY스토어 이동·캐시 우회
native version outdated? ─ yes → optional/required store update
                         └ no  → webVersion differs? ─ yes → web refresh prompt
                                                   └ no  → keep current session

3. 배포와 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.htmlno-cache, no-store새 자산 경로 즉시 반영
/app-version.jsonno-cache, no-store판정값 캐시 방지
해시 JS/CSS1년 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-02webVersion 변경웹 업데이트 안내
U-03업데이트 선택새 캐시 버스터로 교체
U-04앱 최신 미만선택 스토어 안내
U-05앱 최소 미만필수 스토어 안내
U-06manifest 실패앱 정상 사용
U-07탭·앱 복귀최신 manifest 재검사
U-08배포 후HEAD = origin/main = webVersion

9. 다른 프로젝트 적용 체크리스트

10. Moodify 구현 매핑

역할파일
버전 비교·manifest·갱신src/utils/appUpdate.js
검사 이벤트·안내 모달src/components/AppUpdatePrompt.jsx
전역 마운트src/App.jsx
빌드 IDvite.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 전환 계획을 권장합니다.