App Router - data fetching

June 11, 2023

    Nextjs

Fetching Data on the Server

data fetching은 서버에서 이루어지는 것이 성능면에서 좋기 때문에 서버 컴포넌트에서 이루어 지는 것을 권장된다. 서버 컴포넌트에서 데이터를 fetching하면 아래와 같은 이점들이 있다.

  • 클라이언트가 민감한 정보를 관리할 필요가 없어 보안에 유리
  • 동일 fetching에 대해 서버가 자동으로 캐싱 데이터를 제공
  • Database위치에 따라 latancy를 줄일 수 있고 성능 개선 여지가 있음
  • waterfall을 줄일 수 있음

layout에서 fetching한 데이터는 자식 컴포넌트로 전달해 줄 수 없다. 그럼에도 불구하고 nextjs는 layout에서 필요한 data fetching을 수행하기를 권장하는데 이는 nextjs가 중복 요청을 없애주고 data를 캐싱해 주기 때문이다.

Parallel and Sequential Data Fetching

데이터 패칭이 컴포넌트에서 일어날 때 waterfall이 발생한다. 이는 초기 렌더링 시간을 불필요하게 늘리는 일이므로 여러 데이터 패칭은 병렬적으로 이뤄지는 것이 좋다.

waterfall을 피하고 병렬 요청을 하기 위해서 Promise.all을 사용 한다.

import Albums from './albums' async function getArtist(username: string) { const res = await fetch(`https://api.example.com/artist/${username}`) return res.json() } async function getArtistAlbums(username: string) { const res = await fetch(`https://api.example.com/artist/${username}/albums`) return res.json() } export default async function Page({ params: { username }, }: { params: { username: string } }) { // 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> </> ) }

SSR, SSG, ISR

이전 글에서 App router를 선택 시 기본 값으로 모든 컴포넌트가 SSR이 된다고 했다. page router에서는 getserversideprops/getstaticprops로 렌더링 방식을 페이지 단위로 결정했지만 App router에서는 fetch()가 그 역할을 대신한다.

SSG

기본적으로 fetch()는 static fetch로 설정 되어 있다.이말은 빌드 시점에서 데이터를 가지고 와서 캐싱되고, 같은 요청에 대해 이를 재사용한다는 말이다. (Static site처럼 행동)

fetch('https://...') // cache: 'force-cache' is the default

따라서 개발자는 필요에 따라 데이터의 revalidate를 설정하거나 데이터가 캐싱되지 않게 설정해서 데이터 업데이트를 관리해야 한다.

ISR

fetch()에 revalidate를 설정하여 일정 시간마다 데이터를 업데이트 하면 해당 컴포넌트는 ISR이 된다.

fetch('https://...', { next: { revalidate: 10 } })

위 코드는 다음 10초 후에 백그라운드에서 다시 데이터 요청을 한다

SSR

매 요청마다 새로운 데이터 요청을 보내려면 데이터를 캐시하지 않는 옵션을 사용해야 한다.

fetch('https://...', { cache: 'no-store' })

이제 데이터는 캐싱되지 않고 컴포넌트는 SSR이 된다.

Data Caching

위에서 fetch()를 사용하면 서버에서 자동으로 데이터를 캐싱해서 재사용 하므로 각 컴포넌트마다 데이터 패칭 함수를 재사용 하더라도 서버에서 자동으로 중복 요청을 제거하고 캐싱된 데이터를 사용했다. (nextjs에서 권장하는 방식) 하지만 fetch() api는 로컬 경로에 있는 데이터를 가지고 오지 못해, 로컬에 json으로 저장된 데이터를 가지고 오려면 react cache를 사용해야 한다.

// utils/getUser.ts import { cache } from 'react' export const getUser = cache(async (id: string) => { const user = await db.user.findUnique({ id }) return user }) // app/user/[id]/layout.tsx import { getUser } from '@utils/getUser' export default async function UserLayout({ params: { id } }) { const user = await getUser(id) // ... }