웹 서비스를 운영하다 보면 배포를 완료했음에도 불구하고 사용자의 브라우저에서 이전 버전의 자산이 계속 노출되는 상황이 발생하더라고요. 특히 이미지나 스타일시트, 자바스크립트 파일이 업데이트되었음에도 불구하고 브라우저가 기존에 캐싱된 파일을 재사용하면서 발생하는 문제는 사용자 경험에 큰 영향을 줄 수 있는 부분이라 적절한 캐시 제어 전략이 필요해 보여요. 브라우저는 기본적으로 서버가 보내주는 HTTP 응답 헤더를 바탕으로 해당 자원을 얼마나 오랫동안 로컬에 저장할지 판단하게 되는데, 이때 핵심적인 역할을 하는 것이 바로 Cache-Control 헤더예요.

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
    expires max;
    add_header Cache-Control "public, max-age=31536000, immutable";
}

location / {
    add_header Cache-Control "no-cache, no-store, must-revalidate";
}

Cache-Control 헤더 내의 max-age 속성은 초 단위로 캐시를 유지할 기간을 설정하는 역할을 수행해요. 예를 들어 31536000초로 설정하면 브라우저가 약 1년 동안 해당 파일을 캐시로 유지하게 되더라고요. 이때 no-cache라는 값은 캐시를 아예 사용하지 않는다는 의미가 아니라, 캐시를 사용하기 전에 항상 서버에 해당 파일이 변하지 않았는지 확인을 거치라는 의미로 해석해야 해요. 이 과정에서 서버는 내용이 변하지 않았다면 304 Not Modified라는 상태 코드를 반환하여 불필요한 데이터 전송을 방지할 수 있어요. 만약 보안상 이유나 실시간성 때문에 캐시를 아예 생성하지 않아야 하는 경우에는 no-store를 사용하여 브라우저가 데이터를 저장하지 못하도록 강제할 수 있더라고요.

실무에서는 모든 자산에 동일한 정책을 적용하기보다 자원의 성격에 따라 구분해서 대응하는 방식이 효율적이에요. 폰트나 로고 이미지처럼 거의 변하지 않는 자산은 max-age를 길게 설정하여 네트워크 비용을 절감하고, 자주 업데이트되는 스크립트나 스타일시트의 경우에는 캐시 버스팅 기법을 병행하는 것이 일반적이에요. 캐시 버스팅은 파일명 뒤에 해시값이나 버전 번호를 붙여 파일의 경로 자체를 매번 다르게 생성하는 방식인데, 이를 Cache-Control과 함께 활용하면 브라우저 캐시를 최대한 활용하면서도 새로운 버전이 배포될 때 즉각적으로 반영되는 구조를 안정적으로 구축할 수 있더라고요.

+ Recent posts