우아한 테크코스

캐시란?

캐시란?

캐시란 자주 사용하는 데이터나 값을 미리 복사해놓는 임시 장소를 가리킨다. 저장 공간이 작고 비싼 대신, 빠른 성능을 제공한다. 즉, 반복적으로 사용되는 데이터를 불러올 때, 매번 DBMS나 서버에 요청하는 것이 아니라 메모리에 저장하였다가 쓰는 것을 의미한다. 

 

캐시 적용 / 미적용 시나리오

캐시가 없을 때

  • 데이터가 변경되지 않아도 계속 네트워크를 통해서 데이터를 다운로드하여야 한다.
  • 인터넷 네트워크는 매우 느리고 비싸다.
  • 브라우저 로딩 속도가 느리다.
  • 느린 사용자 경험

캐시 적용

  • 캐시덕분에 캐시 가능 시간 동안 네트워크를 사용하지 않아도 된다.
  • 비싼 네트워크 사용량을 줄인다.
  • 브라우저 로딩 속도를 빠르게 할 수 있다.
  • 빠른 사용자 경험

캐시 시간 초과

  • 캐시 유효 시간이 초과 → 서버를 통해 데이터 다시 조회 및 캐시 갱신
  • 네트워크 다운로드가 이때 다시 진행됨.
    • 데이터가 동일하다면? → 굳이 다운로드할 필요가 X

검증 헤더와 조건부 요청

검증헤더

: 캐시 데이터와 서버 데이터가 같은지 검증하는 데이터

  • Last-Modified, ETag

조건부 요청 헤더

: 검증 헤더로 조건에 따른 분기

  • If-Modified-Since: Last-Modified 사용
  • If-None-Match: Etag 사용
  • 조건이 만족 → 200 OK
  • 만족 X → 304 Not Modified

Last-Modified 사용

If-Modified-Since 이후에 데이터가 수정되었는지 확인

캐시 유효시간이 초과해도 데이터가 갱신되지 않으면 (캐시 = 서버)

→ 304 Not Modified + 헤더 메타 정보만 응답 (Body)

= 네트워크 부하가 줄어들게 됨. (실용적인 해결책!)

단점

  • 1초 미만 단위로 캐시 조정 불가능
  • 날짜 기반의 로직 사용
  • 데이터를 수정해서 날짜가 다르지만, 같은 데이터를 수정해서 데이터 결과가 똑같은 경우 (A → B → A 일 경우, 콘텐츠는 변경된 건 아님.)
  • 서버에서 별도의 캐시 로직을 관리하고 싶은 경우 (스페이스 등을 무시)

ETag 사용

  • 캐시용 데이터에 임의의 고유한 이름 달아둠
    • ex) Etag: “v1.0”, Etag: “ag123123”
  • 데이터가 변경 → 이름을 변경함
  • ETag를 보내서 같으면 유지, 다르면 다시 받음.
  • 캐시 제어 로직을 서버에서 완전히 관리

→ 클라이언트는 단순히 이 값을 서버에 제공하게 됨.

 

적용하기

RFC(Request for Comments) 표준 방식에 의해 캐시 validation 방식이 변경됨

기존 : etag, last-modified 둘 다 비교

변경: etag만 비교 (ShallowEtagFIlter를 사용할 수도 있음) 

 

캐시와 조건부 요청 헤더

캐시 제어 헤더

  • Cache-Control
    • max-age : 캐시 유효 시간, 초단위 (Expires보다 더 유연함)
    • no-cache : 데이터는 캐시 해도 되지만, 항상 원(origin) 서버에 검증하고 사용
    • no-store : 데이터에 민감한 정보가 있으므로 저장 X
  • Pragrma (하위 호환)
  • Expires - 캐시 만료일 지정 (하위 호환)

휴리스틱 캐시

  • 휴리스틱 캐시란?
    • Cache-Control이 지정되지 않더라도, 기본적으로 cache는 최대한 사용해 성능을 높이는 것이 추천되기 때문에 기본적으로 적용되는 캐시 정책.
    • 기본적으로 1년 동안 업데이트되지 않은 컨텐츠는 잘 업데이트 되지 않는 것으로 알려져 있다. 따라서, 클라이언트는 max-age가 설정되지 않았더라도 어느 정도 사용되게 된다.
    • 재사용 기간은 구현에 따라 다르지만, 기본적으로 0.1년 정도를 권장한다.
  • 만약 응답이 Cache-control : max-age 헤더나 expires 헤더들을 포함하지 않고 있다면 캐시는 경험적인 방법으로 heuristic 최대 나이를 게 산할 것이다.

검증헤더와 조건부 요청 헤더

검증 헤더 → Etag, LastModified

조건부 요청 헤더

  • Etag → If-Match, If-None-Match
  • Last-Modified → If-Modified-Since, If-Unmodified-Since

프록시 캐시

클라이언트들과 오리진 서버의 거리가 너무 멀리 떨어져 있어서 응답 시간이 많이 소요되는 경우 고려되는 기술로, 클라이언트들과 원 서버 사이에 프록시 캐시 서버를 둠으로써 응답시간을 줄이는 방식이다.

클라이언트로부터 요청되는 자원에 대해 프록시 캐시서버에서 캐시를 저장하여 제공함으로써 여러 사람이 찾는 자료일수록 이미 캐시에 등록되어 있기에 빠른속도로 자원을 받을 수 있다.

클라이언트에서 사용되는 캐시는 해당 클라이언트에서만 사용되기에 private 캐시라 부르고 프록시 캐시 서버에서 사용되는 캐시는 여러 클라이언트들에게 노출되야 하기에 public 캐시라 부른다.

  • Cache-Control: public : 응답이 public 캐시에 저장되어도 됨
  • Cache-Control: private : 응답이 해당 사용자만을 위한 것임, private 캐시에 저장해야 함(기본값)
  • Cache-Control: s-maxage : 프록시 캐시에만 적용되는 max-age
  • Age: 60 (HTTP 헤더) : 오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간(초)

캐시 무효화

Cache-Control: no-cache, no-store, must-revalidate

Pragma: no-cache

통장 잔고와 같이 캐시 활용이 돼서는 안 되는 서비스의 경우 캐시를 확실하게 무효화해야 하는데, 하위 호환까지 고려해서 위와 같이 작성을 하면 된다.

  • Cache-Control: no-cache
    • 데이터는 캐시 해도 되지만, 항상 원 서버에 검증하고 사용(이름에 주의!)
  • Cache-Control: no-store
    • 데이터에 민감한 정보가 있으므로 저장하면 안 됨(메모리에서 사용하고 최대한 빨리 삭제)
    • 다만, 해당 내용보다 no-cache가 조금 더 확실하다.
  • Cache-Control: must-revalidate
    • 캐시 만료 후 최초 조회 시 원 서버에 검증해야 함
    • 원 서버 접근 실패 시 반드시 오류가 발생해야 함 - 504(Gateway Timeout)
    • must-revalidate는 캐시 유효 시간이라면 캐시를 사용함
  • Pragma: no-cache
    • HTTP 1.0 하위 호환

HTTP Compression 설정하기

Enable HTTP Response Compression를 참고

  • HTTP 응답을 압축하여 웹 사이트 성능 향상