ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 강력새로고침 하면 불러오는데 일반새로고침은 불러오지 못한다? _ service worker
    Front-end 2025. 6. 4. 08:56
    반응형

    이런 문제를 겪어본 적이 있는지 모르겠다.

    "강력 새로고침을 하면 최신버전으로 업데이트가 되는데, 이 후 일반 새로고침을 하면 과거버전으로 돌아와요"
    여러가지 문제가 있을 수 있는데 내가 간과하고 있었던 것이 캐시의 "우선순위" 였다.

     

    캐싱, 서비스워커

     


     

    과거의 잔재 PWA

    회사에서 어떤 프로젝트를 했고, 프로토타입은 간단하게 만들자고 해서 MBTI 같은 느낌으로 심리 설문조사 앱을 만들었다.

    이 때, PWA까지 붙여서 폰에서 앱처럼 다운로드도 가능하게 만들었는데 이는 나중에 v2.0.0으로 나가자고 하며 어중이떠중이로 레거시가 되어갔다.

    그런데 어느 날, v1을 사용해야 한다고 하면서 카카오 로그인과 우리 카카오 채널 등록 여부를 확인하고 등록이 확인되면 설문조사가 가능하게 해달라고 요청 받았고, 부랴부랴 일을 끝 마치니 테스트도 없이 바로 사람들에게 공표를 해버리셨다....

    그렇게 올라간 프로젝트 v1.

    갑자기 올라가 두려운 마음에 Sentry도 우선 붙여뒀고, 현재는 필요없다고 생각하는 PWA나 다른 라이브러리들을 삭제하고 배포를 한 후 지켜보자고 했는데 문제가 발생했다.

    새로 배포한 내역들이 적용되지 않고 있었다.

    배포 문제인가? Nginx 문제인가? 웹 캐싱 문제인가?

    여러 고민을 해보면서 테스트를 이어갔다.

     

    문제 1. 모든 디바이스에서 똑같이 에러가 발생하지 않고 최신의 배포상태를 잘 유지하는 사람도 있다

    문제 2. 강력 새로고침 하면 최신의 빌드가 잘 나오지만, 이 후 일반 새로고침 하면 다시 과거의 버전으로 돌아간다

     

    그래서 우선은 캐시를 지우는 방향으로 고민해봤다.

    Nginx를 이용해서 index.html의 경우 캐시하지 않는 것으로 수정해봤고, React 자체적으로도 캐시하지 못하도록 태그를 넣고, 해시를 추가해서 빌드해보았다. 하지만 전부 실패였다.

    무슨 일인지 고민하면서 개발자도구를 뒤적이다가 Service worker 부분에 sw.js가 남아있는 것을 보고 아차 싶었다.

     

    sw.js는 이전에 PWA를 만들면서 나온 Service worker로 돌아가던 js 파일이다.

    이것은 웹보다 캐시 우선순위가 높다.

    그러니 이전 버전에서 설정된 Service worker가 새로운 버전에 적용되지 않아서 예전 Service worker가 Nginx 설정을 무시하여 지금과 같은 상황을 만들어낸 것이다.

     

     

    Service worker의 특징

    서비스워커(Service worker)는 브라우저의 백그라운드에서 실행되며 다음과 같은 특징을 가진다.

    첫 번째로 프록시 서버 역할을 할 수 있다.

    웹 애플리케이션, 브라우저, 네트워크 사이에서 프록시 서버 역할을 할 수 있다.

    네트워크 요청을 가로채고, 캐시된 리소스를 제공하거나, 네트워크 사용 가능 여부에 따라 적절한 조치를 취할 수 있는 것이다.

    두 번째는 독립적인 생명 주기를 가진다는 것이다.

    웹 페이지와 달리 독립적인 생명 주기를 가지고 있으며 별도의 스레드에서 실행이 된다.

    그래서 웹페이지가 닫혀있고, 네트워크가 연결되지 않아도 백그라운드에서 실행될 수 있는 것이다.

    세 번째는 HTTPS가 필수적인 요소이다.

    PWA를 만들 때도 HTTPS가 필수적이라는 말을 볼 수 있을 것이다. Service worker는 보안상의 이유로 HTTPS 환경에서만 동작한다는 특징을 가진다.

     

     

    왜 Service worker가 Nginx 설정을 무시하는가?

    Service worker의 특징이라고 할 수 있는 네트워크 요청을 가로채는 기능 때문이다.

    Nginx와 같은 웹 서버의 캐싱 설정보다 우선적으로 동작하게 되는데 이 때문에 현재의 나와 같은 새로운 버전을 배포해도 이미 캐시되어 버린 이전 버전을 보여주는 경우가 있는 것이다.

     

     

    새로고침 해도 새로운 버전이 보이지 않는다면?

    그렇다면 어떻게 이를 해결할 수 있을까? 

    몇 가지 방법이 있겠지만 어쨌든 현재 네트워크를 가로채고 있는 Service worker를 수정해줘야한다.

     

    1. Service worker unregister

    나와 같이 PWA가 더 이상 필요하지 않은 등 sw.js를 필요로 하지 않는다면 등록을 해제하면 될 것이다.

    이를 위해 사용할 메서드는 unregister() 이다.

      navigator.serviceWorker.getRegistrations().then((registrations) => {
        for(let registration of registrations) {
          registration.unregister();
        }
      })

     

    navigator.serviceWorker.getRegistrations()을 하면 내가 가진 Service worker를 볼 수 있어서 나의 경우에는 map을 돌려서 다 제거를 해버렸다.

    if ('serviceWorker' in navigator) {
      const registrations = await navigator.serviceWorker.getRegistrations();
      await Promise.all(registrations.map((r) => r.unregister()));
    }

     

    2. Service worker 업데이트

    기존 Service worker를 업데이트하여 새로운 캐시를 사용할 수 있도록 하는 방법도 있다.

    새로운 버전의 Service worker를 배포할 때 캐시 이름을 변경하면 브라우저는 이전 캐시를 사용하지 않고 새로운 캐시를 사용하게 된다.

    '그래도 이전 Service worker가 남아있으니 문제가 계속 되지 않을까...? 생각할 수 있지만 새 Service worker가 설치되면 이전 캐시를 삭제하는 로직을 추가할 수 있다.

    self.addEventListener('activate', function(event) {
      event.waitUntil(
        caches.keys().then(function(cacheNames) {
          return Promise.all(
            cacheNames.map(function(cacheName) {
              if (cacheName !== newCacheName) { // newCacheName은 새로운 캐시 이름
                return caches.delete(cacheName);
              }
            })
          );
        })
      );
    });

     

    3. Nginx에서 sw.js 캐시 금지 헤더 추가

     

     나와 같이 Nginx를 사용하고 있다면 sw.js에 대해서 항상 최신 파일을 받을 수 있도록 만드는 방법도 있다.

    초반에 서비스워커에 대한 문제라는 것을 인식하지 못했을 때 index.html을 캐싱하지 않도록 만들기도 해봤는데 문제가 해결되지도 않을 뿐더러 인터넷이 느린 경우 새로고침 할 때마다 페이지가 너무 늦게 뜨고, 특히 카카오 로그인 같은 불러와야 하는 부분에서 버튼이 너무 늦게 떠서 보이지 않다보니 이걸로는 해결 안되겠다 싶은 생각도 했었다.

    location /sw.js {
      add_header Cache-Control "no-cache, no-store, must-revalidate";
      add_header Pragma "no-cache";
      add_header Expires 0;
      try_files $uri $uri/ =404;
    }
    반응형
Designed by Tistory.