Post

NextJS 데이터 페칭 & 캐싱

NextJS 캐싱 정신 모델과 request memoization, data/full route cache, ISR/SSR 선택, 무효화 원칙

NextJS 데이터 페칭 & 캐싱

기능이 아니라 “정신 모델”로 이해해야 장애를 피한다

NextJS App Router에서 가장 많은 장애가 발생하는 영역은
라우팅도, 서버 컴포넌트도 아니라 “캐싱”이다.

문제의 원인은 대부분 하나다.

캐시를 옵션으로 이해하고,
실제로는 기본 동작이라는 사실을 놓친다.

이 글은 API 설명을 하지 않는다.
대신 NextJS가 데이터를 어떻게 ‘기억’하려고 하는지를 하나의 정신 모델로 정리한다.


전체 정신 모델 한 줄 요약

NextJS는
“같은 요청, 같은 데이터, 같은 UI라면 다시 계산하지 않는다”
를 기본 전제로 설계된 프레임워크다.

이 전제에서 모든 캐싱 개념이 파생된다.


1. request memoization

잘못된 이해 → 올바른 이해

❌ 잘못된 이해

  • “캐싱은 Redis나 CDN 같은 별도 계층의 문제다”
  • “같은 요청을 두 번 보내면 두 번 실행된다”

✅ 올바른 이해

하나의 렌더링 사이클 안에서는 같은 요청은 한 번만 실행된다.

이를 request memoization이라 부른다.

  • 동일한 fetch
  • 동일한 인자
  • 동일한 렌더링 컨텍스트

👉 서버 컴포넌트 트리 내에서는
중복 fetch가 자동으로 제거된다.

중요한 포인트:

  • 개발자가 직접 관리하지 않는다
  • 캐시 스토리지에 저장되지 않는다
  • “한 요청 흐름 안에서만” 유효하다

즉, request memoization은
성능 최적화가 아니라 중복 제거 장치다.


2. data cache vs full route cache

가장 많이 헷갈리는 지점

❌ 잘못된 이해

  • “data cache = API 캐시”
  • “full route cache = 페이지 캐시”
  • 둘을 독립적으로 생각함

이 이해는 거의 항상 장애로 이어진다.


✅ 올바른 이해: 계층 구조로 생각해야 한다

NextJS 캐시는 층위가 있다.

1
2
3
4
5
6
7
[ request memoization ]
↓
[ data cache (fetch 단위) ]
↓
[ full route cache (렌더링 결과) ]


Data Cache (데이터 캐시)

  • fetch 결과를 저장
  • 서버 컴포넌트에서 사용
  • 동일한 데이터 요청 재사용

의미

“데이터를 다시 가져올 필요가 있는가?”


Full Route Cache (라우트 캐시)

  • HTML + 렌더링 결과를 저장
  • 데이터 + UI 조합 결과

의미

“이 페이지를 다시 그릴 필요가 있는가?”


핵심 관계

  • data cache가 바뀌지 않으면
    → full route cache도 유효
  • data cache가 무효화되면
    → full route cache도 연쇄 무효화

👉 라우트 캐시는 데이터 캐시에 종속된다.


3. ISR / SSR 선택 기준

“정적 vs 동적”이 아니다

❌ 잘못된 이해

  • ISR = 정적 사이트
  • SSR = 동적 사이트
  • 실시간 데이터면 SSR

✅ 올바른 이해

선택 기준은 데이터의 성격과 허용 지연이다.

ISR이 적합한 경우

  • 데이터 변경 빈도 낮음
  • 약간의 지연 허용 가능
  • 트래픽 많음

정신 모델

“지금 당장 최신이 아니어도 된다”


SSR이 적합한 경우

  • 요청마다 결과가 달라짐
  • 사용자별 데이터
  • 실시간성 중요

정신 모델

“이 요청은 캐시하면 안 된다”


중요한 오해 하나

SSR을 쓴다고
캐시가 없는 것이 아니다.

  • request memoization은 여전히 적용
  • 데이터 단위 캐시는 선택적으로 사용 가능

👉 SSR은 “캐시 없음”이 아니라
“라우트 결과를 캐시하지 않음”이다.


4. revalidatePath / revalidateTag

캐시를 “끄는” 도구가 아니다

❌ 잘못된 이해

  • “데이터 바뀌었으니 캐시 삭제”
  • “업데이트 후 무조건 호출”

✅ 올바른 이해

이들은 캐시 무효화 트리거다.

revalidatePath

  • 특정 URL의 렌더링 결과 무효화
  • 해당 경로의 full route cache 제거

사용 시점

  • 콘텐츠 수정
  • 관리 페이지에서 데이터 변경

revalidateTag

  • 데이터 단위 무효화
  • 해당 tag를 사용하는 모든 fetch 영향

사용 시점

  • 동일 데이터가 여러 페이지에서 사용될 때
  • 데이터 중심 무효화 필요할 때

핵심 사고 전환

“어떤 페이지를 지울까?” ❌
“어떤 데이터 변화가 어떤 UI에 영향을 주는가?” ⭕

revalidateTag는
도메인 중심 캐싱 사고를 강제한다.


장애를 만드는 전형적인 잘못된 사고

  1. 서버 데이터를 상태로 관리
  2. 캐시를 옵션으로 취급
  3. SSR이면 캐시가 없다고 착각
  4. revalidate를 만능 리셋 버튼처럼 사용

이 네 가지는 거의 항상 성능 저하 + 데이터 불일치로 이어진다.


최종 정리: 캐싱 사고 체크리스트

  • 이 데이터는 얼마나 자주 변하는가?
  • “항상 최신”이 정말 필요한가?
  • 데이터 캐시와 라우트 캐시를 구분했는가?
  • 무효화 대상은 페이지인가, 데이터인가?
  • 캐시 전략이 도메인 규칙을 반영하는가?

결론

NextJS의 캐싱은
기능 집합이 아니라 사고 체계다.

  • 데이터를 언제 다시 계산할지
  • UI를 언제 다시 그릴지
  • 서버 비용을 어디서 지불할지

이 질문에 대한 답을
프레임워크가 대신 관리해 주는 것이 NextJS다.

캐싱을 이해하지 못하면 NextJS는 불투명해지고,
캐싱을 이해하면 NextJS는 예측 가능한 시스템이 된다.


This post is licensed under CC BY 4.0 by the author.