Home fetch
Post
X

fetch

App Router에서 사용하는 axios와 fetch에 대해 알아보자.


fetch

App Router의 서버 컴포넌트에서 fetch를 사용할 경우, 단순히 데이터를 가져오는 도구를 넘어서, Next.js의 렌더링 엔진과 깊게 통합된 고급 기능을 제공합니다.


자동 캐싱

Next.js는 기본적으로 응답을 SSG 방식처럼 캐싱합니다.

이로인해 동일한 요청 시, 사용자는 빠르게 결과를 받을 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
// app/products/page.tsx
const getProducts = async () => {
  const res = await fetch("https://api.example.com/products"); // 자동 캐싱됨
  return res.json();
};

export default async function Page() {
  const products = await getProducts();

  return <div>{products.length}개의 상품</div>;
}

캐시 무효화 (revalidate)

next: { revalidate: N } 옵션을 사용해 일정 시간마다 캐시를 무효화하고 새 데이터를 가져오게 할 수 있습니다.

Next.js는 백그라운드에서 새로운 데이터를 요청해서 캐시를 갱신합니다.

ISR 과 같은 효과

1
2
3
4
5
6
const getPosts = async () => {
  const res = await fetch("https://api.example.com/posts", {
    next: { revalidate: 30 }, // 30초마다 새로고침
  });
  return res.json();
};

Preloading

Next.js가 빌드 시점에 필요한 데이터를 미리 요청해 사전 로딩합니다.

1
2
3
4
5
6
7
8
9
10
11
12
// app/page.tsx
import Link from "next/link";

export default function Home() {
  return <Link href="/products">제품 목록 보러가기</Link>;
}

// app/products/page.tsx
const getProducts = async () => {
  const res = await fetch("https://api.example.com/products");
  return res.json();
};

Linkhover하는 순간 preload가 발생해 사용자가 /products로 가기 전에, getProducts()의 fetch()를 미리 호출합니다.

next/link를 사용할 때 기본으로 활성화


병렬 요청 최적화

하나의 서버 컴포넌트 안에서 여러개의 fetch를 요청하는 경우, 하나의 요청으로 통합되거나 병렬 처리합니다.

실제로는 await 없이 먼저 fetch를 실행해서 Promise 객체를 저장한 뒤, Promise.all() 등으로 병렬 처리합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const getUser = () =>
  fetch("https://api.example.com/user").then((res) => res.json());
const getOrders = () =>
  fetch("https://api.example.com/orders").then((res) => res.json());

export default async function Page() {
  const [user, orders] = await Promise.all([getUser(), getOrders()]); // 병렬 처리됨

  return (
    <div>
      <h1>{user.name}님의 주문 목록</h1>
      <ul>
        {orders.map((o: any) => (
          <li key={o.id}>{o.item}</li>
        ))}
      </ul>
    </div>
  );
}

서버 전용 실행

app/ 내부의 서버 컴포넌트에서만 실행되므로 클라이언트 번들에 포함되지 않습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/dashboard/page.tsx (서버 컴포넌트)
const getSecret = async () => {
  const res = await fetch(process.env.INTERNAL_API_URL!, {
    headers: {
      Authorization: `Bearer ${process.env.INTERNAL_TOKEN}`,
    },
  });
  return res.json();
};

export default async function DashboardPage() {
  const secret = await getSecret();
  return <div>민감 정보: {secret.data}</div>;
}

API Key, 토큰 등 보안 정보가 절대 클라이언트로 노출되지 않습니다.


cache: ‘no-store’ 옵션

SSR처럼 항상 새 데이터를 가져옵니다.

1
2
// SSR: 항상 최신 데이터 (캐싱 없음)
fetch(url, { cache: "no-store" });

응답 구조

fetch의 응답은 Response 객체로 제공됩니다.

  • status : HTTP 응답 상태 코드
  • statusText : HTTP 상태 코드에 대한 텍스트 설명 (예: “OK”, “Not Found”)
  • headers : 응답 헤더 정보
  • ok : 응답이 성공적이면 true, 실패하면 false
  • json() : 응답 본문을 JSON 형식으로 파싱하여 반환하는 메서드
  • text() : 응답 본문을 텍스트 형식으로 반환하는 메서드
  • blob() : 응답 본문을 Blob 객체로 반환하는 메서드
  • arrayBuffer() : 응답 본문을 ArrayBuffer 객체로 반환하는 메서드

fetch와 await 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function fetchData() {
  try {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/posts/1"
    );

    // 응답 상태가 성공적이라면
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }

    const data = await response.json(); // 응답 본문을 JSON으로 변환
    console.log("Fetched Data:", data); // 응답 데이터 출력
  } catch (error) {
    console.error("Fetch Error:", error); // 오류 처리
  }
}

fetchData();
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.