본문으로 건너뛰기

사전 렌더링과 데이터 가져오기

사전 렌더링

기본적으로 넥스트는 모든 페이지를 미리 렌더링합니다. 넥스트는 클라이언트 측 자바스크립트로 모든 작업을 수행하는 대신 각 페이지에 대해 미리 HTML을 생성합니다. 사전 렌더링은 더 나은 성능과 SEO를 제공합니다.

생성된 각 HTML은 해당 페이지에 필요한 최소한의 자바스크립트 코드와 연결됩니다. 브라우저에서 페이지를 로드하면 해당 자바스크립트 코드가 실행되고 페이지가 완전히 상호 작용이 가능하게 만들어집니다. 이 과정을 수화(hydration)라고 합니다.

다음 그림들은 사전 렌더링이 있을 때와 없을 때를 설명합니다.

두 가지 형태의 사전 렌더링

넥스트에는 정적 생성(Static Generation)과 SSR(Server-Side Rendering)의 두 가지 사전 렌더링 방식이 있습니다. 둘의 차이점은 페이지에 대한 HTML을 생성하는 시점입니다.

  • 정적 생성빌드 시점에서 HTML을 생성하는 사전 렌더링 방식입니다. 사전 렌더링된 HTML은 각 요청에서 재사용됩니다.
  • SSR각 요청에 대해 HTML을 생성하는 사전 렌더링 방식입니다.
참고

npm run devyarn dev로 실행하는 개발 모드에서는 정적 생성 페이지를 비롯한 모든 페이지가 각 요청에 대해 사전 렌더링됩니다.

페이지 단위 기준

넥스트를 사용하면 각 페이지에 사용할 사전 렌더링 방식을 선택할 수 있습니다. 대부분의 페이지에는 정적 생성을 사용하고 일부 페이지에는 SSR을 사용하여 하이브리드 넥스트 앱을 만들 수 있습니다.

정적 생성과 SSR은 언제 사용해야 하나요

정적 생성을 사용하면 페이지를 한 번만 만들고 CDN에서 제공할 수 있으므로 모든 요청에 대해 서버가 페이지를 렌더링하도록 하는 것보다 훨씬 빠릅니다. 따라서 가능하면 정적 생성을 사용하는 것이 좋습니다.

다음을 포함하여 다양한 유형의 페이지에 대해 정적 생성을 사용할 수 있습니다 .

  • 마케팅 페이지
  • 블로그 게시물
  • 전자상거래 제품 목록
  • 도움말 및 문서

"사용자의 요청에 앞서 해당 페이지를 미리 렌더링할 수 있나요?"라는 질문에 대한 대답이 "예"인 경우에는 정적 생성을 선택합니다.

반면 사용자의 요청보다 먼저 페이지를 미리 렌더링할 수 없다면 정적 생성은 좋은 생각이 아닙니다. 페이지에 자주 갱신되는 데이터가 표시되고 요청마다 페이지 콘텐츠가 변경된다면 SSR을 사용할 수 있습니다. 속도는 느려지지만 미리 렌더링된 페이지는 항상 최신 상태로 유지됩니다. 또는 사전 렌더링을 건너뛰고 클라이언트 측 자바스크립트(CSR)를 사용하여 자주 갱신되는 데이터를 채울 수 있습니다.

데이터가 없는 정적 생성과 데이터가 있는 정적 생성

정적 생성은 데이터 유무에 관계없이 수행할 수 있습니다.

외부 데이터를 가져올 필요가 없는 페이지는 앱이 프로덕션용으로 빌드될 때 자동으로 정적으로 생성됩니다.

그러나 일부 페이지의 경우에는 먼저 외부 데이터를 가져오고 HTML을 렌더링해야 합니다. 빌드 타임에 파일 시스템에 접근하거나, 외부 API를 가져오거나, 데이터베이스에 질의해야 할 수도 있습니다. 넥스트는 데이터가 있는 정적 생성을 기본으로 지원합니다.

getStaticProps를 이용한 데이터가 있는 정적 생성

넥스트에서 페이지 컴포넌트를 내보낼 때 getStaticProps라는 async 함수를 내보낼 수 있습니다. 이 함수를 내보내면 다음과 같이 됩니다.

  • getStaticProps가 프로덕션의 빌드 타임에 실행됨
  • 함수 내부에서 외부 데이터를 가져와서 페이지에 프롭으로 전달할 수 있음
jsx
export default function Home(props) { ... }
export async function getStaticProps() {
// 파일 시스템, API, DB 등에서 외부 데이터를 가져옵니다.
const data = ...
// props 키의 값은 Home 컴포넌트로 전달될 것입니다.
return {
props: ...
}
}
jsx
export default function Home(props) { ... }
export async function getStaticProps() {
// 파일 시스템, API, DB 등에서 외부 데이터를 가져옵니다.
const data = ...
// props 키의 값은 Home 컴포넌트로 전달될 것입니다.
return {
props: ...
}
}

기본적으로 getStaticProps는 넥스트에게 다음과 같이 지시하는 것입니다. "이 페이지에는 데이터 종속성이 있습니다. 따라서 빌드 타임에 이 페이지를 사전 렌더링할 때 이 문제를 먼저 해결해야 합니다."

참고

개발 모드에서는 getStaticProps가 각 요청마다 실행됩니다.

외부 API나 데이터베이스 질의로 데이터 가져오기

파일 시스템은 물론, 외부 API 엔드포인트와 같은 다른 소스에서 데이터를 가져올 수 있습니다.

jsx
export async function getSortedPostsData() {
const res = await fetch('..');
return res.json();
}
jsx
export async function getSortedPostsData() {
const res = await fetch('..');
return res.json();
}
참고

넥스트는 클라이언트와 서버 모두에서 fetch()을 폴리필합니다. 별도로 가져올 필요가 없습니다.

데이터베이스를 직접 질의할 수도 있습니다.

jsx
import someDatabaseSDK from 'someDatabaseSDK'
const databaseClient = someDatabaseSDK.createClient(...)
export async function getSortedPostsData() {
return databaseClient.query('SELECT posts...')
}
jsx
import someDatabaseSDK from 'someDatabaseSDK'
const databaseClient = someDatabaseSDK.createClient(...)
export async function getSortedPostsData() {
return databaseClient.query('SELECT posts...')
}

이것은 getStaticProps서버 측에서만 실행되기 때문에 가능한 것입니다.

getStaticProps는 클라이언트 측에서 실행되지 않습니다. 브라우저용 JS 번들에 포함조차 되지 않습니다. 따라서 데이터베이스 질의와 같은 코드를 브라우저로 보내지 않고도 직접 작성할 수 있습니다.

개발 vs 프로덕션

  • npm run devyarn dev로 실행되는 개발에서는 getStaticProps가 요청마다 실행됩니다.
  • 프로덕션에서는 getStaticProps가 빌드 타임에 실행됩니다. 이 작동은 getStaticPaths에서 반환되는 fallback 키를 이용해 개선될 수 있습니다.

getStaticProps는 빌드 타임에 실행되므로 질의 매개변수나 HTTP 헤더와 같이 요청 타임에만 사용할 수 있는 데이터는 사용할 수 없습니다.

페이지에서만 사용 가능

getStaticProps는 페이지에서만 내보낼 수 있습니다. 페이지가 아닌 파일에서는 내보낼 수 없습니다.

이런 제한이 있는 이유 중 하나는 페이지가 렌더링되기 전에 리액트에 필요한 모든 데이터가 준비되어야 하기 때문입니다.

요청 타임에 데이터를 가져와야 하는 경우

사용자의 요청에 앞서 페이지를 미리 렌더링할 수 없는 경우에는 정적 생성이 부적절합니다.

페이지에 자주 갱신되는 데이터가 표시되고 요청마다 페이지 콘텐츠가 변경되는 경우가 존재할 수 있습니다. 이때는 SSR을 사용하거나 사전 렌더링을 건너뛸 수 있습니다.

요청 타임에 데이터 가져오기

빌드 타임이 아닌 요청 타임에 데이터를 가져와야 하는 경우에는 SSR을 이용할 수 있습니다.

SSR을 이용하려면 페이지에서 getStaticProps 대신 getServerSideProps을 내보내야 합니다.

getServerSideProps 사용하기

다음은 getServerSideProps의 시작 코드입니다.

jsx
export async function getServerSideProps(context) {
return {
props: {
// 컴포넌트에게 전달될 프롭
},
};
}
jsx
export async function getServerSideProps(context) {
return {
props: {
// 컴포넌트에게 전달될 프롭
},
};
}

getServerSideProps는 요청 타임에 호출되기 때문에 해당 매개변수 context에는 요청 관련 매개변수가 포함됩니다.

요청 타임에 데이터를 가져와야 하는 페이지를 사전 렌더링해야 하는 경우에만 getServerSideProps을 사용해야 합니다.

getServerSideProps의 첫 바이트까지의 시간(TTFB)은 getStaticProps보다 느릴 것입니다. 서버가 매 요청마다 결과를 계산해야 하고 별도의 설정 없이는 CDN에서 결과를 캐시할 수 없기 때문입니다.

CSR

데이터를 미리 렌더링할 필요가 없다면 CSR이라고 불리는 다음 전략을 사용할 수 있습니다.

  • 페이지에서 외부 데이터가 필요하지 않은 부분을 정적으로 생성(사전 렌더링)함
  • 페이지가 로드되면 자바스크립트를 사용하여 클라이언트에서 외부 데이터를 가져오고 나머지 부분을 채움

이 접근 방식의 적절한 예시로는 사용자 대시보드 페이지가 있습니다. 대시보드는 사용자별 비공개 페이지이므로 SEO와 관련이 없으며 페이지를 미리 렌더링 할 필요가 없습니다. 데이터는 자주 갱신되므로 요청 타임의 데이터 가져오기가 필요합니다.

SWR

넥스트 뒤에 있는 팀은 데이터 가져오기를 위한 리액트 훅, SWR을 만들었습니다. 클라이언트 측에서 데이터를 가져오는 경우에 적극 권장합니다. 캐싱, 재검증, 초점 추적, 일정 시간마다 다시 가져오기 등을 처리합니다.

다음은 간단한 사용 예시입니다.

jsx
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetch);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}
jsx
import useSWR from 'swr';
function Profile() {
const { data, error } = useSWR('/api/user', fetch);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>hello {data.name}!</div>;
}

자세한 내용은 SWR에서 확인하세요.