결론
server action을 사용해 해당 경로를 revalidation해주세요.
상황
- 서버컴포넌트에서 route handler를 통해 json-server로 요청을 보낸다.
(원래는 서버컴포넌트에서 route handler를 사용할 필요 없이 바로 요청을 보내도 된다.)
- 게시글을 수정하고 추가했을 때 해당 게시물 페이지로 라우팅 된다.
- 라우팅 된 게시물 페이지에서 get요청으로 받은 수정된 게시글 내용이 업데이트되지 않는다.
- 게시글 목록페이지로 넘어가도 추가된 게시물을 볼 수 없다.
- 새로고침을 해야만 변경된 데이터가 모두 로드된다.
post, patch요청한 데이터는 정상적으로 업데이트 되었고
route handler로 호출한 get요청도 정상적으로 되는것을 network 탭에서 확인했다.
Next.js는 빌드 시점에 자동으로 모든 경로를 랜더링하고 캐싱한다.
모든 요청에 대해 서버에서 랜더링 하는 대신 캐시된 경로를 제공해서 페이지 로딩 속도를 높여주는 장점이 있지만
데이터 변경이 있을때는 캐싱을 무효화 시켜주어야 한다.
공식문서에서 소개하는 갖가지 방법을 적용해보며 겪은 실패와 성공기를 기록한다.
시도한 방법
시도 1. getServerSideProps API (동작 X)
13 버전이 출시된 지 얼마 안 되었기 때문에 구글링 했을 때 pages router에서의 해결 방법이 많았다.
getServerSideProps는 요청 시에 데이터를 가져오고 페이지의 콘텐츠를 렌더링 하는 함수이다.
app router를 사용하는 next.js 13 버전부터는 서버 사이드 API가 동작하지 않는다.
시도 2. fetch(url, {cache: 'no-store'}) (동작 X)
next.js에서는 fetch로 가져온 데이터를 자동으로 캐싱한다.
동적으로 데이터를 업데이트하기 위해 fetch API에서의 옵션을 제공한다.
그중 {cache:'no-store'} 옵션은 매번 fetch가 호출될 때마다 데이터를 새로 받아온다.
하지만 get요청을 하는 코드가 있는 상세페이지와 목록 페이지로 라우팅이 되면
기존에 생성되었던 페이지를 로드하기 때문에 같은 데이터를 보게 된다.
시도 3. 새로운 경로로 라우팅 (동작 O)
return router.push(`/posts/detail/${post?.id}?${Date.now()}`);
쿼리 파라미터를 추가해서 새로운 경로를 만들어 같은 경로를 호출하지 않는 척 (?) 해서
fetch를 실행시키는 방법이다.
이 방법은 아주 잘 작동했지만 보기 좋지 않다....
시도 4. import { revalidatePath } from 'next/cache’ (동작 X)
post, patch 요청 이후 무효화를 시켜야 하기 때문에
해당 요청이 일어나는 컴포넌트와 route handler에서의 요청에서
revalidatePath(`/posts/detail/${postId}`)
를 실행시켰지만 동작하지 않았다.
시도 5. route.refresh() (동작 O)
useRouter의 refresh 메서드로 강제 새로고침을 하는 방법이다.
patch, post 요청이 완료된 후 refresh로 fetch를 실행시킨다.
await makeRequest(METHOD.PATCH, `${BASE_API_URL}/posts/${post?.id}`, {
title,
content,
});
router.replace(`/posts/detail/${post?.id}`);
router.refresh();
return;
생활코딩 next.js 강의에서도 router.refresh()를 이용해 게시물의 최신 데이터를 받아와서 적용해 본 방법이고
잘 동작하지만 이 방법도 시도 3번처럼 올바른 방법이 아닌 것 같았다.
시도 6. useEffect (동작 O)
데이터를 상태로 저장해서 마운트 될 때마다 실행시켜 주는 방법이다.
useEffect훅을 사용하려면 클라이언트 컴포넌트를 사용해야 하기 때문에 따로 컴포넌트를 분리해서 사용했다.
데이터를 다른 컴포넌트에도 공유하기 위해 context API를 사용했다.
'use client';
import { ReactNode, useContext, useEffect, useState } from 'react';
import { useParams } from 'next/navigation';
import { createContext } from 'react';
import makeRequest from '@/utils/makeRequest';
import { BASE_URL } from '@/constants/api';
import { Post } from '@/types/types';
import ContentViewer from '@/components/common/ContentViewer';
import H1 from '@/components/common/H1';
const dataContext = createContext<{ post?: Post }>({});
export default function DataContainer({ children }: { children: ReactNode }) {
const [post, setPost] = useState<Post | undefined>(undefined);
const params = useParams();
useEffect(() => {
const dataFetch = async () => {
const postId = params?.id;
const res = await makeRequest('GET', `${BASE_URL}/posts/${postId}`);
setPost(res.post);
};
dataFetch();
}, [params?.id]);
if (!post) {
return <p>Loading...</p>;
}
return <dataContext.Provider value={{ post }}>{children}</dataContext.Provider>;
}
export function PostBox() {
const { post }: { post?: Post } = useContext(dataContext);
return (
<>
<H1>{post?.title}</H1>
<ContentViewer post={post} />
</>
);
}
하지만 게시글 내용이 CSR로 처리된다면 SSR, SSG의 장점인 SEO의 의미가 없어진다.
시도 7. server action (동작 O, 채택한 방법)
vercel에서 작성한 무려 24년 1월에 포스팅된 next.js app router에서 자주 하는 실수에서 찾아낸 방법이다.
server action은 서버에서 실행되는 비동기 함수이다. 서버와 클라이언트 컴포넌트에서 데이터 mutation을 할 때 사용할 수 있다.
함수를 선언하고 상단에 'use server'를 작성한다. 해당 함수는 따로 파일로 분리해도 되고 서버 컴포넌트 내에 정의해도 된다.
클라이언트 컴포넌트에 사용하고 싶다면 import 하거나 prop으로 넘겨준다.
공식문서에 따르면 route handler에서도 revalidatePath를 사용할 수 있는데 왜 4번의 방법이 안되는지는 아직 모르겠다....!
import { revalidatePath } from 'next/cache';
export default async function Page({ params }: ParamsProps) {
async function write(title: string, content: string) {
'use server'; // 서버액션을 생성해서
const res = await makeRequest(METHOD.PATCH, `${BASE_API_URL}/posts/${post?.id}`, {
title,
content,
});
revalidatePath(`/posts/${post?.id}`); //요청 후 해당 경로를 무효화 시켜준다.
return res;
}
return (
<Section>
<PostEdit post={post} write={write} /> // 서버 액션은 클라이언트 컴포넌트에 넘겨줄 수 있다.
</Section>
);
}
마무리
개발 공부를 시작한 지 1년도 안된 사이에 이렇게 업데이트가 자주 일어나는 next.js를 경험해 보면서 프론트엔드의 기술 트렌드 변화 속도를 체감할 수 있었다... 정보를 찾으려 해도 절반정도가 pages router 기반이어서 공식문서를 꼼꼼히 읽어봐야만 했고 덕분에 next.js와 SSR, SSG를 더 깊게 이해할 수 있었다.
'트러블슈팅' 카테고리의 다른 글
[Next.js] Static Site Generation(SSG) 배포하기 (2) | 2024.02.16 |
---|---|
[모모] 페이지 변경시 스크롤이 고정되는 문제 (0) | 2023.09.11 |