동시에 두 로컬 프로젝트를 작업하다가 한 프로젝트가 쓰던 localhost:11111 포트를 다른 프로젝트에서 열었다. 새 프로젝트의 개발 서버를 실행했는데도 브라우저는 이전 화면을 열려고 해서 자꾸 하얀 화면이 뜨는 것.
강력 새로고침을 하면 잠시 달라지는 것처럼 보였지만 일반 새로고침을 하면 다시 같은 문제가 나타났다. 이 경우에는 HTTP 캐시만 의심하기보다, 이전 프로젝트가 등록한 Service Worker가 아직 해당 origin의 요청을 제어하고 있는지 확인해야 했다.

같은 포트를 쓰면 같은 origin을 다시 쓰게 된다
브라우저는 웹 저장소와 Service Worker를 URL 전체가 아니라 origin 단위로 구분한다. origin은 스킴, 호스트, 포트의 조합이다. 따라서 http://localhost:11111과 http://localhost:3000은 포트가 달라 서로 다른 origin이지만, 이전 프로젝트와 새 프로젝트가 모두 http://localhost:11111을 사용하면 브라우저 입장에서는 같은 웹 앱의 주소다.
PWA 또는 개발용 Service Worker가 / 범위로 등록되어 있었다면 이 origin 아래의 페이지와 리소스 요청을 제어할 수 있다. 이전 프로젝트의 개발 서버를 멈춘 뒤에도 등록 자체가 자동으로 사라지는 것은 아니었다. 기존 Service Worker가 fetch 이벤트를 받아 캐시, 오프라인 응답, 이전 앱 셸을 반환하고 있던 것이다.
Service Worker는 브라우저와 네트워크 사이에서 요청을 가로채는 프록시처럼 동작하기 때문에 일반 새로고침이나 HTTP 캐시 무효화만으로는 활성 Service Worker를 제거하지 못한다.
Service Worker 상태 확인
Chrome DevTools에서 문제 주소를 연 뒤 Application > Service workers로 이동한다. 이 화면에서 현재 origin에 등록된 Service Worker와 제어 상태를 확인할 수 있다.
아래 화면에서 http://localhost:11111/에 등록된 Service Worker와 Bypass for network, Unregister 항목을 확인할 수 있다.

http://localhost:11111/에 활성 상태의 Worker가 있었다. 현재 포트에서 이전 앱의 Worker가 계속 실행 중인 것이다. Source에 표시되는 파일명도 이전 프로젝트의 등록 코드와 맞는지 확인한다.
같은 화면에서 해결 방법은 두 가지다.
Bypass for network: 현재 DevTools 세션에서 Service Worker를 거치지 않고 네트워크 요청을 보낸다. Worker가 원인인지 빠르게 구분할 때 쓴다.Unregister: 해당 origin에 등록된 Service Worker를 해제한다. 이전 프로젝트의 Worker를 더 이상 쓰지 않을 때 선택한다.
바로 Unregister 해서 워커를 꺼버렸더니 바로 정상작동했다.
콘솔에서 등록을 확인하거나 해제하는 방법
DevTools 화면을 열기 어려운 경우에는 현재 origin의 등록 목록을 콘솔에서 확인할 수 있다.
const registrations = await navigator.serviceWorker.getRegistrations()
console.table(
registrations.map((registration) => ({
scope: registration.scope,
active: registration.active?.scriptURL,
}))
)
이 코드는 현재 origin에 등록된 Service Worker의 범위와 활성 스크립트 주소만 보여 준다. 다른 origin의 등록은 보이지 않으므로, 포트를 바꿨다면 문제가 발생한 정확한 주소에서 실행해야 한다.
const registrations = await navigator.serviceWorker.getRegistrations()
await Promise.all(registrations.map((registration) => registration.unregister()))
location.reload()
로컬 개발 환경에서 이전 Worker를 모두 해제해야 한다면 위 코드를 실행한다. unregister()는 현재 origin의 등록을 대상으로 한다.
참고한 페이지
developer.chrome.com
프로그레시브 웹 앱 디버그 | Chrome DevTools | Chrome for Developers
Application 패널을 사용하여 웹 앱 매니페스트, 서비스 워커 및 서비스 워커 캐시를 검사, 수정 및 디버그합니다.
developer.mozilla.org
Using Service Workers - Web APIs | MDN
Service Worker의 등록, 설치, 활성화, 캐시 제어와 요청 처리 흐름을 설명합니다.
developer.mozilla.org
Origin - Glossary | MDN
Origin은 스킴, 호스트, 포트의 조합으로 정의됩니다.
댓글 0개