HTTP Cache
HTTP 캐싱은 웹 성능 최적화의 핵심 기술로, 네트워크 트래픽 감소, 응답 시간 단축, 서버 부하 경감을 목표로 한다.
HTTP 캐싱은 성능과 비용 효율성을 극대화하는 필수 기술이다. 적절한 Cache-Control
설정과 검증 메커니즘을 통해 현명하게 활용해야 한다.
캐시 전략은 애플리케이션 특성(정적/동적 비율, 업데이트 주기)에 맞게 설계하고, 지속적인 모니터링으로 최적 상태를 유지하는 것이 핵심이다.
캐싱 기본 원리
캐시 동작 프로세스
- 초기 요청: 클라이언트가 리소스(이미지, CSS, JS)를 요청하면 서버는
Cache-Control
헤더와 함께 응답한다.
max-age=3600
: 1시간 유효
캐시 저장: 브라우저는 응답을 메모리/디스크 캐시에 저장한다.
- 메모리 캐시: 빠르지만 브라우저 종료 시 삭제
- 디스크 캐시: 지속적 저장 (e.g.,
public
리소스)
재요청 시:
- 유효 시간 내: 캐시에서 직접 제공 (Cache Hit)
- 유효 시간 초과: 서버에 검증 요청 (Cache Miss) →
304 Not Modified
또는 새 데이터 수신
캐시 유형 분류
- 저장 위치 기준
유형 | 저장 위치 | 예시 | 특징 |
---|---|---|---|
브라우저 캐시 | 클라이언트 | Chrome Disk Cache | 개인 전용, private 지시어 |
프록시 캐시 | 중간 서버 | CDN (Cloudflare, Akamai) | 다수 사용자 공유, public |
게이트웨이 캐시 | 서버 앞단 | Nginx, Varnish | 서버 부하 분산 |
- 접근 권한 기준
- Private Cache: 사용자별 개인 데이터 (e.g.,
Authorization
헤더 포함 응답) - Shared Cache: 다수 사용자 공유 (e.g., CDN의 정적 자원)
- Private Cache: 사용자별 개인 데이터 (e.g.,
핵심 HTTP 헤더
HTTP/1.1부터는 Cache-Control
헤더를 통해 캐시 동작을 세밀하게 제어할 수 있다.
헤더 | 설명 | 예시 |
---|---|---|
Cache-Control | 캐시 동작 지정 | Cache-Control: max-age=3600 |
Expires | 만료 일시 지정 | Expires: Wed, 21 Oct 2024 07:28:00 GMT |
ETag | 리소스 버전 식별자 | ETag: "33a64df551425fcc55e4" |
Last-Modified | 마지막 수정 시간 | Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT |
Cache-Control
지시어 | 의미 | 예시 | |
---|---|---|---|
max-age=3600 | 캐시 유효 시간 (초) | max-age=86400 (24시간) | |
s-maxage=3600 | 공유 캐시 유효 시간 (초) | s-maxage=3600 | |
no-store | 캐시 저장 금지 (민감 정보) | 금융 거래 페이지 | |
no-cache | 항상 서버 검증 필요 | no-cache | |
public | 공유 캐시 저장 허용 | CDN 호환 리소스 | |
private | 브라우저 전용 저장 | 개인화 데이터 | |
must-revalidate | 만료 시 반드시 서버 검증 | 중요한 업데이트 자원 |
검증 헤더
- ETag: 파일 해시값 (e.g.,
"686897696a7c876b7e"
)- 리소스의 고유 식별자로, 리소스가 변경될 때마다 새로운 ETag 값이 부여된다.
- 클라이언트는
If-None-Match
헤더에 ETag 값을 포함하여 서버에 요청을 보내고, 서버는 리소스 변경 여부에 따라 200(OK) 또는 304(Not Modified) 상태 코드를 반환한다.
- Last-Modified: 최종 수정 시간
- 리소스의 최종 수정 시각을 나타낸다.
- 클라이언트는
If-Modified-Since
헤더에 이 값을 포함하여 서버에 요청을 보내며, 서버는 리소스가 해당 시각 이후로 변경되었는지 판단하여 응답한다.
캐싱 전략
전략 | 설명 | 사용 사례 |
---|---|---|
강한 캐싱 | max-age + immutable | 정적 자원 (이미지, 폰트) |
검증 캐싱 | no-cache + ETag | 자주 변경되는 JSON API |
조건부 캐싱 | max-age + must-revalidate | 중요한 업데이트 (e.g., CSS) |
강한 캐싱 (Strong Caching)
적용 예시: 해시가 포함된 정적 이미지
- 파일명:
logo.d41d8c.webp
(컨텐츠 해시 포함) - 동작:
- 브라우저는 1년간(
max-age=31536000
) 캐시 유지 - URL 변경 없이 컨텐츠 수정 시 자동 무효화(해시 변경)
immutable
로 백그라운드 재검증 방지
- 브라우저는 1년간(
사용처
- 웹 폰트(
.woff2
) - 아이콘 SVG 파일
- 프레임워크 번들 JS(
react.production.min.js
)
검증 캐싱 (Validation Caching)
적용 예시: 실시간 주식 가격 API
- 요청 검증:
- 동작:
- 캐시 저장은 허용하지만 매 요청 시 서버 검증
ETag
변경 시 새 데이터 전송(200 OK)- 변경 없을 시 304로 트래픽 절약
사용처
- 실시간 뉴스 피드
- 사용자 프로필 API
- 인증 토큰 검증 엔드포인트
조건부 캐싱 (Conditional Caching)
적용 예시: 글로벌 CSS 파일
- 만료 후 동작:
- 특징:
- 10분간 캐시 사용(
max-age=600
) - 만료 시
Last-Modified
기준 검증 - 반드시 서버 응답 확인(
must-revalidate
)
- 10분간 캐시 사용(
사용처
- CMS 컨텐츠
- 자주 업데이트되는 설정 파일
- A/B 테스트용 JS
전략 선택 가이드
기준 | 강한 캐싱 | 검증 캐싱 | 조건부 캐싱 |
---|---|---|---|
변경 빈도 | 거의 없음 | 매우 잦음 | 중간 |
무효화 방법 | URL 변경 | ETag/Last-Mod | 시간 기반 |
트래픽 절감 | 최대(100%) | 중간(304 활용) | 높음 |
적용 난이도 | 쉬움 | 복잡함 | 중간 |
최적화 팁:
- 정적 자원은
immutable
+ 해시 파일명 조합 권장- API 응답에
stale-while-revalidate
로 백그라운드 갱신 가능
1
Cache-Control: max-age=3600, stale-while-revalidate=300
캐시 무효화 메커니즘
- 수동 무효화
- 브라우저:
Ctrl + F5
(강력 새로고침) →Cache-Control: no-cache
- CDN: 퍼지(Purge) API 호출
- 브라우저:
- 자동 무효화
- 파일 이름 해싱:
style.[hash].css
- 쿼리 파라미터:
?v=2
(구식, 권장되지 않음)
- 파일 이름 해싱:
캐싱 장단점
장점
- 성능 향상: 초기 로드 시간 60-80% 감소
- 비용 절감: 트래픽 비용 최대 40% 감소
- 안정성: 서버 장애 시 캐시 제공 가능
단점
- 데이터 부실: 만료 전 업데이트 시 문제
- 복잡성:
Vary
,Key
관리 어려움 - 보안 리스크: 민감 데이터 캐시 오염
고급 기법
Vary 헤더
- 다중 버전 관리: 언어, 압축 방식별 캐시
|
|
Service Worker 캐싱
오프라인 지원:
Cache API
를 통한 프로그램적 제어
모니터링 및 최적화
도구
- Chrome DevTools: Network 탭에서
Size
컬럼 확인 - WebPageTest: 캐시 적중률 분석
- Lighthouse: 캐시 정책 진단
최적화 체크리스트
- 정적 자원은
immutable
사용 - 동적 컨텐츠는
no-cache
+ 검증 헤더 - CDN에
s-maxage
설정 ETag
대신Last-Modified
사용 자제 (정밀도 낮음)
자주 발생하는 문제와 해결
문제 | 원인 | 해결 방법 |
---|---|---|
캐시가 안됨 | 잘못된 캐시 설정 | Cache-Control 헤더 확인 |
오래된 컨텐츠 제공 | 캐시 만료 설정 부적절 | 적절한 max-age 설정 |
용량 문제 | 과도한 캐시 저장 | 캐시 정책 최적화 |
HTTP 캐싱을 구현할 때 주의해야 할 주요 사항
- 과도한 캐싱 주의:
성능 향상을 위해 과도하게 공격적인 캐싱을 적용하면 오래된 데이터를 제공하거나 민감한 정보를 노출할 수 있다. max-age 지시문을 신중하게 사용하고, 중요한 리소스에는 must-revalidate를 적용해야 한다. - 일관된 캐시 정책 유지:
애플리케이션 전체에 걸쳐 일관된 캐시 정책을 적용해야 한다. 미들웨어를 사용하여 전역 캐시 정책을 설정하고, 정기적으로 캐시 설정을 감사하는 것이 좋다. - 동적 콘텐츠와 정적 콘텐츠 구분:
정적 리소스에는 공격적인 캐싱 전략을 사용할 수 있지만, 동적 콘텐츠에는 짧은 TTL 값과 no-store 같은 지시문을 사용하여 개인화된 데이터의 관련성을 유지해야 한다. - 보안 고려:
민감한 정보가 포함된 페이지에는 no-store 지시문을 사용하여 브라우저나 중간 캐시에 저장되지 않도록 해야 한다. - 캐시 무효화 전략:
콘텐츠가 변경될 때 캐시를 적절히 무효화하는 전략을 구현해야 한다. ETag와 Last-Modified 헤더를 활용하여 효율적인 재검증을 수행할 수 있다. - CDN과의 통합:
CDN을 사용할 때는 s-maxage 지시문을 활용하여 CDN 특정 캐싱 동작을 제어해야 한다. - 클라이언트와 서버 측 캐싱 균형:
클라이언트 측(브라우저) 캐싱과 서버 측 캐싱을 적절히 조합하여 최적의 성능을 달성해야 한다.