캐싱으로 Next.js 앱 속도 높이는 방법

발행: (2025년 12월 21일 오후 11:00 GMT+9)
9 min read
원문: Dev.to

Source: Dev.to

위에 제공된 소스 링크만 포함되어 있어 번역할 본문이 없습니다. 번역을 원하는 전체 텍스트를 알려주시면 한국어로 번역해 드리겠습니다.

캐싱이란?

Caching은 기본적으로 이미 가져오거나 계산한 데이터를 기억하는 것을 의미하며, 따라서 같은 작업을 다시 할 필요가 없습니다.

  • 사용자가 페이지를 방문할 때마다 API를 호출하거나 데이터베이스에 접근하는 대신, 앱이 해당 데이터를 일시적으로 저장하고 업데이트가 필요할 때까지 재사용할 수 있습니다.
  • 이것을 뇌가 집 주소를 기억하는 것에 비유할 수 있습니다. 양식을 작성할 때마다 주소를 찾아볼 필요가 없죠. 같은 개념이 앱에도 적용됩니다.

캐싱은 어디에서 발생할 수 있나요?

ReactNext.js에서는 캐싱이 다양한 레벨에서 발생할 수 있습니다:

계층캐시할 수 있는 항목
클라이언트 측API 응답, 계산된 값, 컴포넌트 출력
서버 측SSR/SSG 중에 가져온 데이터, 라우트 수준 데이터
네트워크 / CDN전체 페이지, 정적 자산, 엣지 캐시된 API 라우트

각 계층을 현명하게 활용하면 앱을 더 빠르게 만들 수 있는 기회를 제공합니다.

왜 캐시가 필요할까?

  • 사용자에게 페이지 로드 속도를 높여줍니다.
  • 서버 비용 및 API 사용량을 줄여줍니다.
  • 앱이 더 부드럽고 반응성이 좋아집니다.
  • 페이지 로드 시간을 단축해 SEO와 Core Web Vitals를 개선합니다.

Next.js 캐싱 기능

1. fetch()와 내장 캐싱 제어

// Example: cache for 1 hour
const data = await fetch('https://api.example.com/posts', {
  next: { revalidate: 3600 }   // seconds
});
  • revalidate는 지정된 초가 지난 후 Next.js가 데이터를 다시 가져오도록 합니다.
  • 자주 변경되지 않는 데이터(블로그 포스트, 제품 목록, 정적 콘텐츠)에 적합합니다.

항상 최신 데이터를 받아야 할 경우:

const data = await fetch('/api/always-fresh', {
  cache: 'no-store'   // never cache
});

2. 캐시 컴포넌트 ('use cache' 지시문)

// Sidebar component – cached
async function Sidebar() {
  'use cache';
  const categories = await fetch('/api/categories');
  return (
    <>
      {/* Sidebar content here */}
    </>
  );
}
  • 서버 컴포넌트 내부에 'use cache'를 사용하면 UI 그 부분만 캐시됩니다.
  • 사이드바, 네비게이션 등 거의 변하지 않는 UI에 좋습니다.

3. 라우트 / 페이지 세그먼트 캐싱 (SSG & ISR)

// pages/blog/[slug].tsx
export const revalidate = 3600; // 1 hour
  • Next.js는 빌드 시 페이지를 사전 렌더링하고, 지정된 간격이 지나면 백그라운드에서 재검증합니다.
  • 블로그 글, 제품 페이지 등 정적 속도와 동적 최신성을 동시에 원하는 경우에 이상적입니다.

React‑Side 캐싱 기법

1. 메모이제이션 (useMemo & useCallback)

import { useMemo } from 'react';

const sortedList = useMemo(() => {
  return data.sort((a, b) => a.name.localeCompare(b.name));
}, [data]);   // recompute only when `data` changes
  • 비싼 계산(예: 정렬)이 매 렌더링마다 실행되는 것을 방지합니다.

2. 내장 캐시가 있는 데이터 가져오기 라이브러리

SWR 예시

import useSWR from 'swr';

const fetcher = (url: string) => fetch(url).then(res => res.json());

export default function Profile() {
  const { data, error } = useSWR('/api/user', fetcher, {
    refreshInterval: 60000 // revalidate every minute
  });

  if (error) return <div>Error loading</div>;
  if (!data)   return <div>Loading...</div>;

  return <div>Hello {data.name}</div>;
}
  • SWR은 응답을 캐시하고, 이후 렌더링에서는 즉시 반환하며, 백그라운드에서 재검증합니다.
  • React Query도 동일하게 동작하므로, 스타일에 맞는 것을 선택하면 됩니다.

간단한 캐싱 체크리스트

  1. Identify 자주 변경되지 않는 데이터를 식별합니다.
  2. user‑specific 또는 time‑sensitive 데이터를 동적으로 유지합니다.
  3. Cache only where it improves performance.
  4. Set a revalidation time 또는 모든 캐시 항목에 대한 만료 시간을 설정합니다.
  5. Test & monitor 결과를 테스트하고 모니터링합니다 (예: Lighthouse, 서버 로그).

캐싱은 모두를 캐시하는 것이 아니라, 올바른 것을 캐시하는 것입니다.

일반적인 캐싱 실수

실수왜 문제가 되는가
과도한 캐싱 – 만료 시간을 설정하지 않음오래된 데이터를 제공함
사용자 데이터를 전역으로 캐싱세션이 뒤섞이고 개인 정보가 유출됨
무효화(Invalidation)를 잊음업데이트가 절대 표시되지 않음
디버깅 혼란 – 데이터가 캐시되어 있다고 가정하고 최신이라고 생각함“누락된” 업데이트를 찾는 데 시간 낭비

황금 규칙: 속도와 신선도의 균형.

실제 사례 균형 (내 예시)

  • Sidebar categories & featured posts'use cache' 로 캐시됨.
  • Post listfetch(..., { next: { revalidate: 600 } }) 로 가져옴 (10분마다 재검증).
  • User‑specific sections (comments, notifications) – 항상 동적(캐시 안 함).

이는 정확성을 손상시키지 않으면서 빠른 사이트를 제공합니다.

TL;DR

  • Caching = 데이터를 기억해서 작업을 다시 하지 않게 함.
  • Next.js의 fetch 옵션, Cache Components, ISR을 사용해 서버‑사이드 캐싱을 수행합니다.
  • React의 useMemo, useCallbackSWR 혹은 React Query 같은 라이브러리를 사용해 클라이언트‑사이드 캐싱을 수행합니다.
  • 정적인 것은 캐시하고, 동적인 것은 최신 상태로 유지하며, 항상 재검증 전략을 설정합니다.

캐싱은 앱을 즉각적으로 느끼게 하는 가장 쉬운 방법 중 하나입니다.

즐거운 코딩 되세요! 🚀

React 또는 Next.js 앱을 번개처럼 빠르게 만들기

전체 시스템을 재구축할 필요 없이 영향을 확인할 수 있습니다 — 작은 것부터 시작하세요. 자주 변경되지 않는 부분을 캐시하고, 테스트한 뒤 점차 확장해 나가세요.

Next.js가 빠르게 진화함에 따라 route caching, fetch‑level caching, cache components와 같은 기능이 이전보다 성능 최적화를 훨씬 간단하게 만들어 줍니다.

캐싱을 현명하게 활용하면 앱이 더 빨리 로드될 뿐만 아니라 적은 노력으로 더 많은 사용자를 처리할 수 있습니다.


읽어 주셔서 감사합니다. 이런 콘텐츠를 더 보고 싶다면, React, Next.js, 그리고 웹 성능에 대한 실용적인 인사이트를 공유하는 제 다른 블로그들을 확인해 보세요.

Back to Blog

관련 글

더 보기 »