SSG 방식으로 pre-rendering을 진행하는 경우 "빌드를 할 때만" HTML 문서를 생성하고 이후 따로 빌드를 명시적으로 하지 않는 이상 동일한 HTML 문서를 계속해서 사용하게 됩니다.
이는 서버의 데이터를 사용하는 경우 서버 데이터가 변경되었다면 변경된 데이터를 갖지 않고 기존 데이터를 갖게 되는 문제점이 있습니다. 이를 해결하기 위해서 매번 빌드를 명시적으로 하는 것은 매우 비효율적입니다.
이 문제를 해결하기 위해서 getStaticProps
함수가 반환하는 객체에 "revalidate 프로퍼티"를 통해 해결할 수 있었습니다.
앞에서 살펴본 getStaticProps
를 revalidate로 갱신할 주기를 설정하더라도 사용자게 더 정확한 정보를 제공해야한다면 이는 적절하지 않을 수 있습니다.
이를 해결하기 위해서 Server-side Rendering 방식의 pre-rendering을 사용할 수 있습니다. SSR 방식을 사용하기 위해서는 페이지 컴포넌트 파일 내 getServerSideProps
비동기 함수를 export 해주어야 합니다.
전체적인 구조는 getStaticProps
함수와 유사합니다. getStaticProps
와 getServerSideProps
모두 "페이지 컴포넌트에게 초기 렌더링 데이터를 props로 전달"하기 위해서 사용되는 함수입니다. 그러므로 한 페이지 컴포넌트 파일 내 두 함수를 모두 export할 수 없습니다.
단지 둘의 차이점은 실행되는 시점에 차이점이 존재하며, 전달받는 인수나 반환값에도 약간의 차이가 존재합니다.
SSR 방식을 사용하는 페이지를 요청한 경우 pre-rendering은 서버가 요청을 받은 후 실행됩니다.
즉, 요청을 받은 뒤에 HTML 문서와 getServerSideProps
함수의 결과를 나타내는 JSON 파일을 클라이언트에게 전달해줍니다.
export getServerSideRendering(context) {
// fething data && backend api,,,
context.params;
context.req;
context.res;
return {
props: {
key: value,
,,,
},
notFound: false | true,
redirect: {
destination: '/경로'
}
};
};
getServerSidePorps
함수가 인수로 전달받는 context 객체는 다음과 같은 구조를 갖고 있습니다.
"context.req"
: Node.js의 http.incomingMessage 객체가 바인딩되어 있습니다.
"context.res"
: Node.js의 http.serverResponse 객체가 바인딩되어 있습니다.
"context.params"
: 동적 페이지의 경우 경로 파라미터와 경로값을 프로퍼티로 갖는 객체가 존재
getServerSideProps
함수의 반환값으로는 객체를 작성해야하며 객체는 아래와 같은 프로퍼티를 작성할 수 있습니다.
"props" 어트리뷰트
: 페이지 컴포넌트에게 전달할 props 객체. 작성된 프로퍼티는 컴포넌트에게 그대로 prop으로 전달됩니다.
실제로는 props 프로퍼티에 바인딩된 객체도 _app.js의 MyApp 컴포넌트가 전달받는 props.pagesProps 값이 됩니다.
"notFound" 어트리뷰트
: 불리언 값을 작성하며, true 작성시 404 에러 페이지를 전달합니다.
"redirect" 어트리뷰트
: 객체를 작성하며 객체의 destination 프로퍼티에 리다리렉팅될 경로값을 문자열로 작성시 해당 경로로 리다이렉팅됩니다.
서버측에서는 요청을 받을 때마다 getServerSideProps
함수를 실행하여 초기 렌더링 데이터를 페이지 컴포넌트에게 전달하여 실행한 결과를 HTML 문서의 콘텐츠로 포함하여 새롭게 생성하게 됩니다.
서버측에서는 새롭게 생성한 HTML 문서와 JSON 파일을 클라이언트에게 전달합니다.
클라이언트측에서는 전달받은 HTML 문서 즉시 렌더링하고, Hydrate 과정에서 실행되는 페이지 컴포넌트에게는 JSON 파일을 props 객체로 하여 실행합니다.
서버측에서는 해당 페이지에 접근할 때마다 getServerSideProps
함수를 실행하여 그 결과를 JSON 파일로 클라이언트에게 전달합니다.
클라이언트측에서는 JSON 파일을 props 객체로 전달하여 페이지 컴포넌트 실행하고 화면을 리렌더링하게 됩니다.
동적 페이지를 SSR 방식으로 사용하는 경우에는 따로 getStaticPaths
함수로 경로 파라미터 값을 작성하지 않아도 됩니다.
SSR 방식은 서버가 요청을 받을 때마다 getServerSideProps
함수를 실행하기 때문에 해당 시점에는 경로 파라미터 값을 서버가 알고 있기 때문에 따로 지정하지 않아도 됩니다.
또한 pre-rendering시 실행되는 페이지 컴포넌트 함수나 Hydrate시 실행되는 페이지 컴포넌트 함수들은 query 객체가 빈 객체로 실행되지 않습니다. query 객체는 경로 파라미터에 대한 정보를 가진 채로 실행됩니다.
즉, SSR 방식을 사용하는 동적 페이지의 경우 더이상 빈 query 객체에 대한 대비를 하지 않아도 됩니다.
페이지 컴포넌트 함수가 실행될 때 언제나 query 객체는 경로 파라미터에 대한 정보를 가진 상태로 실행됩니다.
서버측에서는 요청을 받을 때마다 getServerSideProps
함수를 실행하여 초기 렌더링 데이터를 페이지 컴포넌트에게 전달하여 실행한 결과를 HTML 문서의 콘텐츠로 포함하여 새롭게 생성하게 됩니다.
이때 pre-rendering을 위해 실행하는 페이지 컴포넌트 함수 내 query 객체는 경로 파라미터에 대한 정보를 가진 채로 실행합니다.
서버측에는 새롭게 생성한 HTML 문서와 JSON 파일을 클라이언트에게 전달합니다.
클라이언트측에서는 HTML 문서를 즉시 렌더링하고, Hydrate 과정시 실행되는 페이지 컴포넌트 함수에게 JSON 파일을 props 객체로 하여 실행합니다.
Hydrate 과정시 실행되는 페이지 컴포넌트 함수 내 query 객체 또한 경로 파라미터에 대한 정보를 가진 채로 실행됩니다.
서버측에서는 해당 페이지에 접근할 때마다 getServerSide
함수를 실행한 결과를 JSON 파일로 생성하여 이를 클라이언트측에 전달합니다.
클라이언트측에서는 JSON 파일을 props 객체로 사용하여 페이지 컴포넌트 함수를 실행하여 리렌더링을 합니다.
이때 실행되는 페이지 컴포넌트 함수 내 query 객체 또한 경로 파라미터에 대한 정보를 가진 채로 실행됩니다.
빈 query 객체와 빈 props 객체로 컴포넌트 실행에 영향을 끼치는 경우는 아래와 같습니다.
아래 경우에서 모두 빈 query 객체와 빈 props 객체에 대해서 대비를 해주어야 합니다.
Static 방식 동적 페이지 요청시
: Hydrate 과정시 실행되는 페이지 컴포넌트 함수는 query가 빈 객체인 상태로 실행됩니다.
SSG 방식 paths에 작성되지 않은 동적 페이지 요청시, fallback이 true인 경우
: Hydrate 과정시 실행되는 페이지 컴포넌트 함수는 query와 props가 빈 객체인 상태로 실행됩니다.
이렇게 Hydrate 과정에서 props, query가 빈 객체인 상태로 실행되는 경우 이후 NextJS가 해당 페이지 컴포넌트 함수를 한번 더 실행하며 이때 실행될 때는 props와 query가 빈 객체가 아닌 상태로 실행됩니다.
앞에서 살펴본 getStaticProps
와 getServerSideProps
함수를 사용하여 서버측에서 미리 렌더링에 필요한 데이터를 가져오는 방식(Server-side Data Fetching)을 사용할 수 있었습니다.
하지만 서버측에서 데이터를 미리 가져오는 것이 마냥 좋은 방법이 아니며 각 상황에 맞게 사용하는 것이 중요합니다.
아래와 같은 상황일 때는 서버측에서 데이터를 미리 가져오는것 보다는 클라이언트측에서 서버에게 보내 데이터를 가져오는 것(Client-side Data Fetching)이 더 나은 상황입니다.
주식과 같이 데이터가 몇초마다 여러 차례 변경된다면 서버측에서 pre-fetching과 pre-rendering하는 의미가 없습니다. 이미 사전에 만든 HTML 문서는 과거의 페이지가 되어있기 때문입니다.
이런 경우 페이지에 방문했을 때 클라이언트측에서 useEffect
훅을 사용하여 HTTP 요청을 보내 최신 데이터를 가져와 업데이트하는 것이 사용자 입장에서도 좋을 것입니다.
쇼핑몰의 최근 주문 내역이나 장바구니와 같은 데이터들, 또는 로그인해야지만 열람 가능한 특정 데이터들의 경우 페이지를 사전에 생성할 필요가 없습니다.