서버 컴포넌트 도입으로 변경된 데이터 페칭 방식
라우터 버전이 갖는 한계를 극복하기 위한 것
SSR : getServerSideProps
SSG, ISR : getStaticProps
Export async function getServerSideProps() {
// 데이터 페칭 코드
return { props: { … } };
}
Export async function getStaticProps() {
// 데이터 페칭 코드
return { props: { … } };
}
= 불러온 데이터를 페이지 컴포넌트에 Props로 전달된다.
// 서버에서만 실행되는 코드
Export async function getServerSideProps() {
// 데이터 페칭 코드
return { props: { … } };
}
// 서버와 클라이언트에서 모두 실행되는 코드
Export function Page(props) {
return <div> … </div>;
}
Async function Page() {
const dataForPage = await fetch(“…”); // 페이지 컴포넌트에 필요한 데이터 페칭
return <Child/>;
}
Async function Child() {
const dataForChild = await fetch(“…”); // Child 컴포넌트에 필요한 데이터 페칭
return <div>…</div>
}
서버 컴포넌트이기 때문에 async/await 으로 비동기적으로 가져온다.
백엔드 서버에서
getServerSideProps와 같은 함수를 사용할 필요 없이 컴포넌트가 필요한 데이터를 직접 가져올 수 있어 데이터 전달이 단순
async function RecoBooks() {
try {
const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/book/random`,
{ cache: "force-cache"} // fetch 메서드로 불러온 데이터는 무조건 캐시 (영구적)
);
-> 로깅 기능을 사용하면 캐시 동작을 직관적으로 확인 가능 (cache skip)
cache: “force-cache” : 영구적으로 캐시하는 옵션 (주로 정적 데이터에 사용)
= 첫 번째 접속 요청일 경우 ( 캐시 miss) 가 발생한다. -> 백엔드 서버에 데이터를 요청하기 전 데이터가 있는지 확인 (cache hit)
cache: “no-store” : 요청으로 불러온 데이터를 절대 캐시하지 않고, 캐시된 데이터도 사용하지 않는다. (매번 백엔드 서버에 요청) = 매번 새로운 데이터에 사용
Next.js 15버전부터 캐시 옵션을 설정하지 않아도 된다.
next: { revalidate: 시간 } : 3으로 설정하면 3초 주기로 갱신
Next: { tags: [] } : 특정 시점의 요청 기반으로 데이터 캐시 무효화
설정한 태그값을 이용해 특정 시점에 캐시 데이터를 갱신할 수 있다.
Ex. Next: { tags: [“a”] } “a”라는 태그를 활용해 특정 시점 데이터를 갱신, 하나의 페칭에 여러 태그 설정 가능
{ next: { revalidate: 3, tags: [“a”] }} : 특정 주기로 데이터 갱신, “a” 태그로 갱신
{ cache: “force-cache”, next: { tags: [“a”] }} : 영구적으로 캐싱된 데이터, “a”태그로 갱신
Export async function GET() {
await revalidateTag(“a”);
} // revalidateTag “a” 태그와 관련된 데이터 캐시는 무효화(purge)
fetch(‘/book/‘);
fetch(‘/book/search?q=123’); // 요청 주소, 쿼리 파라미터 기준으로 별도의 캐시로 저장
fetch(“/book”, { headers: { Authorization: “Bearer token-1” }, });
= 데이터 캐시 확인하려면 .next에서 cache/fetch-cache 폴더 확인
http 요청을 보내기 위한 라이브러리, 백엔드 서버에서 데이터 요청, 보낼 때 사용한다.
Axios : 직관적인 기능(인터셉터, 에러 처리) http요청
ky: fetch API를 기반으로 만든 가볍고 모던한 라이브러리이다. 코드가 간겨라면서 구조적이어서 읽기 쉽다.
fetch메서드를 사용할 수 없는 상황이 존재 서드파티가 제공하는 메서드를 이용해 데이터를 불러와야 하기 때문
=> cache 메서드를 사용해 데이터 요청을 수동으로 메모이제이션 할 수 있다.
Import { cache } from “react”;
Export const getPosts = cache(async () => {
const { data: posts } = await supabase.from(“posts”).select();
return posts;
});
Cache 인수로 함수를 받아 함수의 반환값을 캐시한다. 전달된 인수를 기준으로 이루어지며 프로그램이 종료되면 자동으로 소멸된다.
Request Memoization 데이터 페칭 최적화 기능
서버에서 특정 페이지를 사전 렌더링할 때 페이지에 필요한 데이터를 불러오는 요청이 중복 수행되지 않도록 최적화한다.
= 페이지를 사전 렌더링할 때만 사용된다. (중복 요청 방지)
cache: “no-store” 로 변경한다. => 새로운 데이터 불러온다. (서버에서 확인 가능 request )
Try-catch 문 외에도 error.tsx 파일을 사용하여 에러를 감지하고 처리할 수 있다.
App폴더에 error.tsx생성 단계별로 에러 처리 (계층적 에러 처리 방식)
“Use client” 컴포넌트가 오류를 복구할 때 브라우저가 제공하는 기능 사용
force-cache를 사용하면 try-catch 예외처리 해도 error 페이지에 렌더링되지 않는다.
// try-catch 문 지우면 가능
error를 핸들링할 때 하위의 레이아웃 파일은 모두 무시된다.
검색 폼 레이아웃도 같이 렌더링하려면 (with-searchbar) 폴더에 별도 error.tsx 파일 생성
"use client";
interface Props {
error: Error & { digest?: string};
}
export default function Error({error}: Props) {
console.log(error);
return (
<div>오류가 발생했습니다.</div>
)
} // props의 타입을 정의 -> error 프로퍼티 포함, props로 받은 error 객체를 콘솔에 출력한다.
reset에 페이지에서 발생한 오류를 복구할 수 있는 함수가 있다.
"use client";
interface Props {
error: Error & { digest?: string};
reset: () => void;
}
export default function Error({error, reset}: Props) {
console.log(error);
return (
<>
<div>오류가 발생했습니다.</div>
<button onClick={() => reset()}>다시 시도</button>
</>
)
} // 똑같은 오류 메시지만 계속 출력된다.
onClick = {() => {
window.location.reload(); // 새로고침 하면 서버에게 초기 접속 요청을 다시 보내며, 오류 해결
}}
= 다시시도를 먼저 가동 후 서버 컴포넌트가 실행
<button onClick={() => {
startTransition(() => { // startTransition 메서드를 사용하여 간단히 해결할 수 있다.
router.refresh(); // 서버 컴포넌트 다시 실행
reset(); // 다시 렌더링