Next.js - 앱 라우터 버전의 데이터 페칭

Stella·2026년 1월 5일

Next.js

목록 보기
3/8

- 앱 라우터 버전의 데이터 페칭

서버 컴포넌트 도입으로 변경된 데이터 페칭 방식
라우터 버전이 갖는 한계를 극복하기 위한 것

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>;
}

= 이러한 Props로 전달하는 방식은 문제를 만든다.

페이지를 구성하는 컴포넌트 간 데이터를 전달하는 과정이 불필요하게 복잡해진다. Props Drilling문제

  • 서버 컴포넌트 사용하여 문제 해결
Async function Page() {
	const dataForPage = await fetch(“…”); // 페이지 컴포넌트에 필요한 데이터 페칭
	return <Child/>;
}

Async function Child() {
	const dataForChild = await fetch(“…”); // Child 컴포넌트에 필요한 데이터 페칭
	return <div></div>
}

서버 컴포넌트이기 때문에 async/await 으로 비동기적으로 가져온다.
백엔드 서버에서

앱 라우터 버전에서 컴포넌트가 직접 서버에서 데이터를 가져온다.

getServerSideProps와 같은 함수를 사용할 필요 없이 컴포넌트가 필요한 데이터를 직접 가져올 수 있어 데이터 전달이 단순

데이터 요청을 영구적으로 보관하는 데이터 캐시

  1. 데이터 캐시 : fetch 메서드로 데이터를 불러올 때 사용, fetch 메서드에서 두번째 인수로 옵션 추가
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 폴더 확인

- Axios, ky

http 요청을 보내기 위한 라이브러리, 백엔드 서버에서 데이터 요청, 보낼 때 사용한다.
Axios : 직관적인 기능(인터셉터, 에러 처리) http요청
ky: fetch API를 기반으로 만든 가볍고 모던한 라이브러리이다. 코드가 간겨라면서 구조적이어서 읽기 쉽다.

- 서드파티(Firebase, Supabase, Prisma 등) 사용하는 경우

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.tsx 파일로 발생한 오류를 일괄처리 한다.

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(); // 새로고침 하면 서버에게 초기 접속 요청을 다시 보내며, 오류 해결
}}
  • 라우터 객체를 이용 (추천)
    새로고침 해도 정보가 다 날라가지 않도록
    useRouter 훅 next/navigation 패키지에서 불러온다.
    Const router = useRouter();
    router.refresh(); // 서버 컴포넌트 다시 실행하도록
    reset(); // 다시 시도

= 다시시도를 먼저 가동 후 서버 컴포넌트가 실행

<button onClick={() => { 
	startTransition(() => { // startTransition 메서드를 사용하여 간단히 해결할 수 있다.
		router.refresh(); // 서버 컴포넌트 다시 실행
		reset(); // 다시 렌더링
  • startTransition : UI를 멈추지 않고 여유있게 업데이트 하는 기능
profile
공부 기록

0개의 댓글