- 데이터 로딩은 모든 어플리케이션에서 핵심적인 부분임
- 해당 부분에서는 React, Next.js의 데이터 로딩, 캐싱, 갱신에 대한 방법을 알 수 있음
- 데이터를 가져오는 방법은 아래 4가지가 존재함
- 서버에서
fetch
를 통해 가져오기 - 서버에서 외부 라이브러리를 통해 가져오기
- 클라이언트에서
Route Handler
를 통해 가져오기 - 클라이언트에서 외부 라이브러리를 통해 가져오기
- 서버에서
- Next.js는 기본적으로
fetch
API를 상속받아서 사용중임 - 해당 기능은 서버에 대한 각 요청에 대해서 캐싱과 갱신을 커스텀 할 수 있음
- 리액트는
fetch
를 사용해서 React 컴포넌트 트리를 렌더링 하는 동안 가져오기 요청을 자동으로 캐싱함 - 서버 컴포넌트에서
Route Handler
,Server Actions
내부에서async/await
키워드와 함께fetch
를 사용할 수 있음
async function getData() {
const res = await fetch("https://api.example.com/...");
// 반횐된 값을 직렬화되지 않은 값임.
// Date, Map, Set 등 데이터 반환이 가능함
if (!res.ok) {
// 예외가 발생하는 경우 제일 가까운 error.ts 바운더리가 활성화됨
throw new Error("Failed to fetch data");
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main></main>;
}
- 캐싱은 데이터를 저장해두므로 모든 요청에 대해서 데이터를 또 가져올 필요가 없음
- 기본적으로 fetch에서 반환된 값을 Data Cache에 자동으로 저장함
- 빌드타임이나 요청시간에 데이터를 fetch 하고 각 데이터 요청에 재사용이 가능하다는것을 의미함
// 'force-cache'가 기본값임, 수정도 가능함
fetch("https://...", { cache: "force-cache" });
그러나 캐싱에는 예외가 있음
Server Action
또는 Route Handler
내부 HTTP POST 요청은 캐싱하지 않음
- 갱신은 기존에 캐싱된 데이터를 삭제하고 새로운 데이터를 가져오는 과정임
- 데이터가 변경되고 실시간 정보를 확실히 보여주고 싶을때 유용함
- 갱신 기법은 2가지가 존재함
- 지정한 시간이 지나면 저장된 캐시 데이터를 삭제하는 방법
- 데이터의 변동이 없고 실시간성 데이터가 아닌 경우 유용함
- 시간 기반으로 데이터를 갱신할려고 할때는
next.revalidate
옵션이나 fetch에다가 시간을 지정한다
- 정적 렌더링의 경우 여러개의 요청중에서 제일 낮은 시간이 갱신시간이 된다
- 동적 렌더링의 경우 각 fetch마다 다른 갱신시간을 갖는다
- 특정 경로에 따라서 데이터를 갱신한다
- 특정 태그에 따라서 데이터를 갱신한다
export default async function Page() {
// fetch 옵션에 tags: string[]을 추가한다
const res = await fetch("https://...", { next: { tags: ["collection"] } });
const data = await res.json();
// ...
}
"use server";
import { revalidateTag } from "next/cache";
export default async function action() {
// collection 태그를 갱신한다
revalidateTag("collection");
}
cache: 'no-store'
옵션을 사용
fetch("https://...", { cache: "no-store" });
revalidate: 0
옵션을 사용
fetch("https://...", { next: { revalidate: 0 } });
- Route Handler 내부에서 HTTP POST 메소드 사용
cookies
,headers
같은 동적함수 이후에 request 요청하기- 맨 위에
const dynamic = 'force-dynamic'
정의하기 fetchCache
옵션 지정하기Authorization
이나Cookie
헤더 사용하는 fetch
fetch
옵션에{cache: 'no-store'}
를 사용하면 매 요청마다 동적으로 데이터를 가져온다
- 세그먼트 구성 옵션을 활용해서 제어하기
- 하지만 각 fetch 요청에 대해서 개별로 캐싱 동작을 구성하는것을 권장한다
- 일부 라이브러리(CMS, ORM 등)은 fetch를 지원하지 않음
- 이런 경우 Route Segment Config Option + cache 함수를 사용해서 캐싱 동작 제어가 가능함
- 캐싱 동작의 결정적인 원인은 정적인지 동적인지에 따라 다름
- 정적 : 데이터 요청 결과가 캐싱되고 세그먼트의 일부로 재검증이 가능함
- 동적 : 데이터 요청의 결과가 캐싱되지 않고, 세그먼트가 렌더링 될때마다 새로 가져옴
- React의
cache
함수는 데이터 요청에 대해 캐싱이 가능함 - 만약 revalidate 옵션을 3600으로 설정하면, 1시간 마다 새로운 데이터를 가져온다고 지정하는거임
import { cache } from "react";
export const getItem = cache(async (id: string) => {
const item = await db.item.findUnique({ id });
return item;
});
import { getItem } from "@/utils/get-item";
export const revalidate = 3600; // 데이터를 1시간 마다 새로 갱신한다
export default async function Page({ params: { id } }: { params: { id: string } }) {
const item = await getItem(id);
// ...
}
- 만약 클라이언트 컴포넌트에서 데이터 가져오기가 필요하면, 클라이언트에서 라우트 핸들러 호출이 가능함
- 라우트 핸들러는 서버에서 동작하고 클라이언트로 데이터를 반환해줌
- API 토큰 등 중요한 정보를 클라이언트에 노출하지 않으려 할때 유용함
- 마찬가지로 SWR 또는 TanStack Query를 활용해서 클라이언트에서 데이터 로딩이 가능함
- 이런 라이브러리들은 자체적으로 캐싱, 갱신 등 기능을 보유하고있음