대규모 풀 리퀘스트를 병합하고, 새로운 사이트 디자인이 프로덕션 환경에 성공적으로 배포되었습니다. 페이지를 새로고침하니 CSS는 완벽하게 적용되었네요. 그런데 브라우저 탭에는 여전히 2023년에 쓰던 깨진 예전 로고가 고집스럽게 남아있습니다. 배포 후 파비콘이 업데이트되지 않아 중요한 런칭을 망칠까 봐 머리를 쥐어뜯고 있다면, 여러분만 겪는 문제가 아닙니다.
파비콘과 같은 정적 자원(Static assets)은 캐시가 매우 강력하게 적용됩니다. 브라우저, 프록시, 그리고 CDN 엣지 네트워크는 대역폭을 절약하기 위해 파비콘을 영구적으로 캐시하려고 담합이라도 한 것 같습니다.
'브라우저 캐시를 지우세요' 같은 뻔한 조언은 건너뛰겠습니다. 로컬 브라우저의 캐시 문제 해결이 필요하다면 로컬 파비콘 캐시 강제 삭제 가이드를 확인하세요. 오늘 다룰 내용은 배포 관점입니다. 새로 배포된 사이트에 접속하는 모든 사용자에게 새 아이콘을 강제로 보여주는 방법에 집중하겠습니다.
빠른 해결책: 쿼리 스트링을 통한 캐시 무효화
시간이 없고 당장 새 아이콘을 띄워야 한다면 쿼리 스트링(Query String)을 사용하세요. 가장 오래된 방법이지만, 여전히 완벽하게 작동합니다.
메인 HTML 템플릿(예: index.html 또는 _document.tsx)을 열고 파비콘 URL 뒤에 버전 파라미터를 추가합니다.
<!-- 변경 전: -->
<link rel='icon' href='/favicon.svg'>
<!-- 변경 후: -->
<link rel='icon' href='/favicon.svg?v=2'>?v=2(또는 타임스탬프나 Git 커밋 해시)를 추가하면, 브라우저는 이를 완전히 새로운 파일로 인식합니다. 로컬 캐시를 우회하고 새로운 네트워크 요청을 강제하게 되죠. 만약 이렇게 해도 여전히 문제가 있다면 경로 설정 자체의 문제일 수 있으니 파비콘 문제 해결 가이드를 참고하세요.
프로덕션 환경에서 파비콘이 멈춰있는 이유
쿼리 스트링으로 문제가 해결되었다면 다행입니다. 하지만 애초에 왜 이런 일이 발생했을까요? 모던 웹 앱을 배포할 때, 파일들은 여러 단계의 강력한 캐시 계층을 통과하게 됩니다.
1. CDN 엣지 캐싱
Vercel, Netlify, Cloudflare 또는 AWS CloudFront를 사용한다면 정적 파일은 전 세계 엣지 노드에 분산됩니다. 이 네트워크들은 favicon.ico라는 파일명만 보고 메모리에서 바로 파일을 제공합니다.
배포 파이프라인에서 정적 파일에 대한 CDN 캐시 무효화(Invalidation)를 명시적으로 실행하지 않는 한, 엣지 노드는 TTL(Time-To-Live)이 만료될 때까지 예전 아이콘을 계속 제공합니다. 이는 때로 24~48시간이 걸리기도 합니다.
2. 빌드 도구의 해시 누락
Vite, Webpack, Next.js 같은 모던 번들러는 매우 똑똑합니다. 빌드할 때마다 CSS와 JS 파일에 고유한 해시(예: main.a8b4c.js)를 추가하여 사용자가 항상 최신 코드를 받도록 보장합니다.
하지만 파비콘은 보통 public/이나 static/ 디렉토리에 위치합니다. 빌드 도구들은 이 파일들을 해시 처리 없이 출력 폴더로 그대로 복사하는 경우가 많습니다. 파일명이 정확히 동일하게 유지되므로, 브라우저는 파일을 다시 다운로드할 이유를 찾지 못합니다.
3. 서비스 워커의 함정 (PWA)
사이트가 프로그레시브 웹 앱(PWA)이라면, 서비스 워커(Service Worker)가 네트워크 요청을 가로채고 있을 것입니다. 서비스 워커는 앱 쉘과 아이콘을 공격적으로 캐시하는 것으로 악명 높습니다.
만약 sw.js 파일이 /favicon.png를 캐시하고 있고 새 배포 시 업데이트 메커니즘이 트리거되지 않았다면, CDN을 우회하더라도 Cache Storage API에서 무기한으로 예전 아이콘을 제공하게 됩니다.
전문가들의 파비콘 캐시 문제 예방 전략
쿼리 스트링은 훌륭한 임시방편이지만, 일부 엄격한 기업용 프록시는 캐시하기 전에 쿼리 파라미터를 제거해버리기도 합니다. 가장 완벽한 방법은 파일명 자체를 버저닝하는 것입니다.
전략 1: 실제 파일명 버저닝
대대적인 리브랜딩을 할 때는 쿼리 스트링에 의존하지 말고 파비콘 파일의 이름을 물리적으로 변경하세요.
- 기존
favicon.ico삭제 - 새로운
favicon-v2.ico업로드 - HTML 태그가 새 파일명을 가리키도록 업데이트
GitHub가 동적 상태 아이콘을 어떻게 처리하는지 살펴보세요. 읽지 않은 알림이 있을 때, GitHub는 캐시된 파일을 덮어쓰려 하지 않습니다. href 속성을 완전히 다른 파일 경로(예: 파란 점이 있는 SVG)로 교체해버립니다. 실제 경로를 변경하는 것만이 모든 캐시 계층을 즉시 우회할 수 있는 100% 확실한 방법입니다.
전략 2: 올바른 Cache-Control 헤더 설정
서버 구성(Nginx, Apache 또는 Node 서버)을 제어할 수 있다면, 파비콘에 대해 구체적인 Cache-Control 헤더를 설정해야 합니다.
# Nginx 파비콘 설정 예시
location ~* \.(ico|png|svg)$ {
expires 1d;
add_header Cache-Control 'public, max-age=86400, must-revalidate';
}max-age를 짧게(1년 대신 1일 등) 설정하면, 예전 아이콘이 캐시되더라도 몇 달 동안 새 배포를 방해하는 불상사를 막을 수 있습니다.
마무리
배포 후 파비콘이 업데이트되지 않는 문제를 다루는 것은 웹 개발자라면 누구나 거치는 통과의례입니다. 브라우저부터 CDN, 서비스 워커에 이르는 캐시 계층은 웹을 빠르게 만들기 위해 설계되었지만, 정적 자원을 업데이트할 때는 엄청난 좌절감을 안겨줍니다.
다음 리브랜딩 때는 이런 골칫거리를 미리 방지하세요. Mzu favicondl을 사용하여 선명하고 모던한 아이콘 세트를 생성하고, 물리적 파일명에 버전 번호를 포함시킨 뒤, 사용자들이 즉시 새로운 브랜드를 볼 수 있다는 확신을 가지고 배포하시기 바랍니다.