[Number] 이슈 컨텐츠
상황
내가 처음 생각했던 방식 (잘못된 이해)
실제 동작
결론
1. generateStaticParams ,revalidatePath, dynamicParams 의 동작
상황
- 블로그 상세 페이지 경로: /post/[postId]
- generateStaticParams를 선언하여 빌드 시점에 정적 경로를 생성
- dynamicParams = false로 설정하여 빌드 시점에 존재하지 않는 경로는 접근 시 404가 뜨도록 제한
- 새로운 포스트가 추가되면 revalidatePath를 통해 최신 상태가 반영되길 기대.
내가 처음 생각했던 방식 (잘못된 이해)
- revalidatePath를 호출하면 해당 경로의 캐시가 무효화된다.
- 캐시 무효화 시 generateStaticParams가 다시 실행되어 새로운 정적 파라미터(postId)가 반영되고, 새로운 HTML이 생성된다고 생각했다.
- 따라서 새로운 포스트를 추가해도 dynamicParams = false 에 걸리지 않고 렌더링 된다고 생각함.
실제 동작
- generateStaticParams는 빌드 시점에만 실행된다.
- 하지만 generateStaticParams는 재배포(빌드)를 하지 않는다면 재실행 되지 않는다. 즉, 런타임(운영 중)에는 절대 다시 실행되지 않는다.
- revalidatePath는 특정 경로의 캐시를 무효화하고,→ 새로 생성된 SSG HTML이 캐싱되는 과정이다.
- 클라이언트가 해당 경로를 다시 요청(접근)할 때 Next.js가 ISR(Incremental Static Regeneration) 방식으로 HTML을 새로 생성한다.
- dynamicParams: false는 빌드 시점에 생성된 경로만 허용한다.→ 즉, revalidatePath를 호출해도 해당 postId가 빌드 시점에 존재하지 않았다면 새 HTML을 만들 수 없다.
- 빌드 후 새로 추가된 postId는 목록에 없으므로 접근 시 404가 뜬다.
- 따라서 빌드 이후에도 새로운 포스트 상세 페이지를 지원하려면 dynamicParams: true로 설정해야 한다.
- 이 경우 새 postId로 접근하면 ISR을 통해 HTML이 생성되고, 이후엔 캐시가 재사용된다.
결론
- 정적 파라미터를 “재생성”하는 기능은 없다.
- revalidatePath는 캐시 무효화 + 해당 경로의 ISR 재생성만 담당한다.
- 빌드 후 추가된 새로운 동적 경로까지 지원하려면 dynamicParams: true가 필수이다. (next는 기본적으로 이를 true로 둬서 굳이 명시는 안해도 됨)
- 재배포할 때만 데이터 추가를 할 것이 아니라면 dynamicParams는 false로 두지 말아야겠다.
2. permanentredirect (next/cache) 를 사용했을 경우 문제점
상황
- "/[pageNumber]" "/[category]/[pageNumber]" 경로에선 전체 포스트 리스트 또는 카테고리에 해당하는 포스트 리스트를 페이지네이션으로 보여주고 있음
- 사용자의 "/" 또는 '/[category]" 에 접근 할 경우 404가 뜰테니 이를 방지하기 위해 pageNumber가 1인 페이지로 리다이렉트
- 같은 경로 상위 layout.tsx에서 생성 중인 SSG 컴포넌트인 사이드바가 깜빡거리는 현상 목격
내가 처음 생각했던 방식 (잘못된 이해)
- app router에서 (home) 경로를 생성 후 상위에 layout.tsx 를 생성했고 그 안에는 공동으로 사용 될 레이아웃인 Sidebar와 Header 생성
- "/[pageNumber]" 와 "/[category]/[pageNumber]" 이 경로에서는 param에 해당하는 page.tsx에는 포스트 리스트를 자식으로 두고 있음
- 따라서 클라이언트에서 "/" 또는 "/[category]" 로의 요청(접근)을 할 경우 pageNumber가 1에 해당하는 포스트 리스트를 보여주기 위해 "/" ,"/[category]" 에 page.tsx를 생성 후 permanentredirect("/1") 과 같이 선언
- 위와 같이 영구 리다이렉트를 시킨다면 어차피 layout 하위 경로인 페이지 이동이 될테니 layout.tsx에서 children에 해당하는 page.tsx들 이외의 컴포넌트들은 리렌더링 되지 않을 거라고 생각
실제 동작
1. 하지만 "/" 과 "/[category]" 에 해당하는 페이지들로 이동할 때마다 사이드바가 깜빡거리는 현상을 목격
2. 리렌더링을 유발하는지 알아보기 위해 Sidebar에 콘솔을 찍어봤고 위 경로에 첫 접근 할 때마다 콘솔 출력을 확인
3. permanentredirect 와 redirect 메서드는 리다이렉트를 수행하면서 전체 페이지의 리렌더링을 유발한다는 것을 확인
결론
1. page.tsx에 동작에 의해 굳이 재실행 될 필요 없는 layout 요소가 리렌더링 되는 것을 피하기 위해 방법을 찾던 중 next config의 rewrites 메서드를 알게 됨.
2. config 자체에서 아래와 같이 옵션을 넣어준다면 사용자 화면에선 사용자가 요청한 처음 경로(source)가 계속 보이지만 next 내부에선 이를 destination으로 인식해서 렌더링을 한다.
3. 그 덕분에 전체 리렌더링이 아닌 page.tsx의 렌더링만을 유발하여 sidebar에 영향을 안줌
async rewrites() {
return [
{
source: '/',
destination: '/1',
},
{
source: '/:category',
destination: '/:category/1',
},
];
},
3. unstable_cache, ravalidateTag & revalidatePath
상황
- 서버 컴포넌트에 쓰일 함수가 prisma를 통해 데이터를 직접 가져옴.
- fetch를 사용하지 않기 때문에 revalidateTag에 쓰일 태그를 쓰지 못함. 그래서 unstable_cache로 캐싱 후 태그를 설정
- 카테고리 수정을 하고 revalidateTag를 실행 해도 변경 전 내용이 화면에 렌더링
내가 처음 생각했던 방식 (잘못된 이해)
- /layout에 Sidebar를 자식으로 두고 Sidebar에선 무효화 태그를 사용하기에 카테고리 수정 후 revalidateTag를 사용한다면 즉시 캐시를 비우고 사용자가 요청하면 변경 된 데이터를 통한 Sidebar를 기반으로 HTML을 생성한다고 생각함.
- 또한 unstable_cache에 revalidate 시간을 설정하여 주기마다 캐시를 검증할려고 함.
실제 동작
- 카테고리 변경 후 revalidateTag를 실행하더라도 화면에는 이전 카테고리를 담고 있는 Sidebar가 화면에 보여짐.
- 문서를 확인해보니 revalidate를 설정하면 그 주기마다 캐시를 체크하기 때문에 revalidateTag가 씹히는 경우가 있다고 함. revalidate가 우선적으로 적용되어서.
결론
카테고리와 같은 정보는 주기마다 체크 할 이유가 없기에 주기 옵션을 지움
아래는 문서 내용( https://nextjs.org/docs/app/api-reference/functions/unstable_cache)
- options: This is an object that controls how the cache behaves. It can contain the following properties:
- tags: An array of tags that can be used to control cache invalidation. Next.js will not use this to uniquely identify the function.
- revalidate: The number of seconds after which the cache should be revalidated. Omit or pass false to cache indefinitely or until matching revalidateTag() or revalidatePath() methods are called.
Next.js App Router에서 revalidatePath와 SSG 캐시 무효화의 진짜 동작 원리
상황
• /(home) 경로의 `layout.tsx`에서는 카테고리 목록을 보여주는 SSG sidebar가 사용되고 있음.
• `page.tsx`는 `generateStaticParams`를 이용해 빌드 시점에 정적 HTML로 생성됨.
• 카테고리 정보 수정 시, sidebar의 최신 데이터를 반영하려고 `revalidatePath("/", 'layout')`을 호출했으나,
카테고리 페이지 이동 시 sidebar가 아직 변경 전 내용으로 표시되는 문제가 발생함.
기존에 잘못 이해했던 부분
• 상위 경로(예: `/`)의 layout만 무효화해도 카테고리(예: `/category`)를 포함한 전체 경로의 최신 sidebar가 반영된다고 생각했음.
• `/category` 경로에 별도 layout이 없으니 상위 layout만 새로 고침되면 모든 하위 페이지도 새로 렌더링된다고 이해함.
실제 동작 원리
• `revalidatePath("/", 'layout')`은 해당 layout의 캐시만 무효화할 뿐, 하위 경로(`/category` 등)에 별도 캐싱(segment 단위 SSG 결과)이 존재하면 그 부분은 다시 생성되지 않음.
• Next.js App Router는 각 segment(예: `/`, `/category` 등)의 layout과 page 컴포넌트를 별도 파일로 캐싱하고 관리함.
• `/category` 경로에 layout 파일이 없더라도, Next.js는 상위 layout을 참조하며 각 경로별로 정적 HTML을 별도 캐시에 저장함.
• 즉, 상위 layout만 무효화해도 하위 경로들이 모두 새로 생성되는 것이 아니라, 실제 무효화 범위는 `revalidatePath`에 지정한 경로의 layout과 그 하위 경로의 layout에서만 효과가 있음.
• 브라우저 강제 새로고침 시에는 서버로부터 최신 데이터를 받아오니 sidebar 반영이 되어 보일 수 있으나, 이는 SSG 캐시와는 별개로 일시적인 현상임.
실제로 해결하려면?
• sidebar에 최신 데이터를 보여주고 싶다면 아래 선택지를 고려:
• sidebar 컴포넌트를 SSR로 렌더링하거나 fetch 옵션에 no-store 설정.
• `revalidatePath`를 sidebar가 포함된 모든 경로, 또는 `/category`처럼 하위 경로까지 반복적으로 호출.
• sidebar를 별도의 api route 또는 server action에서 fetch되도록 구조 변경하여 실시간 반영.
• SSG 캐시 범위와 revalidatePath의 옵션(layout 여부)에 따라 실제로 재생성되는 HTML의 범위가 다르니, 구조/정책을 설계할 때 반드시 이 점을 고려해야 함.
결론
• revalidatePath의 ‘layout’ 옵션은 지정한 경로의 layout 및 그 하위 layout만 무효화하며, 각 segment(page/layout)는 별도 정적 캐시로 관리됨.
• sidebar처럼 여러 경로에서 공통 사용하는 정적 데이터가 바뀔 때는, SSR 변환, 캐시 무효화 경로 확대, 또는 동적 fetch 구조 적용 등으로 문제를 해결해야 함.
• Next.js App Router의 캐싱 구조와 무효화 범위를 제대로 이해하고 적용해야 의도한 대로 데이터가 갱신됨을 유의할 필요가 있음.
'FrontEnd > Next.js' 카테고리의 다른 글
| Next.js로 SSE 환경 만들기 (app router에서 better-sse 사용 불가 이유) (0) | 2025.11.23 |
|---|---|
| revalidatePath로 캐시 무효화 시 마주칠 수 있는 상황 (0) | 2025.10.02 |
| 아이템 리스트 페이지의 서버 컴포넌트를 최적화 시켜보기(fetch 캐싱 옵션) (0) | 2025.09.19 |
| 서버 컴포넌트에서 fetch와 캐싱 동작: SSR, SSG, ISR 관점에서 정리 (0) | 2025.09.13 |
| Resend로 발송 시 개발 모드에서 이메일이 오지 않는 경우 (0) | 2025.08.19 |