#54.TIL | ETag를 이용하여 더 나은 Restful API 만들기

Seongjae Hwang·2023년 1월 15일
0

https://slender-danger-059.notion.site/ETag-Restful-API-6515d851db2241f2a9040b4dc113679d

Restful API

데이터의 생성, 삭제, 수정, 조회에 사용.

Restful API 비용의 문제

API를 통해 조회하는 데이터의 크기는 비용의 문제와 직결되어 자주 문제로 거론됨.

  • 데이터가 크면 클수록 네트워크 구간에서 비용 발생.
    • ex) 10Mb 메모리를 주고 받는다고 하면
      • request 구간에서 10Mb 메모리
      • response 구간에서 10Mb 메모리

하지만, 단순한 request, response 안에는 라우터, 스위치 등 복잡하게 네트워크가 구축되어 있음.

캐시

캐시란 데이터를 미리 복사해 놓는 일종의 임시저장소같은 것을 말하는데, 데이터에 접근하는 시간이 오래 걸리거나 값을 다시 계산하는 시간을 절약하고 싶은 경우 사용.

그래서 캐시라는 시스템 설계를 도입하여 비용을 줄이고, 사용자는 더 빠른 결과를 받아 볼 수 있음.

캐시 시스템은 클라이언트 내부적으로 구축해도 되고, 별도의 캐시 서버를 운영해도 되고 다양한 전략이 있을 수 있음.

HTTP ETag

ETag는 클라이언트가 이전에 요청했던 데이터와 최신 데이터의 변경사항 유무를 검증하는데 사용하는 HTTP 응답 헤더.

클라이언트에서 HTTP GET 요청을 할때 응답 헤더로 ETag값 반환.

ETag의 값은 일반적으로 MD5 등의 Hash함수를 이용하여 생성된 값인 Message Digest를 사용.

마켓컬리 파비콘 이미지 들고 오는 api

반환된 ETag의 값을 HTTP 요청 헤더 If-None-Match에 담아서 요청 데이터가 최신인지 아닌지를 검증. 만약, 데이터의 변경이 없어서 요청한 ETag의 값과 현재 데이터의 ETag의 값이 동일하면 HTTP 응답코드로 304 Not Modified를 반환 받게 됨.

HTTP ETag flowchart

  1. 브라우저는 서버에서 HTTP 요청 헤더에 If-None-Match을 실어 HTTP 요청.
  2. 서버의 파일에 대한 ETag가 브라우저의 If-None-Match 값과 동일한지 판단.
  3. 동일하면, HTTP 304 응답과 함께 Cache-Control 같이.
  4. 동일하지 않을 경우 전체 파일만 반환.

Cache-Control?

Cache-Control 흐름도

  • Cache-Control: no-cache. : 캐시된 URL 버전을 사용하기 전에 매번 서버에서 유효성을 다시 확인해야 한다고 브라우저에 지시
  • Cache-Control: no-store : 브라우저와 다른 중간 캐시(예: CDN)가 파일의 어떤 버전도 저장하지 않도록 지시
  • Cache-Control: max-age=31536000 : 초
  • Cache-Control: private : 브라우저는 파일을 캐시할 수 있지만 중간 캐시는 캐시할 수 없음.
  • Cache-Control: public : 응답은 모든 캐시에 의해 저장될 수 있음.

fast-api에서 ETag 적용하기

자바 스프링에서는 Web.Filter 패키지의 ShallowEtagHeaderFilter 클래스에서 Etag의 표준 기능을 제공하고 있다고 함.

fast-api 공식 문서에는 기본 제공하는 기능을 찾기 어려웠음.
(Custom Response의 FileResponse클래스를 통해 Content-LengthLast-ModifiedETag 에 관련한 내용이 있지만 이는 REST API가 아닌 정적인 파일인 경우 같음.)

**fastapi-etag**

fastapi-etag

Basic etag support for FastAPI, allowing you to benefit from conditional caching in web browsers and reverse-proxy caching layers.

This does not generate etags that are a hash of the response content, but instead lets you pass in a custom etag generating function per endpoint that is called before executing the route function.This lets you bypass expensive API calls when client includes a matching etag in the If-None-Match header, in this case your endpoint is never called, instead returning a 304 response telling the client nothing has changed.

from fastapi import FastAPI
from starlette.requests import Request
from fastapi_etag import Etag, add_exception_handler

app = FastAPI()
add_exception_handler(app)

async def get_hello_etag(request: Request):
    return etag값

@app.get("/hello/{name}", dependencies=[Depends(Etag(get_hello_etag))])
async def hello(name: str):
    return {"hello": name}

Front (If-None-Match)

request headers

HTTP 304

네트워크 비용

before

after

첫번째 api 응닶갑으로 데이터 20Mb가 옴. Etag를 사용하면 동일한 데이터를 한번 더 주고 받을 필요없이 157 Byte로 유효성 검증을 할 수 있었음.

20Mb(약 20,971,520 Byte) → 157 Byte

약 14,000배 정도의 네트워크 비용을 절감. 👍

Restful을 지키는 방법

이런 Etag를 사용하는 API는 REST 아키텍처의 Cacheable 요건을 만족하여 더욱 Restful API를 만들 수 있음.

여기까지는 서버와 클라이언트간 통신에서 캐시 구현.

브라우저가 아닌 서버와 서버간의 통신에서는 또 다른 다양한 방식으로 구현할 수 있음. 예를 들자면 메모리DB인 Redis..

profile
Always Awake

0개의 댓글