정의
Service Worker(페이지와 네트워크 사이에서 요청을 가로채는 백그라운드 스크립트)에 오프라인 대비 "셸 폴백"을 넣을 때, 네트워크가 실패하면 앱 껍데기 HTML(예: /)을 대신 돌려주도록 짜기 쉽다. 하지만 이 폴백을 모든 요청에 걸면, 이미지·API 요청이 실패했을 때도 HTML을 반환해 <img>가 깨지고 blob이 셸 캐시에 무한 적재된다. 셸 폴백은 반드시 request.mode === 'navigate' 인 문서 이동 요청에만 걸어야 한다.
셸(app shell) 은 화면 골격을 담은 최소 HTML, request.mode 는 그 요청이 페이지 이동인지(navigate) 이미지·스크립트 같은 하위 리소스인지 구분하는 표식이다.
왜 필요한가
증상: 사진이 깨져 보이고, 시간이 지날수록 브라우저 저장소가 알 수 없는 데이터로 부풀어 오른다. 원인은 SW가 모든 동일 출처 GET을 가로채 실패 시 셸("/")로 폴백하게 짜인 것이다. 사진 요청이 네트워크 실패로 셸 폴백을 타면, <img>가 이미지 대신 HTML 문서를 받아 렌더에 실패한다. 게다가 그 HTML이 사진 URL 키로 캐시에 쌓여 저장소가 오염된다.
문서 이동(페이지 로드)만 셸로 폴백하면, 이미지·API·정적 자산은 네트워크 결과(또는 각자의 캐시 전략)를 그대로 받아 이런 오염이 사라진다.
동작 원리
핵심은 "셸은 문서 요청의 폴백일 뿐"이라는 것이다. 이미지·API에는 각자의 정책(예: 이미지엔 stale-while-revalidate 캐시, API는 네트워크 직행)을 따로 준다.
실무 적용
- SW 캐시 전략을 요청 종류별로 분기한다:
navigate→ 셸 폴백, 이미지 GET → SWR 캐시, API → 네트워크(+오프라인 큐). - 기존에 오염된 캐시가 있으면 캐시 버전을 올려(
v1→v2) 과거 항목을 purge한다. - 셸 폴백을 넣기 전에 "이 폴백이 문서가 아닌 요청에도 걸리나?"를 반드시 확인한다.
트레이드오프
- navigation 한정 셸 폴백: 오프라인에서도 페이지 골격은 뜨고, 이미지·API는 깨진 HTML 대신 정상 실패(빈 이미지/에러)로 처리된다. 대신 요청 종류별 분기를 직접 짜야 한다.
- 전체 셸 폴백(안티패턴): 코드는 짧지만 이미지 깨짐 + 캐시 오염을 부른다.
사용하면 안 되는 경우
- SW를 아예 쓰지 않는 앱 — 서버 CDN·HTTP 캐시로 충분한지 먼저 본다.
- 오프라인 문서 표시가 필요 없는 경우 — 셸 폴백 자체가 불필요.
흔한 실수
- 모든 GET에 셸 폴백을 걸어 이미지·API 실패에 HTML 반환.
- 폴백으로 받은 HTML이 리소스 URL 키로 캐시에 적재돼 저장소 오염.
- 캐시 오염을 고친 뒤 캐시 버전을 안 올려 과거 오염분이 남음.
관련 개념
- service-worker-stale-while-revalidate — 이미지 등 하위 리소스의 캐시 전략
- server-image-proxy-transcoding-cache — 프록시 이미지 URL과 캐시 역할 분담