[Web] HTTP 캐싱(Caching), 웹 캐시(Web Cache)란 무엇인가?

 HTTP 캐싱(Caching)이란? 

HTTP 캐싱(Caching)은 한번 가지고 온 첫 요청 시 리소스(이미지, 스타일 시트, 자바스크립트 파일 등)를 미리 저장해 뒀다가 다음번 동일한 HTTP 요청에서 서버에서 리소스를 새로 가지고 오는 것이 아닌 미리 저장해 뒀던 리소스를 활용하는 기법을 말합니다. 이 기법을 통해 반복적으로 요청되는 리소스의 부하를 줄여서 웹 로딩속도 향상과 서버의 과부하를 줄일 수 있습니다.

 

이 캐싱이라는 용어는 웹에서만 사용되는 용어는 아닙니다. 자원을 미리 저장해 놓고 활용하는 방식은 효율성이 중요한 어떤 곳에서 든 가장 먼저 생각해 볼 수 있는 방식입니다. CPU의 내부에도 캐시메모리라는 방식이 존재하고, 어떻게 보면 일상생활에서 자주 사용되는 포스트잇도 캐싱이라고 할 수 있습니다. 포스트잇에 수시로 필요한 정보를 메모해 두고 필요할 때마다 확인함으로써, 매번 같은 정보를 다시 찾거나 기억하지 않아도 되어 효율적으로 업무를 수행할 수 있으니까요.

 

 

 HTTP 캐시의 종류 

HTTP 캐시는 크게 로컬 캐시와 공유 캐시로 구분할 수 있습니다. 공유 캐시는 응답을 여러 사용자가 공유하는 캐시이고, 로컬 캐시는 오직 한 사용자에게만 할당되는 캐시입니다.

 

  • 로컬 캐시 : 대표적으로 브라우저 캐시를 말합니다. 이미지, 스타일 시트, 스크립트 등의 리소스를 브라우저에 캐싱하여 다음에 같은 리소스를 요청할 때 서버에 다시 요청하지 않고 로컬에서 빠르게 가져올 수 있습니다.
  • 공유 캐시 : 대표적으로 프록시 캐시를 말합니다. 중간에 위치한 프록시 서버에 리소스를 두고 자주 요청되는 리소스를 프록시 서버에서 캐싱하여 동일한 리소스에 대한 여러 클라이언트의 요청에 대응합니다.

 

로컬 캐시(브라우저 캐시)

 

  1. 브라우저가 서버에 리소스를 처음 요청합니다.
  2. 서버에서 클라이언트로 요청된 리소스를 보냅니다.
  3. 클라이언트는 받은 리소스를 로컬에 저장합니다.
  4. 이후 동일한 리소스에 대한 요청이 있을 때는 서버에 다시 요청하지 않고 로컬 캐시에서 리소스를 불러옵니다.

 

공유 캐시(프록시 캐시)

  1. 브라우저가 서버에 리소스를 요청합니다.
  2. 프록시 서버가 클라이언트의 요청을 받아 서버에 전달합니다.
  3. 서버는 리소스를 프록시 서버로 보냅니다.
  4. 프록시 서버는 받은 리소스를 캐시에 저장하고, 동일한 리소스에 대한 다른 클라이언트의 요청이 있을 때 서버에 다시 요청하지 않고 캐시에서 리소스를 제공합니다.

 


 

 HTTP 캐싱 사용 방법 

HTTP 캐시를 활용하기 위해서는 서버의 HTTP 응답 헤더에 캐싱과 관련된 속성을 보내서 브라우저에게 어떻게 캐싱을 할 것인지 전달합니다. 주요한 헤더로는 Cache-Control과 Expires가 있고 추가적으로 캐시 유효성 검사를 위해 Last-Modified와 ETag도 함께 사용할 수 있습니다.

Cache-Control: public, max-age=86400
Expires: Thu, 22 Feb 2023 10:00:00 GMT
Last-Modified: MON, 1 Jan 2024 00:00:00 GMT
ETag: "abc123"

 

Cache-Control

캐싱 동작을 제어하는 데 사용됩니다.

  • Cache-Control: no-store : 리소스를 캐시 하지 않음
  • Cache-Control: max-age=3600 : 캐시 유효기간을 설정
  • Cache-Control: private : 프록시 캐싱 비활성화

 

Expires

리소스의 만료 시간을 설정하는 데 사용됩니다. 이 옵션값을 사용하면 캐시의 만료일자를 딱 지정할 수 있습니다. 현재는 이 옵션값보다는 Cache-Control의 max-age를 더 많이 사용합니다. 초 단위로 관리하는것이 캐시를 더 유연하게 관리할 수 있기 때문입니다. 추가로 Cache-Control의 max-age와 Expires가 함께 사용될때는 Expires 설정값이 무시됩니다.

  • Expires: Wed, 30 Jan 2024 00:00:00 GMT :  2024년 1월 30일 자정까지 캐시 유효

 

Last-Modified 

리소스의 마지막 수정 날짜를 나타냅니다. 클라이언트는 이 날짜를 캐시 데이터의 날짜와 비교하여 변경된 리소스인지를 확인합니다.

  • Last-Modified: MON, 1 Jan 2024 00:00:00 GMT :  2024년 1월 1일 자정에 리소스가 변경됨

 

ETag

리소스의 상태를 나타내는 엔티티 태그(Entity Tag)로 클라이언트에서 이 문자값을 확인하여 변경내역을 확인합니다.

  • ETag: "abc123"  리소스 고유 문자열 : "abc123"

 

 


 

 HTTP 캐싱 동작 

첫 번째 요청

 

클라이언트에서 star.jpg를 서버에 요청한다고 가정하면 서버는 요청 내용을 보고 실제 이미지인 star.jpg와 HTTP 응답을 내립니다. 이때 브라우저가 캐시를 적용해야 하니 캐시가 유효한 시간을 HTTP 헤더의 cache-control에 설정해서 응답을 보냅니다. 위의 예제처럼 cache-control: max-age=60으로 보내면 브라우저는 60초 동안 캐시에 저장하게 되며 이후 요청에서는 리소스를 받기 전에 캐시를 먼저 확인합니다.

 

 

캐시 유효시간이 경과하지 않은 경우

 

만약 캐시 만료시간인 60초가 경과하지 않고 동일한 요청을 서버에 보냈다고 가정해 보겠습니다. 이때는 HTTP 요청 시에 캐시를 먼저 조회하여 max-age를 확인하여 캐시가 아직 유효한지 확인합니다. 아직 60초가 경과하지 않았으니 서버에 리소스를 요청하지 않고 캐시에서 리소스를 가져옵니다.

 

 

캐시 유효시간이 경과한 경우

 

 

만약 max-age를 확인했는데 캐시 유효시간이 지났다면 서버를 통해 데이터를 다시 조회하고 캐시를 갱신해 줍니다. 하지만 이 메커니즘을 사용한다면  서버의 리소스는 변경되지 않아 캐시와 서버의 리소스가 동일하더라도 유효시간이 지나면 매번 리소스를 받아야 하는 비효율성이 발생합니다. 이를 해결하기 위해서 캐시 유효성 검사 헤더 속성인 Last-Modified와 ETag를 사용할 수 있습니다.

 

 

리소스의 마지막 수정일자 - 검증 헤더(Last-Modified) 추가

 

생각해 보면 서버의 리소스와 캐시에 있는 리소스가 같다는 사실만 알 수 있으면 무거운 리소스를 매번 갱신해줘야 할 이유는 없습니다. 첫 요청 시 서버에서 Last-Modified라는 헤더에 리소스의 최종 변경일을 설정하면 캐시의 유효기간이 지난 뒤 클라이언트에서 서버로 리소스를 요청할 때 if-modified-since 헤더에 last-modified값 보내고 서버는 이 값을 서버에 있는 리소스의 데이터 최종 수정일을 비교하여 변경사항이 있는지 확인합니다. 변경사항이 있으면 200과 함께 데이터를 전송하고, 변경되지 않았다면 304 Not Modified를 응답하여 불필요한 낭비를 막을 수 있습니다.

 

 

 

Last-Modified 대신 ETage(Entity Tag)를 사용할 수도 있습니다. 이 경우에는 서버의 리소스에 임의의 엔티티 태그를 쌍으로 달아놓습니다. 그리고 리소스가 변경되면 이 엔티티 태그를 갱신하는 것이죠. 이 엔티티 태그는 클라이언트의 첫 요청 시 ETag라는 헤더에 값으로 클라이언트에 내려줍니다. 이후 캐시가 만료되었을 때 클라이언트가 서버에 if-None-Match 헤더에 ETag값을 보내고, 서버는 이 값을 리소스의 엔티티 태그와 비교하여 변경사항이 있는지 확인합니다. 변경사항이 있으면 200과 함께 데이터를 전송하고, 변경되지 않았다면 304 Not Modified를 응답하여 불필요한 낭비를 막을 수 있습니다.

 

 

댓글

Designed by JB FACTORY