https://beta.nextjs.org/docs/data-fetching/fetching
위 문서에 대한 번역을 진행합니다.
번역시점은 2023-05-01로 공식문서의 추가적인 업데이트가 있을 수 있습니다.
번역기와 챗지피티에 의존해서 번역하고있습니다.
번역체를 자연스러운 어투로 옮기는 과정에서 오역이 발생할 수 있는 점 미리 알립니다.
한글로 번역하는 것이 더 어색한 단어의 경우 원문을 먼저 표기하겠습니다.
역자의 개인적인 의견에 대한 내용은
이 안에서 이루어집니다.
Data Fetching
Good to know
이 새로운 데이터페칭모델은 현재 React 팀에서 개발 중입니다.
우리는 React RFC 문서를 읽어볼 것을 추천해요!
async/await 기능을 도입한 서버 컴포넌트와 클라이언트 컴포넌트를 위한 새로운 use()hook을 찾아 볼 수 있습니다.
리액트와 Next.js 13에는 애플리케이션에서 데이터를 가져오고 관리하는 새로운 방법이 도입되었습니다.
이 새로운 데이터 페칭 시스템은 App directory에서 동작하며 web api인 fetch를 기반으로 구축됩니다.
fetch는 Promise를 반환하는 web API로 원격 리소스를 가져오는 데 사용됩니다.
React는 fetch를 확장하여서 자동 요청 중복 제거를 제공하고
Next.js는 fetch 옵션 객체를 확장하여 각 요청이 자체 캐싱 및 재검증(revalidate)를 설정할 수 있도록 합니다.
async/await in Server Components
제안된 React RFC를 사용하면 서버 컴포넌트에서 async와 await을 사용하여 데이터를 가져올 수 있어요!
app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/...');
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main></main>;
}
Async Server Component TypeScript error
async 서버 컴포넌트가 사용된 곳에서 타입스크립트는 Promise<Element>가 유효한 jsx 엘리먼트 유형이 아니라는 오류를 발생시킵니다.
이것은 TypeScript의 문제이며 현재 작업중에 있습니다.
임시방편으로 컴포넌트 위에 {/* @ts-expect-error Async Server Component */}를 추가하는 것으로
해당 컴포넌트에 대한 타입 체킹을 비활성화 할 수 있습니다.
use in Client Components
use는 await과 개념적으로 유사합니다.
use는 프로미스를 accepts하는 새로운 리액트 함수입니다.
use는 함수가 반환한 프로미스를 핸들링하며 이는 컴포넌트, 훅, suspense와 호환됩니다.
자세한것은 React RFC 문서에서 참고하세요!
현재 클라이언트 컴포넌트에서 사용 중인 fetch를 wrapping하는 방식은 권장되지 않으며
그 이유는 이러한 방식이 여러번의 리렌더링을 유발시킬 수 있기 때문입니다.
현재로서는 클라이언트 컴포넌트에서 데이터를 페칭해야하는 경우
SWR, React Query와 같은 서드파티라이브러리를 사용하는 것을 추천합니다.
Note : 클라이언트 컴포넌트에서 작업을 가져와서 사용할 수 있게 되면 더 많은 예제를 추가할 예정입니다.
Static Data Fetching
기본적으로 fetch는 데이터를 자동으로 가져오고 무기한 캐시합니다.
fetch('https://...'); // cache: 'force-cache' is the default
//역주 : fetch의 옵션객체의 cache 속성은 force-cache가 default라는 의미
Revalidating Data
시간 간격을 두고 캐시된 데이터의 유효성을 재검증하려면
fetch()의 next.revalidate 옵션을 사용하여 리소스의 캐시 수명(초)를 설정하면 됩니다.
fetch('https://...', { next: { revalidate: 10 } });
//역주 : next.revalidate의 값을 설정해주는 것으로 어느 주기로 revalidate할지 설정할 수 있다.
Dynamic Data Fetching
모든 fetch 요청에서 항상 새로운 데이터를 가져오려면 cache: 'no-store' 옵션을 사용하세요!
fetch('https://...', { cache: 'no-store' })
Data Fetching Patterns
병렬 데이터 페칭
클라이언트와 서버간의 워터폴을 최소화하려면 다음 패턴을 사용하여
데이터를 병렬적으로 가져오는 것이 좋습니다.
app/artist/[username]/page.jsx
import Albums from './albums';
async function getArtist(username) {
const res = await fetch(`https://api.example.com/artist/${username}`);
return res.json();
}
async function getArtistAlbums(username) {
const res = await fetch(`https://api.example.com/artist/${username}/albums`);
return res.json();
}
export default async function Page({ params: { username } }) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumsData = getArtistAlbums(username);
// Wait for the promises to resolve
const [artist, albums] = await Promise.all([artistData, albumsData]);
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums}></Albums>
</>
);
}
서버 컴포넌트에서 await을 호출하기 전에 fetching을 시작하면 각 요청이 동시에 요청을 가져오게 할 수 있습니다.
이렇게 하면 워터폴을 피할 수 있도록 컴포넌트가 설정됩니다.
두 요청을 동시에 시작하면 시간을 절약할 수 있지만 두 프로미스가 모두 해결될 때 까지
사용자는 렌더링된 결과를 볼 수 없습니다.
사용자 경험을 개선하기 위해 suspense boundary를 추가하여 렌더링 작업을 분할하고 결과의 일부분을 가능한 한 빨리 표시할 수 있습니다.
artist/[username]/page.jsx
export default async function Page({ params: { username } }) {
// Initiate both requests in parallel
const artistData = getArtist(username);
const albumData = getArtistAlbums(username);
// Wait for the artist's promise to resolve first
const artist = await artistData;
return (
<>
<h1>{artist.name}</h1>
{/* Send the artist information first,
and wrap albums in a suspense boundary */}
<Suspense fallback={<div>Loading...</div>}>
<Albums promise={albumData} />
</Suspense>
</>
);
}
// Albums Component
async function Albums({ promise }) {
// Wait for the albums promise to resolve
const albums = await promise;
return (
<ul>
{albums.map((album) => (
<li key={album.id}>{album.name}</li>
))}
</ul>
);
}
컴포넌트 구조 개선에 대한 자세한 내용을 참고하고 싶다면 preloading pattern을 참고하세요!
Sequential Data Fetching
데이터를 순차적으로 가져오기위해서는 데이터가 필요한 컴포넌트 내부에서 직접 가져오거나
혹은 데이터가 필요한 컴포넌트 내부에서 fetch 결과를 기다릴 수 있습니다.
app/artist/page.tsx
async function Playlists({ artistID }) {
// Wait for the playlists
const playlists = await getArtistPlaylists(artistID);
return (
<ul>
{playlists.map((playlist) => (
<li key={playlist.id}>{playlist.name}</li>
))}
</ul>
);
}
export default async function Page({ params: { username } }) {
// Wait for the artist
const artist = await getArtist(username);
return (
<>
<h1>{artist.name}</h1>
<Suspense fallback={<div>Loading...</div>}>
<Playlists artistID={artist.id} />
</Suspense>
</>
);
}
컴포넌트 내부에서 데이터를 가져오면 경로의 각 fetch 요청과 중첩된 세그먼트는 이전 요청 or 세그먼트가 완료될 때 까지 data fetching 및 rendering을 시작할 수 없습니다.
Blocking Rendering in a Route
layout 에서 데이터를 fetching하면 그 아래의 모든 route segments에 대한 렌더링은 데이터 로딩이 완료된 후에만 시작할 수 있습니다.
pages directory에서 서버 렌더링을 사용하는 페이지는 getServerSideProps가 완료될때까지 브라우저 로딩 스피너를 표시한 다음 해당 페이지에 대한 React 컴포넌트를 렌더링합니다.
이는 all or nothing data fetching으로 설명할 수 있습니다.
페이지에 대한 전체 데이터를 가져올 수도 있고 아무것도 가져올 수 없을 수도 있다는 의미에서요!
app directory에는 탐색을 할 수 있는 추가적인 옵션이 있습니다.
1. loading.js를 이용하여 데이터 불러오기 함수에서 결과를 스트리밍하는 동안 서버에서 즉시 로딩 상태를 표시할 수 있습니다.
2. 컴포넌트 트리에서 data fetching을 컴포넌트 트리의 아래로 리동시켜 blocking 되는 렌더링이 페이지의 일부분에 그치도록 할 수 있습니다. 예를 들어 loot layout에서 데이터를 가져오는게 아니라 특정 컴포넌트에서 데이터를 가져오도록 로직을 옮기는 것입니다. 가능하다면 데이터를 사용하면 세그먼트에서 데이터를 가져오는게 가장 좋습니다. 이렇게 하면 전체 페이지가 아니라 로딩 중인 부분에만 로딩 상태를 표시할 수 있어집니다!
역주 : 데이터 페칭이 완료되기 전까지는 fallback을 보여주어야 하는데 페이지 전체를 로딩 상태로 두는것보다
페이지의 일부분(실제 데이터를 받아와야만 표현할 수 있는 부분)만 로딩을 시키는게 더 좋다는 이야기 인 것 같네요
Data Fetching without fetch()
ORM이나 데이터베이스 클라이언트와 같은 타사 라이브러리를 사용하는 경우
fetch 요청을 직접 사용하고 구성할 수 있는 기능이 항상 제공되는 것은 아닙니다.
fetch를 사용할 수 없지만 layout이나 페이지의 캐싱, 재검증 동작을 제어하려고하는 경우에는
세그먼트의 기본 캐싱 동작을 사용하거나 세그먼트 캐시 구성을 사용할 수 있습니다.
default Caching Behavior
fetch를 직접 사용하지 않는 data fetch 라이브러리는 경로 캐싱에 영향을 미치지 않으며
경로 세그먼트에 따라서 정적 혹은 동적으로 동작합니다.
세그먼트가 정적(기본값)인 경우에는 요청의 출력은 나머지 세그먼트와 함께 캐시되고 재검증됩니다.(configured된 경우에는요!) 세그먼트가 동적인 경우에는 요청의 출력은 캐시되지 않고 세그먼트가 렌더링될때마다 요청을 다시 가져옵니다.
Good to know
cookies() , header()와 같은 dynamic function은 경로 세그먼트를 동적으로 만듭니다.
Segment Cache Configuration
임시방편으로 타사 쿼리의 캐싱 동작을 구성할 수 있을때까지
segment configuration을 이용하여 전체 세그먼트의 캐시동작을 커스터마이징 할 수 있습니다.
import prisma from './lib/prisma';
export const revalidate = 3600; // revalidate every hour
async function getPosts() {
const posts = await prisma.post.findMany();
return posts;
}
export default async function Page() {
const posts = await getPosts();
// ...
'Next.js Beta Docs 번역' 카테고리의 다른 글
[Data Fetching] (6) ErrorHandling Next.js Beta Docs (0) | 2023.05.03 |
---|---|
[Data Fetching] (5) LoadingUI Next.js Beta Docs (0) | 2023.05.02 |
[Data Fetching] (4) Linking and Navigating Next.js Beta Docs (0) | 2023.05.01 |
[Data Fetching] (3) Pages and Layouts Next.js Beta Docs (0) | 2023.05.01 |
[Data Fetching] (1) Fundamentals Next.js Beta Docs (0) | 2023.05.01 |