요청을 캐시해서 돈을 아껴보자 Thumbnail

Cache Control

8 min read

요청을 캐시해서 돈을 아껴보자

제목은 이렇게 썼지만 블로그를 운영하는데 돈은 안든다 ㅎ..

캐시에 대한 개념은 익히 들어서 알고 있는 개념이었지만 실제로 적용해본 적이 없다는 것을 깨달았다.

이번에 캐시를 적용해야겠다고 생각한 이유는 내 블로그에서 posts 페이지에서 series 페이지로 이동할 때마다 글 리스트를 불러오는 Get 요청을 매번 실행하고 있다는 점에서 착안했다.

React Query를 쓰면 요청과 응답에 대한 캐시를 편하게 사용할 수 있다는 점이 있지만 개인 블로그에서 React Query를 쓰기에는 오버 엔지니어링이라는 생각이 들어서 도입하지 않았다.

어떻게 캐시하나요?

가장 기본적인 방법으로 요청에 대한 응답을 캐싱하기로 결정했다. Nextjs 백엔드에서 보내주는 Response 객체에 header를 달아줘야한다.

요청의 헤더는 이렇게 작성했다. 아래는 블로그의 세부 내용을 불러오는 API의 코드다.

export async function GET(
  req: NextRequest,
  { params }: { params: { slug: string } }
) {
  try {
    await dbConnect();
    const post = await Post.findOne({ slug: params.slug }).lean();

    if (!post) {
      return Response.json(
        { success: false, error: 'Post not found' },
        { status: 404 }
      );
    }

    return Response.json(
      { success: true, post },
      {
        status: 200,
        headers: {
          'Cache-Control': 'public, max-age=3600, s-maxage=86400',
        },
      }
    );
  } catch (error) {
    console.error(error);
    return Response.json(
      { success: false, error: 'Server Error' },
      { status: 500 }
    );
  }
}

여기서 핵심은 header 부분이다. 'Cache-Control': 'public, max-age=3600, s-maxage=86400' 헤더의 Cache-Control 항목에 직관적으로 적혀있는 것을 볼 수 있다.

캐싱 시스템

public은 캐싱 시스템에 대한 항목이다. public과 private이 있는데 public은 모든 캐싱 계층에 저장된다는 의미이고, private은 브라우저 캐시 같은 사용자 개인 캐시 저장소에만 저장된다는 의미이다.

그렇다면 "모든 캐싱 계층"이란 무슨 말일까?

캐싱 계층은 여러가지가 존재한다. 가장 기본적으로는 브라우저 캐시가 있다. 우리가 사용하는 로컬 브라우저이고 로컬 캐시이고, 개인 사용자만 접근 가능하다.

그 다음으로는 프록시 캐시(ISP, 통신사 단에서 운영되는 캐시), CDN 캐시(Cloudflare 같은 Cdn 서비스에서 운영되는 캐시), 리버스 프록시 캐시(Nginx같이 서버 앞에 위치해서 요청을 처리하는 것을 도와주는 소프트웨어 단에서 운영되는 캐시이다.)

private 지시어는 개인적인 정보가 포함되어있는 경우에 사용해볼 수 있을 것 같다.

캐싱 만료

직관적인 의미로 캐시가 언제 만료될지에 대한 정보이다. 대표적으로 max-age, s-maxage가 있다. max-age= 형식으로 몇초 뒤에 만료될 것인지를 적어준다.

s-max-age는 브라우저 캐시가 아닌 공유 캐시에 저장되는 시간을 지정해주는 것이다. 위에서 잠깐 설명한 프록시 캐시, CDN 캐시에 저장되는 시간이다.

max-age=3600 이렇게 지정하면 3600초, 즉 1시간 동안 캐시에 저장된다는 의미이다.

캐싱 검증

must-revalidate이나 proxy-revalidate 같은 것들은 캐시가 만료되었을 시에 어떤 작업을 할 것인지에 대한 정책이다.

must-revalidate는 캐싱된 응답의 max-age가 아직 만료되지 않았을 경우에는 그냥 해당 응답을 사용하고 만약 만료되었을 경우에는 무조건 서버에 연결해서 재검증해야한다는 의미이다.

proxy-revalidate는 브라우저 같은 개인 캐시와는 상관없이 프록시 캐시에 저장된 응답이 만료되었을 경우 재검증 받아야한다는 의미이다.

어떻게 바뀌었나

캐시 정책 적용 전에는 항상 새로운 응답을 불러와서 사용한다. 적용 전적용 전

적용 후에는 응답 헤더에 아까 정한 Cache Control 헤더가 적혀있는 것을 볼 수 있다. 그리고 상태 코드에 보면 200 코드 옆에 (디스크 캐시에서)라고 적혀있다. 캐시에서 응답을 불러온 것이다!!!

적용 후적용 후

캐시를 적용하면 무엇이 좋아지나

장점

캐시를 적용하면 좋은 점이 많다.

  • 성능 향상, 이전에 받은 리소스를 재활용하기 때문에 페이지 로딩 속도가 빨라진다.
  • 네트워크 비용 효율성, 돈을 아낀다는 제목이 여기에서 나온다. vercel의 경우 한달 100GB 트래픽을 지원하는데 이미지를 매번 새로 가져온다면 그만큼 대역폭 소모량도 많아질 것이다. 캐시로 이걸 방지할 수 있겠다.
  • 오프라인 지원, 가끔 인터넷이 끊겨도 유튜브 영상이나 블로그 글을 볼 수 있는 것도 캐시 덕분이다.

단점

물론 장점만 있는 것은 아니다. 정책을 잘 지정하지 못한다면 문제가 생길 수도 있다.

  • 데이터 신선도, max-age 같은 옵션을 너무 오래 지정하게 되면 서버에서는 데이터가 이미 다른 데이터로 변경되었음에도 클라이언트에서는 이전 응답을 재활용하게 된다.
  • 보안, 개인 정보나 로그인해야만 얻을 수 있는 리소스에 대해서 캐시하게 되면 유출 가능성에 대해서도 고려해야한다.
  • 디버깅 어려움, 이건 개발자에게만 해당되는 일이지만 가끔 개발을 하다보면 예상치 못한 일들이 일어날 때 캐시 초기화를 먼저 해보라고 하는 글들을 흔하게 볼 수 있다. 캐시는 직관적으로 보기 어렵기 때문에 디버깅을 어렵게 할 수 있다는 점이 있다.

이런 단점들에 대해서 생각하면서 캐시 정책을 결정해야 장점만 이용할 수 있을 것 같다.

📌 Table of Contents

0
추천 글