Next.js에서는 기능, 페이지 등 상황에 따라 CSR, SSR, SSG 등 데이터를 어떤 방법으로 가져올 지를 선택하는 것이 중요하다.
리액트는 CSR을 기본으로 데이터를 패칭하였다. 이는 페이지 로드 시 초기 HTML은 빈 상태로 제공되고, 브라우저에서 JS가 로드된 후 클라이언트에서 데이터를 가져와 렌더링한다. 하지만 이 방식은 서버에서 HTML이 미리 렌더링되지 않기 때문에 SEO에 불리할 수 있다. 또한 네트워크 요청 이후 화면에 데이터가 표시되기 때문에 초기 로딩이 느릴 수 있다.
반면 Next.js는 CSR, SSR, SSG 등 다양한 렌더링 방식을 제공한다. 따라서 SEO가 중요하고 초기 로딩 성능이 중요한 경우 SSR이나 SSG를, 실시간 데이터가 필요한 경우에는 CSR 등 상황에 따라 선택해서 사용해야 한다.
Next.js의 강점은 CSR, SSR, SSG, ISR을 모두 사용 가능하며, 이를 이용하여 SEO 향상 및 많은 장점들이 있는 것이다.
이번 포스팅에서는 CSR, SSR, SSG의 차이점을 이해하고, data fetching 방법, suspense, 에러 처리 등에 대해 알아보고자 한다.
01. CSR / SSR / SSG / ISR
#1. CSR (Client-Side Rendering)
기존의 React, Angualr, Vue와 같은 SPA 프레임워크들은 CSR 방식을 사용한다.
CSR은 useEffect를 이용해 데이터를 가져오는 평범한 방법이며, 각 페이지 요청마다 클라이언트 측에서 데이터를 가져온다.
페이지가 렌더링 된 후 데이터를 가져오는 함수가 실행된다.
CSR은 사용자가 사이트 접속 시 서버는 비어있는 HTML 문서를 넘겨준다. 클라이언트는 HTML에 링크된 JS 파일을 서버에 요청하고 서버는 해당 JS 파일을 클라이언트에게 전송한다. 클라이언트가 JS 파일을 받은 후에야 브라우저 화면에 웹 페이지가 뜨기 때문에, 사용자가 웹 페이지와 바로 상호작용 할 수 있다. 즉, CSR의 경우에는 사용자가 웹 사이트를 볼 수 있는 시간과 사용자가 웹 사이트와 상호작용을 할 수 있는 시간이 동일하다. 하지만 CSR은 다음과 같은 문제점들이 있다.
SEO에 적합하지 않다.
사용자가 첫 화면을 보기까지 로딩이 오래 걸린다.
#2. SSR (Server-Side Rendering)
SSR은 각 페이지 요청마다 서버측에서 데이터를 가져오는 함수를 실행한다.
페이지가 로드되기 전에 특별한 함수가 먼저 실행되고 잠깐의 딜레이 후에 페이지가 렌더링된다.
SSR에서는 웹사이트 접속 시 서버에서 필요한 데이터를 모두 가져와 HTML 파일과 이를 동적으로 제어할 수 있는 소스 코드를 생성하여 클라이언트에게 보낸다. 클라이언트는 만들어진 HTML 문서를 사용자에게 바로 보여주게 된다. 이러한 방식의 SSR은 페이지 로딩이 빨리지고, CSR과 달리 모든 콘텐츠가 HTML에 담겨있어 효율적인 SEO가 가능해졌다.
SSR은 사용자가 사이트 접속 시 서버는 이미 만들어진 HTML 문서를 클라이언트에게 넘겨주기 때문에 사용자는 화면을 바로 볼 수 있다. 하지만 아직 JS 파일은 받지 못한 상태이므로 바로 상호작용이 불가능하다. 즉, SSR의 경우 사용자가 웹 사이트를 볼 수 있는 시간과 사용자가 웹사이트와 상호작용을 할 수 있는 시간이 동일하지 않다.
SSR의 경우에는 웹사이트를 볼 수 있는 시간과 상호작용을 할 수 있는 시간의 차이를 줄이기 위한 방법이 필요하다.
이를 해결하기 위해 등장하는 것이 다음에 소개하는 SSG이다.
#3. SSG (Static Site Generation)
SSG는 어플리케이션이 빌드될 때 한 번 데이터를 가져오기 위해 특별한 함수를 실행한다.
미리 서버에 HTML 파일을 만들어두고, 이를 사용자에게 보여준다.
사용자와의 상호작용시 데이터가 변하지 않을 페이지는 미리 서버에서 HTML 파일을 만들어두고, 클라이언트의 요청이 있을 때마다 이 파일을 재사용하는 방식이다.
next build 명령어 입력 시 HTML 파일이 생성되고, CDN을 통해 캐싱된다.
SSR 처럼 pre-rendering 된 페이지를 보여주지만 변경 사항을 업데이트 할 수 없다. CSR, SSR 보다 렌더링 속도가 훨씬 빠르다.
SSG는 빌드 타이밍에 정적인 페이지를 미리 만들어두기 때문에 정적 페이지 제공 방식이라고 표현한다.
#4. ISR (Incremental Static Regeneration)
ISR은 기본적으로 SSG이지만 특정 시간과 특정 조건에 따라 해당 페이지만 리빌드하고 데이터를 가져오는 함수를 실행한다.
이는 다양한 렌더링 옵션을 제공하여 성능과 SEO를 상황에 맞춰 최적화할 수 있으며, 하이브리드 방식으로 페이지별로 다른 렌더링 방식을 사용할 수 있다.
02. Next.js 13 이후 변화 : 분리된 클라이언트 컴포넌트와 서버 컴포넌트
Next.js 13 버전부터 서버 컴포넌트와 클라이언트 컴포넌트가 분리되었다.
따라서 이전 버전과 비교하여 기존의 데이터 패칭 방식에 비해 큰 변화를 가져왔다.
Next.js 13 버전이 도입되기 이전에는 getStaticProps, getServerSideProps를 사용하여 페이지 단위로 렌더링 방식을 규정했다.
하지만 13버전 이상부터는 페이지 단위 렌더링 방식이 아닌 서버 컴포넌트 / 클라이언트 컴포넌트가 도입되어, 컴포넌트 단위로 렌더링 방식을 규정했다.
💡 클라이언트 컴포넌트에서의 Data Fetching
클라이언트 컴포넌트는 브라우저에서 렌더링되기 때문에 CSR 방식으로 데이터를 가져올 때 사용된다.
상호작용이 필요한 UI에 주로 사용되며, useEffect나 React Query 등을 활용해 데이터 패칭이 가능하다.
클라이언트 컴포넌트는 'use client'를 필수로 작성해야 하며, useEffect 등을 이용해 data fetching이 가능하다.
클라이언트 컴포넌트에서 Data fetching은 버튼 클릭, 폼 입력 등 사용자와 상호작용이 필요한 경우에 적합하다.
하지만 초기에 데이터를 기다리기 때문에 초기 로딩이 늦어질 수 있는 문제점이 있다.
💡 서버 컴포넌트에서의 Data Fetching
서버 컴포넌트는 기본적으로 서버에서 렌더링되며, 데이터 패칭 시점에서도 서버에서만 수행되기 때문에 클라이언트로 JS 번들을 전송하지 않는다. 만약 클라이언트 컴포넌트에서 데이터를 패칭하고 api를 연결할 경우 개발자 도구의 네트워크 탭에서 api가 호출되는 내역이 사용자에게 노출되어 보안 위험이 존재한다. 하지만 서버 컴포넌트에서 데이터를 패팅할 경우 서버에서만 수행되므로 사용자에게 노출될 위험이 없다.
서버 컴포넌트에서는 fetch, DB 쿼리 등을 직접 사용할 수 있다.
// 서버 컴포넌트 예시
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(res => res.json());
return <div>{data.title}</div>;
}
서버 컴포넌트에서 위와 같이 데이터를 패칭하면 HTML에 데이터가 포함되어 전달되기 때문에 SEO 측면에서 유리하다.
하지만 서버 컴포넌트에서 데이터 패칭은 상호작용이 필요하지 않은 경우에 사용하는 것이 좋다.
03. Next.js 13 : CSR로 Data Fetching
CSR 방식은 데이터를 클라이언트 측에서 패칭하여 렌더링하는 방식이다.
주로 useEffect나 React Query 등을 이용해 데이터를 가져온다.
useEffect 내부에서 데이터를 호출하며, return 내부에는 데이터 UI를 구성하여 보여준다.
SSG는 이미 만들어진 페이지로 바로 보여주기 때문에 속도가 빠르고 서버 부담이 적을 수 있다.
하지만, 동적인 콘텐츠들을 활용할 수 없고 정적이기 때문에 변화가 없는 고정 페이지에 사용해야 한다.
따라서 만약 새로운 데이터를 보여주려면 새로 빌드하고 배포해야 한다.
05. Next.js 13 : ISR로 Data Fetching
ISR은 SSG 방식을 사용하되 주기적으로 데이터를 새로 불러오기 위해서는 {next: {revalidate: time}} 옵션에 데이터를 새로 fetch 할 시간 간격을 설정해주면 된다. 아래 코드 예시는 60초마다 api에 데이터 요청을 하게 되므로, 그 사이에 데이터 변경 사항이 있었다면 별도의 빌드 과정이 없더라도 해당 데이터를 볼 수 있다.
한 fetch가 다른 fetch의 결과에 따라 달라지기 때문에 하나의 컴포넌트에서 fetch 전에 조건이 충족되기를 원하는 경우에는 해당 패턴을 사용할 수 있다. 하지만 잘못 사용하면 로딩 시간이 길어지는 문제점이 있다.
export default async function Page({
params: { username },
}: {
params: { username: string }
}) {
// Get artist information
const artist = await getArtist(username)
return (
<>
<h1>{artist.name}</h1>
{/* Show fallback UI while the Playlists component is loading */}
<Suspense fallback={<div>Loading...</div>}>
{/* Pass the artist ID to the Playlists component */}
<Playlists artistID={artist.id} />
</Suspense>
</>
)
}
async function Playlists({ artistID }: { artistID: string }) {
// Use the artist ID to fetch playlists
const playlists = await getArtistPlaylists(artistID)
return (
<ul>
{playlists.map((playlist) => (
<li key={playlist.id}>{playlist.name}</li>
))}
</ul>
)
}
먼저, await getArtist(username) 함수로 첫 번째 데이터 요청이 발생한다.
이때 중요한 점은 await을 사용하여 getArtist 호출이 완료될 때까지 기다린다.
따라서 PlayLists 컴포넌트는 getArtist 데이터 호출이 완료될 때까지 기다렸다가 이후에 렌더링된다.
만약 첫번째 데이터 패칭인 getArtist()가 20초가 소요되고, 두번째 컴포넌트가 0.5초가 소요된다고 가정하자.
이 경우에 순차 패턴이 적용되면 두번째 컴포넌트가 아무리 적게 시간이 소요되어도 데이터 패칭을 완료하는데 20.5초가 소요될 것이다.
#2. Parallel Data Fetching
앞선 순차 방식의 시간이 많이 걸리는 문제를 해결하기 위한 방법이 병렬 패턴으로 데이터를 패칭하는 것이다.
Next.js 13에서는 기본적으로 페이지와 레이아웃이 병렬로 렌더링된다. 하지만 컴포넌트 내에서 async/await을 사용하게 되면, 동일한 컴포넌트 내의 요청은 순차적으로 수행될 수 있다. 이를 해결하기 위해 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 getAlbums(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 }
}) {
const artistData = getArtist(username)
const albumsData = getAlbums(username)
// Initiate both requests in parallel
const [artist, albums] = await Promise.all([artistData, albumsData])
return (
<>
<h1>{artist.name}</h1>
<Albums list={albums} />
</>
)
}
Promise.all은 여러 비동기 요청을 병렬로 수행할 수 있도록 도와준다.
두 요청이 동시에 시작되고, 모든 요청이 완료될 때까지 기다린다.
이렇게 하면 두 요청이 순차적으로 완료되는 시간보다 짧아진다.
💡 Suspense 사용
Suspense는 컴포넌트가 비동기 작업을 처리할 때, 데이터가 로드될 때까지 사용자에게 대기 화면(로딩 스피너 등)을 보여줄 수 있도록 도와주는 기능이다. 특히 서버 컴포넌트에서 비동기 데이터를 가져올 때 유용하다.
import { Suspense } from "react";
import AlbumList from "./AlbumList";
import UserInfo from "./UserInfo";
export default function Page() {
return (
<div>
<h1>Music Page</h1>
<Suspense fallback={<div>Loading User Info...</div>}>
<UserInfo />
</Suspense>
<Suspense fallback={<div>Loading Album List...</div>}>
<AlbumList />
</Suspense>
</div>
);
}
UserInfo와 AlbumList는 각각 비동기 데이터를 가져오는 컴포넌트이다.
두 컴포넌트는 동시에 로드되며, 각각의 Suspense fallback UI가 표시된다. 데이터가 모두 로드되면 실제 컴포넌트가 렌더링된다.
Suspense를 사용하면 다음과 같은 장점이 있다.
사용자 경험 개선: 데이터 로딩 중 사용자에게 피드백을 줄 수 있어 빈 화면 대신 로딩 중임을 표시
코드 분할: React.lazy와 함께 사용하면 필요한 시점에만 컴포넌트를 로드하여 번들 크기를 줄일 수 있음
[Next.js] Data Fetching (Next.js 13 버전 이상)
Next.js에서는 기능, 페이지 등 상황에 따라 CSR, SSR, SSG 등 데이터를 어떤 방법으로 가져올 지를 선택하는 것이 중요하다.
리액트는 CSR을 기본으로 데이터를 패칭하였다. 이는 페이지 로드 시 초기 HTML은 빈 상태로 제공되고, 브라우저에서 JS가 로드된 후 클라이언트에서 데이터를 가져와 렌더링한다. 하지만 이 방식은 서버에서 HTML이 미리 렌더링되지 않기 때문에 SEO에 불리할 수 있다. 또한 네트워크 요청 이후 화면에 데이터가 표시되기 때문에 초기 로딩이 느릴 수 있다.
반면 Next.js는 CSR, SSR, SSG 등 다양한 렌더링 방식을 제공한다. 따라서 SEO가 중요하고 초기 로딩 성능이 중요한 경우 SSR이나 SSG를, 실시간 데이터가 필요한 경우에는 CSR 등 상황에 따라 선택해서 사용해야 한다.
Next.js의 강점은 CSR, SSR, SSG, ISR을 모두 사용 가능하며, 이를 이용하여 SEO 향상 및 많은 장점들이 있는 것이다.
이번 포스팅에서는 CSR, SSR, SSG의 차이점을 이해하고, data fetching 방법, suspense, 에러 처리 등에 대해 알아보고자 한다.
01. CSR / SSR / SSG / ISR
#1. CSR (Client-Side Rendering)
기존의 React, Angualr, Vue와 같은 SPA 프레임워크들은 CSR 방식을 사용한다.
CSR은 useEffect를 이용해 데이터를 가져오는 평범한 방법이며, 각 페이지 요청마다 클라이언트 측에서 데이터를 가져온다.
페이지가 렌더링 된 후 데이터를 가져오는 함수가 실행된다.
CSR은 사용자가 사이트 접속 시 서버는 비어있는 HTML 문서를 넘겨준다. 클라이언트는 HTML에 링크된 JS 파일을 서버에 요청하고 서버는 해당 JS 파일을 클라이언트에게 전송한다. 클라이언트가 JS 파일을 받은 후에야 브라우저 화면에 웹 페이지가 뜨기 때문에, 사용자가 웹 페이지와 바로 상호작용 할 수 있다. 즉, CSR의 경우에는 사용자가 웹 사이트를 볼 수 있는 시간과 사용자가 웹 사이트와 상호작용을 할 수 있는 시간이 동일하다. 하지만 CSR은 다음과 같은 문제점들이 있다.
#2. SSR (Server-Side Rendering)
SSR은 각 페이지 요청마다 서버측에서 데이터를 가져오는 함수를 실행한다.
페이지가 로드되기 전에 특별한 함수가 먼저 실행되고 잠깐의 딜레이 후에 페이지가 렌더링된다.
SSR에서는 웹사이트 접속 시 서버에서 필요한 데이터를 모두 가져와 HTML 파일과 이를 동적으로 제어할 수 있는 소스 코드를 생성하여 클라이언트에게 보낸다. 클라이언트는 만들어진 HTML 문서를 사용자에게 바로 보여주게 된다. 이러한 방식의 SSR은 페이지 로딩이 빨리지고, CSR과 달리 모든 콘텐츠가 HTML에 담겨있어 효율적인 SEO가 가능해졌다.
SSR은 사용자가 사이트 접속 시 서버는 이미 만들어진 HTML 문서를 클라이언트에게 넘겨주기 때문에 사용자는 화면을 바로 볼 수 있다. 하지만 아직 JS 파일은 받지 못한 상태이므로 바로 상호작용이 불가능하다. 즉, SSR의 경우 사용자가 웹 사이트를 볼 수 있는 시간과 사용자가 웹사이트와 상호작용을 할 수 있는 시간이 동일하지 않다.
SSR의 경우에는 웹사이트를 볼 수 있는 시간과 상호작용을 할 수 있는 시간의 차이를 줄이기 위한 방법이 필요하다.
이를 해결하기 위해 등장하는 것이 다음에 소개하는 SSG이다.
#3. SSG (Static Site Generation)
SSG는 어플리케이션이 빌드될 때 한 번 데이터를 가져오기 위해 특별한 함수를 실행한다.
미리 서버에 HTML 파일을 만들어두고, 이를 사용자에게 보여준다.
사용자와의 상호작용시 데이터가 변하지 않을 페이지는 미리 서버에서 HTML 파일을 만들어두고, 클라이언트의 요청이 있을 때마다 이 파일을 재사용하는 방식이다.
next build 명령어 입력 시 HTML 파일이 생성되고, CDN을 통해 캐싱된다.
SSR 처럼 pre-rendering 된 페이지를 보여주지만 변경 사항을 업데이트 할 수 없다. CSR, SSR 보다 렌더링 속도가 훨씬 빠르다.
#4. ISR (Incremental Static Regeneration)
ISR은 기본적으로 SSG이지만 특정 시간과 특정 조건에 따라 해당 페이지만 리빌드하고 데이터를 가져오는 함수를 실행한다.
이는 다양한 렌더링 옵션을 제공하여 성능과 SEO를 상황에 맞춰 최적화할 수 있으며, 하이브리드 방식으로 페이지별로 다른 렌더링 방식을 사용할 수 있다.
02. Next.js 13 이후 변화 : 분리된 클라이언트 컴포넌트와 서버 컴포넌트
Next.js 13 버전부터 서버 컴포넌트와 클라이언트 컴포넌트가 분리되었다.
따라서 이전 버전과 비교하여 기존의 데이터 패칭 방식에 비해 큰 변화를 가져왔다.
Next.js 13 버전이 도입되기 이전에는 getStaticProps, getServerSideProps를 사용하여 페이지 단위로 렌더링 방식을 규정했다.
하지만 13버전 이상부터는 페이지 단위 렌더링 방식이 아닌 서버 컴포넌트 / 클라이언트 컴포넌트가 도입되어, 컴포넌트 단위로 렌더링 방식을 규정했다.
💡 클라이언트 컴포넌트에서의 Data Fetching
클라이언트 컴포넌트는 브라우저에서 렌더링되기 때문에 CSR 방식으로 데이터를 가져올 때 사용된다.
상호작용이 필요한 UI에 주로 사용되며, useEffect나 React Query 등을 활용해 데이터 패칭이 가능하다.
클라이언트 컴포넌트는 'use client'를 필수로 작성해야 하며, useEffect 등을 이용해 data fetching이 가능하다.
클라이언트 컴포넌트에서 Data fetching은 버튼 클릭, 폼 입력 등 사용자와 상호작용이 필요한 경우에 적합하다.
하지만 초기에 데이터를 기다리기 때문에 초기 로딩이 늦어질 수 있는 문제점이 있다.
💡 서버 컴포넌트에서의 Data Fetching
서버 컴포넌트는 기본적으로 서버에서 렌더링되며, 데이터 패칭 시점에서도 서버에서만 수행되기 때문에 클라이언트로 JS 번들을 전송하지 않는다. 만약 클라이언트 컴포넌트에서 데이터를 패칭하고 api를 연결할 경우 개발자 도구의 네트워크 탭에서 api가 호출되는 내역이 사용자에게 노출되어 보안 위험이 존재한다. 하지만 서버 컴포넌트에서 데이터를 패팅할 경우 서버에서만 수행되므로 사용자에게 노출될 위험이 없다.
서버 컴포넌트에서는 fetch, DB 쿼리 등을 직접 사용할 수 있다.
서버 컴포넌트에서 위와 같이 데이터를 패칭하면 HTML에 데이터가 포함되어 전달되기 때문에 SEO 측면에서 유리하다.
하지만 서버 컴포넌트에서 데이터 패칭은 상호작용이 필요하지 않은 경우에 사용하는 것이 좋다.
03. Next.js 13 : CSR로 Data Fetching
CSR 방식은 데이터를 클라이언트 측에서 패칭하여 렌더링하는 방식이다.
주로 useEffect나 React Query 등을 이용해 데이터를 가져온다.
useEffect 내부에서 데이터를 호출하며, return 내부에는 데이터 UI를 구성하여 보여준다.
CSR은 동적으로 변화하는 콘텐츠들을 실시간으로 빠르게 변화시켜 줄 때 사용하는 것이 적합하다.
하지만 페이지 초기 로딩 시 불러오는 데이터에 따라 로딩이 느릴 수 도 있으며, 새로고침 시 페이지 화면이 깜빡거리는 현상이 발생할 수 있다. 또한 SEO에 취약하다.
CSR 방식으로 데이터를 패칭했을 때 개발자 도구의 네트워크 탭을 확인하면 위와 같다.
사용자는 네트워크 탭에서 어떤 api가 호출되었는지 알 수 있다. 만약 데이터베이스와 통신한다면 사용자에게 데이터베이스의 비밀번호가 노출될 수 있는 상황도 발생할 수 있는 것이다.
따라서 이 방식은 보안성 측면에서도 좋은 방법이 아닐 것이다.
04. Next.js 13 : SSR로 Data Fetching
SSR 방식은 데이터를 Fetch 할 때 "no-store" 옵션으로 모든 요청에서 최신 데이터를 받아올 수 있다.
이는 Next.js 12 이하 버전에서 getServerSideProps와 유사하다.
SSR은 서버에서 데이터 패칭을 하고 만들어진 페이지로 보여줄 수 있어서 취약했던 SEO를 해결할 수 있다.
하지만 서버 부하가 커질 수 있으며, TTV(Time To View)와 TTI(Time To Interact) 사이에 틈이 생길 수 있다.
또한 매 요청마다 서버에서 데이터를 가져오기 때문에 느릴 수 있다.
SSR로 데이터를 패칭했을 때 네트워크 탭 결과이다.
SSR은 서버 쪽에서 데이터를 패칭하였기 때문에 네트워크 탭에서 호출한 api가 노출되지 않는다.
04. Next.js 13 : SSG로 Data Fetching
SSG는 데이터를 패칭할 때 "force-cache" 옵션을 사용하여 구현할 수 있다.
cache: "force-cache"는 default 값이기 때문에 생략 가능하다.
force-cache는 캐싱을 강제하여 정적 사이트로 만들어주고, 처음에는 서버 사이드에서 작동하고 두번째 부터는 캐싱된 값을 불러온다.
이는 빌드 타임에 한 번 만들어진 페이지가 영구적으로 캐시된다.
SSG는 이미 만들어진 페이지로 바로 보여주기 때문에 속도가 빠르고 서버 부담이 적을 수 있다.
하지만, 동적인 콘텐츠들을 활용할 수 없고 정적이기 때문에 변화가 없는 고정 페이지에 사용해야 한다.
따라서 만약 새로운 데이터를 보여주려면 새로 빌드하고 배포해야 한다.
05. Next.js 13 : ISR로 Data Fetching
ISR은 SSG 방식을 사용하되 주기적으로 데이터를 새로 불러오기 위해서는 {next: {revalidate: time}} 옵션에 데이터를 새로 fetch 할 시간 간격을 설정해주면 된다. 아래 코드 예시는 60초마다 api에 데이터 요청을 하게 되므로, 그 사이에 데이터 변경 사항이 있었다면 별도의 빌드 과정이 없더라도 해당 데이터를 볼 수 있다.
06. Data Fetching Patterns
#1. Sequential Data Fetching
Sequential Data Fetching은 요청이 서로 종속되어 순차적으로 수행된다.
한 fetch가 다른 fetch의 결과에 따라 달라지기 때문에 하나의 컴포넌트에서 fetch 전에 조건이 충족되기를 원하는 경우에는 해당 패턴을 사용할 수 있다. 하지만 잘못 사용하면 로딩 시간이 길어지는 문제점이 있다.
먼저, await getArtist(username) 함수로 첫 번째 데이터 요청이 발생한다.
이때 중요한 점은 await을 사용하여 getArtist 호출이 완료될 때까지 기다린다.
따라서 PlayLists 컴포넌트는 getArtist 데이터 호출이 완료될 때까지 기다렸다가 이후에 렌더링된다.
만약 첫번째 데이터 패칭인 getArtist()가 20초가 소요되고, 두번째 컴포넌트가 0.5초가 소요된다고 가정하자.
이 경우에 순차 패턴이 적용되면 두번째 컴포넌트가 아무리 적게 시간이 소요되어도 데이터 패칭을 완료하는데 20.5초가 소요될 것이다.
#2. Parallel Data Fetching
앞선 순차 방식의 시간이 많이 걸리는 문제를 해결하기 위한 방법이 병렬 패턴으로 데이터를 패칭하는 것이다.
Next.js 13에서는 기본적으로 페이지와 레이아웃이 병렬로 렌더링된다. 하지만 컴포넌트 내에서 async/await을 사용하게 되면, 동일한 컴포넌트 내의 요청은 순차적으로 수행될 수 있다. 이를 해결하기 위해 Promise.all을 사용하여 병렬로 데이터를 패칭할 수 있다.
Promise.all은 여러 비동기 요청을 병렬로 수행할 수 있도록 도와준다.
두 요청이 동시에 시작되고, 모든 요청이 완료될 때까지 기다린다.
이렇게 하면 두 요청이 순차적으로 완료되는 시간보다 짧아진다.
💡 Suspense 사용
Suspense는 컴포넌트가 비동기 작업을 처리할 때, 데이터가 로드될 때까지 사용자에게 대기 화면(로딩 스피너 등)을 보여줄 수 있도록 도와주는 기능이다. 특히 서버 컴포넌트에서 비동기 데이터를 가져올 때 유용하다.
UserInfo와 AlbumList는 각각 비동기 데이터를 가져오는 컴포넌트이다.
두 컴포넌트는 동시에 로드되며, 각각의 Suspense fallback UI가 표시된다. 데이터가 모두 로드되면 실제 컴포넌트가 렌더링된다.
Suspense를 사용하면 다음과 같은 장점이 있다.
References
https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#sequential-data-fetching
Data Fetching: Data Fetching and Caching | Next.js
Learn best practices for fetching data on the server or client in Next.js.
nextjs.org
https://velog.io/@hamjw0122/Next.js-Next.js%EC%97%90%EC%84%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-Fetching%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95
[Next.js] Next.js에서 Data Fetching하기
🔎 Next.js에서는 어떤 방식으로 Data-Fetching을 할까?
velog.io
'Front-End > Next.js' 카테고리의 다른 글