06. 캐시
캐시의 등장 배경
2020년 인터넷 쇼핑의 가장 핫한 토픽은 바로 새벽 배송, 0.5일 배송입니다. 이를 가능케 하는 여러 요인들이 있겠지만 유통 방식의 혁명도 많은 영향을 끼쳤습니다. 정말 옛날의 유통방식부터 살펴봅시다! 과거의 유통 방식은 발송과 수신하는 사람이 1:1로 물건을 주고 받는 방식인 '포인트 투 포인트' 방식을 주로 사용했습니다. 그렇다 보니 인기있는 상품의 판매자들은 엄청난 구매 요청을 감당하기가 힘들었습니다(1명이 N명의 요청을 처리).
엄청나게 많은 운송업자들이 필요한건 덤이었고요. 효율성이 굉장히 떨어졌습니다.
이 문제점을 해결하기 위해 새로운 방식을 채택하게 됩니다. 바로 1:1로 위치상 중간지점에 고객들의 수요를 충분히 커버할만한 큰 창고(허브)를 만들어 재고를 꽉꽉 채운 뒤 주변지역으로 물건을 배송(스포크)하는 방식인데요. 이를 허브 앤 스포크라고 부릅니다. 그리고 현대에 들어서는 고객들의 소비경향을 분석해 재고의 양과 보관할 위치를 결정하는 인공지능(데이터 기반 의사결정)을 통해 새벽 배송, 0.5일 배송을 가능하게 만들었죠.
컴퓨터라고 다를게 없습니다!
단어만 조금 바꿔봅시다!
구매자 -> 클라이언트, 판매자 -> 서버
판매하는 물건 -> 데이터
운송업자 -> 커넥션과 자원
옛날 옛적의 네트워크... - 포인트 투 포인트
고전의 방식으로는 하나의 서버(판매자)가 수많은 클라이언트(고객)의 요청을 처리해야 했습니다. 한 번에 많은 요청이 오기 때문에서버가 다운되는 것도 심심치 않았죠. (자원소모가 높다)
캐시의 등장! - 허브 앤 스포크
그래서 컴퓨터에도 허브 앤 스포크 방식을 차용하기로 했습니다!
여기서 큰 창고(허브)의 역할이 바로 오늘 배울 캐시(Cache)입니다!
이렇게 원 서버의 데이터 사본을 보관해두는 캐시(허브)가 등장하면서 서버의 부하도 분산되고, 갑작스러운 트래픽 증가에도 여유가 있는 캐시에 요청을 하기 때문에 대기시간도 현저히 줄어들게 되었습니다!
추가적으로 캐시의 성능이 점점 높아져가며, 클라이언트들의 요청 경향을 딥러닝해 처리 속도를 높이고 있습니다! (새벽배송).
이러한 장점을 정리하면 다음과 같습니다.
1. 네트워크 병목현상 해결
- 서버 접근 시 속도는 네트워크 중 가장 느린 속도..
- 캐시에 빠른 네트워크를 탑재해 빠르게 데이터를 전달하자!
2. 데이터 사본을 통해 중복 요청에 대한 빠른 처리 및 비용 절감
3. 트래픽 급증에 대한 처리
- 여러 캐시를 배치함으로써 갑작스러운 트래픽 급증에 대한 대응력을 높일 수 있다.
캐시 종류
1. 개인 전용 캐시
개인에게만 할당된 캐시를 개인 전용 캐시라 합니다.
대부분 개인용 컴퓨터에 사본을 저장(캐싱)하며, 개인이 캐시의 사이즈와 설정을 수정할 수 있습니다.
브라우저에서 임시 파일이 바로 캐시 개인 전용 캐시입니다!
2. 공용 캐시
여러 사용자에게 할당된 캐시를 공용 캐시라 합니다.
사용자가 많다 보니, 개인 전용 캐시보다는 커야 하며, 사용자들의 공통된 요구를 캐싱하기 때문에,
불필요한 트래픽을 줄일 수 있는 장점이 있습니다.
Plus. 캐시의 계층화
원 서버와 직접 통신하는 것은 최대한 피하는 것이 좋습니다. (대역폭이나 서버 부하 등..) 하지만 캐시에 사본이 없는 이상은, 필연적으로 원 서버와 트랜젝션을 수행할 수밖에 없습니다. 이 상황을 막기 위해 원 서버의 사본을 가지고 있는 캐시들이 여러 곂의 저지선을 이루고 있습니다. 이를 캐시의 계층화라고 합니다.
그림에서 보이다시피, 자식 캐시에서 부적중이 발생했을 때, 더 큰 부모 캐시가 걸러진 트래픽을 처리하는 방식입니다.
- 개인 전용 캐시 적중 실패 -> 레벨 1 캐시
- 레벨 1 캐시 적중 실패 -> 레벨 2 캐시
- 계속 캐시에서 검색
- 캐시 적중 실패 -> 원 서버
Plus2. 캐시 망, 콘텐츠 라우팅, 피어링
캐시 망은 여러 캐시들과 여러 서버들이 복잡하게 얽혀 있습니다. 그래서 어느 부모 캐시를 거칠지 결정을 내려줄 필요가 있는데, 바로 콘텐츠 라우팅이 이 역할을 수행합니다.
콘텐츠 라우팅은 캐시 망에 있는 서버, 부모 캐시, 로컬 캐시 모두 탐색이 가능합니다.
하나의 서버를 같이 사용하는 조직들 중 필요하다면 다른 조직의 캐시도 접근이 가능합니다. (이를 피어링이라 합니다.)
캐시 문서 검사
사용자들을 위해 서버와 캐시는 서로 교차로 문서를 검사합니다. 이번에는 검사 방식에 대해 알아봅시다!
1. 캐시 문서 정확도(캐시 적중률)
캐시(창고)가 무한한 크기를 가진다면 얼마나 좋을까요? 안타깝게도 캐시의 크기는 한정되어 있습니다.
그렇기 때문에 이 한정된 공간에 클라이언트들이 가장 찾는 데이터만 사본화 해두어 최대한의 효율을 내야 합니다!
만약 클라이언트가 찾는 문서가 캐시에 들어 있다면, 이를 캐시 적중이라 하며, 캐시는 이 적중률을 높이기 위해 여러 노력을 기울여야 합니다!
2. 재검사
클라이언트가 원하는 정보가 캐시에 있다고 생각해봅시다. 그런데 막상 이 데이터를 꺼냈는데 최신화가 안된 데이터라면 그걸 모르고 사용한 클라이언트는 더욱 난감할 것입니다. 이를 위해서 캐시는 데이터의 최신화가 되었는지를 판별하는 HTTP 재검사를 실시합니다. 하지만, 재검사를 위해서 사본 데이터 전체를 보내는 것은 여러모로 시간이 오래 걸릴 것입니다. 비효율적이기도 하고요.
How?
그래서 캐시가 서버에서 조건부 Get 요청에 조건부 요청 헤더 'If-Modified-Since'나, 'If-None-Match'를 추가해 전송합니다. 조건부 요청 헤더가 참이 될 시 Get 요청이 정상적으로 처리되는 방식입니다!
(당연히 거짓이면 Get 요청은 이루어지지 않습니다.)
이렇게 되면 사본 전체가 아닌 일부의 데이터 값만 비교해서 재검사를 할 수 있습니다!
1) If-Modified-Since <date> 헤더 (IMS 검사)
캐시가 가진 [사본이 마지막으로 수정된 날짜(date)]와 원 서버가 가진 [원본의 마지막으로 수정된 날짜]를 비교해
다르면 요청 메처 드를 처리합니다.
- 참 (=수정 O) : '200 OK' + 새 문서 + 새 만료기간 연장 + 다른 필요한 데이터 전송
- 거짓(=수정 X) : '304 Not Modified' + 새 만료기간 연장 + 다른 갱신이 필요한 것들을 전송
2) If-None-Match <Tag> 헤더
날짜(date) 대신 태그(Tag)라는 일련번호를 데이터에 부여해 사용하는 방식입니다.
캐시 된 문서의 태그가 서버 문서의 태그와 다를 때 요청 메서드를 처리합니다. (동작 방식은 IMS 검사와 같습니다!)
IMS 검사는 특히 이런 경우 많이 사용합니다
1. 백그라운드 프로세스에 의해 내용 변화 없이 날짜만 최신화되는 경우
2. 캐시들이 체크하기엔 너무 사소한 경우
3. 서버가 최근 변경 일지를 정확히 판별할 능력이 없는 경우
4. 실시간 동기화처럼 1초보다 작은 간격으로 갱신되는 경우.
3. 캐시 신선도 검사
음식에는 유통기한이라는 것이 있습니다. 사본에도 신뢰성을 추가하기 위해서 유통기간을 부여합니다. 이를 캐시 신선도라고 합니다! 이는 사본에 Cache-Control, Expires 헤더를 이용하면 사본의 유효기간을 부여할 수 있습니다.
Cache-Control의 Max-age
생성된 시기부터 시작해 경과한 시간 (초단위)을 설정합니다. 이 시간이 지나면, 사본은 신선하지 않은 것으로 판단합니다.
예시 : Max-age : 100 => 100초 뒤 이 사본은 신선하지 않다. (=만료되었다)
Expires
절대 유효기간입니다. 이 시각이 지나면, 사본은 신선하지 않은 것으로 판단합니다.
예시 : Fri, Jul 2021, 05:00:00 GMT => 2021.07.05 금요일 5시에 이 사본은 만료됩니다.
이 캐시 신선도 검사를 통과했다면, 해당 콘텐츠를 반환합니다.
만약 통과하지 못했다면, 재검사 후 신선도를 갱신하거나, 서버에서 새로운 사본을 가져옵니다.
캐시 처리 단계
그럼 이제 전체적인 캐시 처리 단계를 살펴봅시다!
1st. 요청받기
네트워크로부터 도착한 요청 메시지를 받습니다.
2nd. 파싱
메시지에서 'URL'과 '헤더'를 추출해 조작하기 쉬운 자료구조에 담는다.
3rd. 캐시 검사
요청된 콘텐츠가 캐시 되어있는지 확인하는 작업입니다.
1) 로컬 캐시에 존재하는가? 있으면 콘텐츠 반환, 없으면 '2)'로
2) 부모 캐시나 원 서버에서 검색해 가져온다.
4th. 신선도 검사
콘텐츠의 신선도를 검사하는 작업입니다.
5th. 응답 생성
원서버에서 온 것처럼 하기 위해서, 서버의 응답 헤더를 토대로 응답 메시지를 생성합니다.
여기서 추가적으로 신선도를 Via헤더에 추가해, 후에 사용자가 확인 시 프록시를 거쳤음을 표기해야 합니다.
6th. 전송 및 로깅
클라이언트에게 응답 메시지를 전송하고, 해당 트랜젝션을 로그에 남깁니다.
캐시 제어
마지막으로 서버가 캐시를 제어하는 몇 가지 헤더에 대해서 알아봅시다
1) No-Store
캐시는 응답 메시지에 담김 콘텐츠를 사본화 합니다. 하지만 이 헤더는 사본화를 금지하고, 전송하는 즉시 삭제하도록 명령하는 헤더입니다.
2) No-Cache
'No-Store'와 다르게 사본화는 허용합니다. 하지만 재검사 전까지 클라이언트에게 제공하는 것을 금지합니다.
3) Must-Revalidate
No-Cache와 비슷하게 재검사 없이는 클라이언트에게 제공하지 못하도록 하는 엄격한 헤더입니다.
성능 개선을 위해 몇몇 만료된 객체는 제공 가능하도록 명령할 수 있습니다.
4) Max-age
만료 경과시간을 설정할 수 있습니다.
추가적으로 's-Max-age'를 사용하면 공용 캐시에 모두 적용이 가능하고, 속성 값에 0을 부여하면, 접근할 때마다 캐싱과 리프레시를 막을 수 있습니다.
5) Expires
실제 만료 시각을 설정할 수 있습니다.
'Max-age'와 혼동하여 속성 값에 0을 부여하지 맙시다. 이는 문법 오류를 일으킵니다!