두 번째 방문이 첫 방문보다 느리다면 — Cache-Control 헤더 한 줄로 결정되는 웹사이트 재방문 속도

첫 방문보다 두 번째 방문이 빨라야 정상이다

좋은 웹사이트에는 한 가지 공통점이 있습니다. 같은 사용자가 두 번째로 들어왔을 때 페이지가 눈에 띄게 빨리 뜬다는 점입니다. 첫 방문 때는 모든 이미지·폰트·자바스크립트를 새로 받아야 하지만, 두 번째 방문은 이미 다운로드한 자산을 브라우저가 그대로 꺼내 쓰면 되기 때문입니다.

그런데 의외로 많은 국내 웹사이트가 이 혜택을 누리지 못합니다. 이미지 한 장, 로고 하나까지 매번 서버에서 다시 받아오는 식입니다. 원인은 거의 같습니다. 응답에 적절한 Cache-Control 헤더가 빠져 있거나, 잘못 설정되어 있기 때문입니다.

Cache-Control 헤더 한 줄이 모든 걸 정한다

브라우저는 서버가 내려준 응답 헤더를 보고 "이 파일을 얼마나 보관할지", "다음에는 새로 받을지 캐시를 쓸지"를 결정합니다. 그 결정의 기준이 바로 Cache-Control 헤더입니다.

  • max-age=31536000 — 이 파일은 1년 동안 다시 받지 마세요. 해시가 붙은 정적 파일에 적합합니다.
  • public — CDN과 중간 프록시도 함께 캐싱해도 좋다는 신호입니다.
  • private — 사용자별로 다른 응답이라 브라우저만 보관해야 한다는 뜻입니다.
  • no-cache — 캐시는 두되, 매번 서버에 "변경됐어?"를 묻고 사용합니다.
  • no-store — 아예 저장하지 마세요. 결제·로그인 페이지에 씁니다.
  • immutable — 만료 전에는 검증 요청도 보내지 말라는 강한 신호입니다.

ETag와 Last-Modified — 캐시가 살아 있는지 확인하는 두 가지 방법

max-age가 만료되면 브라우저는 그 파일을 무조건 처음부터 다시 받지 않습니다. 대신 서버에 "내가 가진 버전이 아직 유효해?"라고 묻습니다. 이때 사용하는 게 ETagLast-Modified입니다.

서버가 "변하지 않았다"고 응답하면(304 Not Modified) 본문 데이터는 다시 내려보내지 않고 헤더만 오갑니다. 같은 이미지를 통째로 다시 받는 것과 비교해 수십 배 빠른 가벼운 왕복이 됩니다. 두 헤더 중 하나만 켜두어도 충분하지만, ETag가 좀 더 정확합니다.

절대 길게 캐시되면 안 되는 것들

캐싱은 만능이 아닙니다. HTML 페이지·로그인 화면·결제 페이지·관리자 페이지를 길게 캐시하면 사고가 납니다. 사장님이 새 글을 올렸는데 어제 캐시가 그대로 떠버리거나, 로그아웃한 사용자에게 다른 사람의 마이페이지가 잠깐 보이는 문제가 여기서 시작됩니다.

안전한 기본값은 이렇습니다. HTML은 no-cache 또는 매우 짧은 max-age로, 사용자별 응답은 private, no-store로, 그리고 해시가 붙은 정적 파일(예: app.4f2a.js)만 1년 immutable로 캐싱합니다.

우리 사이트의 캐싱 정책, 한 장으로 정리하면

실무에서 자주 쓰는 기본 가이드는 다음과 같습니다.

  • 이미지·폰트·해시 붙은 JS/CSS — public, max-age=31536000, immutable
  • 해시 없는 정적 파일 — public, max-age=86400 정도로 짧게
  • HTML 페이지 — no-cache 또는 max-age=0, must-revalidate
  • API 응답·로그인 후 화면 — private, no-store

이 한 장이면 첫 방문은 그대로 두면서 두 번째 방문 속도를 크게 끌어올릴 수 있습니다. 추가로 CDN과 함께 쓰면 첫 방문조차 가까운 서버에서 받게 되어 효과가 배가됩니다.

속도는 결국 신뢰다

0.5초 차이가 이탈률을 흔드는 시대입니다. 그런데 캐싱은 새 코드를 짜지 않고도 헤더 한 줄로 즉시 효과가 나타나는 보기 드문 개선입니다. CYAN 에이전시는 사이트를 만들 때부터 캐싱 정책을 함께 설계하고, 운영 중인 사이트도 응답 헤더를 점검해 무거운 사이트를 가볍게 다듬는 작업을 진행하고 있습니다. 빠른 사이트는 결국 신뢰받는 사이트입니다.