getInitialProps, getServerSideProps, getStaticProps 전부 Server Side Rendering(SSR)을 위해 필요한 함수들이다. SSR은 서버에서 미리 데이터를 채운 페이지를 브라우저에 보여준다. 이는 특히 SEO 구현에 유용하다.
이번 시간은 getInitalProp와 getServerSideProps에 대해 알아보자.
각각의 렌더링 유형은 props를 페이지 컴포넌트로 전달된다. 초기 HTML가 내려왔을 때, 클라이언트 측에서 이 props 데이터를 확인 할 수 있다. 이는 페이지에서 적절한 hydrate를 위함이다.
그러므로 클라이언트에서 사용하면 안되는 민감한 정보(secret key등)를 props로 전달하면 안 된다.
getInitialProps
사실 getInitialProps는 Automatic Static Optimization을 사용하지 못하기 때문에 권장하지 않는다. getInitialProps는 비동기로 동작하며 원하는 페이지에 static method 로서 추가할 수 있다.
function Page({ stars }) {
return <div>Next stars: {stars}</div>
}
Page.getInitialProps = async (ctx) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
export default Page
- getInitialProps에서 return된 데이터는 서버 렌더링 시 JSON.stringify가 수행하는 것과 유사하게 직렬화된다. getInitialProps에서 return된 객체가 Date, Map 또는 Set을 사용하지 않고 일반 Object인지 확인하자.
- getInitialProps는 처음에는 서버에서 호출된다. 그 후 `next/link`또는 `next/router`를 이용하여 페이지 이동할 때는 클라이언트에서 호출된다.
- 만약 커스텀 _app에서 getInitialProps를 사용하고, 이동한 페이지에서 getServerSideProps가 구현되었다면, getInitialProps는 서버에서 실행된다.
= > getServerSideProps이 없으면 SSR할 필요가 없으므로 렌더링 최적화를 위해 CSR로 사용하지만, getServerSideProps이 있다면 SSR 때문에 어차피 최적화도 안될 것이다. (첫 페이지를 로딩할 때처럼 HTML과 스크립트를 다시 불러와야 하므로).
그래서 약간의 성능을 챙기기위해 서버에서 같이 실행되는 듯하다?(혹시 틀린 정보라면 댓글 부탁드립니다.)
Context Object
getInitialProps는 context 파라미터를 받는데, 이 파라미터 각체는 다음 프로퍼티를 갖고있다.
- pathname - /pages 폴더 내에 있는 페이지의 현재 경로. (ex: /post?type=news 일 경우 /post)
- query - 객체로 파싱된 URL의 Query String (ex: /post?type=news 일 경우 {type:'news'})
- asPath - 브라우저에 표시되는 실제 경로(쿼리 포함)의 String (ex: /post?type=news 일 경우 /post?type=news)
- req - HTTP 요청 객체(서버 전용)
- res - HTTP 응답 객체(서버 전용)
- err - 렌더링 중 오류가 발생한 경우 오류 객체
주의점
- getInitialProps는 자식 컴포넌트에서 사용할 수 없고, 오직 default export가 정의된 페이지에서 사용할 수 있다.
- getInitialProps 내부에서 SSR을 사용한다면, 애플리케이션이 느려지지 않게 적절히 import 해야한다.
getServerSideProps
페이지에서 getServerSideProps (SSR) 를 호출하면, Next.js는 각 요청에 대해 getServerSIdeProps가 return한 데이터를 사용하여 이 페이지를 pre-rendering한다.
function Page({ data }) {
// 렌더링 데이터
}
// 매 요청마다 호출된다.
export async function getServerSideProps(context) {
const res = await fetch(`https://.../data`)
const data = await res.json()
// props 객체를 통해 data를 page로 전달한다.
return { props: { data } }
}
export default Page
- getServerSideProps는 서버 측에서만 실행되며 브라우저에서는 실행되지 않는다.
- 페이지를 직접 요청하면 요청 시점에 getServerSideProps가 실행되고, 이 페이지가 return된 소품과 함께 미리 렌더링된다.
- `next/link`또는 `next/router`를 이용하여 페이지를 요청하면 Next.js가 서버에 API를 요청하고, 서버는 getServerSIdeProps를 실행한다.
- https://next-code-elimination.vercel.app/ 에 들어가보면 Next.js로 만든 코드들을 Next.js 코드를 제거한 순수 client-side 코드로 변경하는 것을 볼 수 있다.
- getServerSideProps는 페이지에서만 동작한다. 페이지가 아닌 파일(_app.js, _document.js, component등)에서는 동작하지 않는다.
- getServerSideProps내에서 에러가 발생시 기본적으로 pages/500.js 을 보여준다. dev모드에서는 500파일을 보여주진 않고, 에러화면을 보여준다.
Context Object
context 파라미터의 객체에는 아래의 key값들이 있다.
- params: 페이지가 dynamic route인 경우, (ex: page 이름이 [id].js라면 {id: ...} 객체 return)
- req - HTTP 요청 객체(서버 전용)
- res - HTTP 응답 객체(서버 전용)
- query - 객체로 파싱된 URL의 Query String (ex: /post?type=news 일 경우 {type:'news'} 객체 return)
- preview: true일 경우 Preview Mode 모드
- previewData: preview에서 사용되는 데이터. setPreviewData함수를 이용하여 설정할 수 있다.
- resolvedUrl: 클라이언트 전환을 위해 _next/data 접두사를 제거하고, 원래 쿼리 값을 포함하는 정규화된 요청 URL 버전
(ex /post?type=news 일 경우 '/post?type=news' 문자열 return - locale: 현재 로케일 값
- locales: 사용 가능한 로케일의 목록 값
- defaultLocale: 기본 로케일 값
notFount
`notFound: true`는 Next.js의 getServerSideProps 메서드에서 return되는 객체의 속성 중 하나로, 해당 페이지를 찾을 수 없음을 나타낸다.
예를 들어, 데이터베이스에서 요청한 데이터가 존재하지 않는 경우, 혹은 유효하지 않은 URL을 요청한 경우 등이 있다. 이 경우, `notFound: true`를 return하여 클라이언트에게 해당 페이지가 존재하지 않음을 알리고, 사용자를 404 페이지로 redirection할 수 있다.
export async function getServerSideProps(context) {
const { params } = context;
const post = await fetch(`https://api.example.com/posts/${params.id}`).then(res => res.json());
if (!post) {
return {
notFound: true
}
}
return {
props: {
post
}
}
}
위 예시 코드에서는 getServerSideProps 메서드에서 데이터베이스에서 요청한 데이터가 존재하지 않는 경우 `notFound: true`를 return한다. 이 경우, Next.js는 클라이언트에게 해당 페이지가 존재하지 않음을 알리고, 404 페이지로 redirection한다.
redirect
redirect는 Next.js의 getServerSideProps 메서드에서 return되는 객체의 속성 중 하나로, 클라이언트를 다른 페이지로 redirection한다.
예를 들어, 사용자가 로그인하지 않은 경우 로그인 페이지로 redirection하거나, 특정 조건이 충족되지 않는 경우 다른 페이지로 redirection하는 등이 있다.
export async function getServerSideProps(context) {
const { req } = context;
if (!req.headers.cookie) {
return {
redirect: {
destination: '/login',
permanent: false,
// statusCode: 301
}
}
}
return {
props: {
// ...
}
}
}
위 예시 코드에서는 getServerSideProps 메서드에서 클라이언트의 쿠키를 검사하여 로그인하지 않은 경우 `redirect`를 return하여 로그인 페이지로 redirection한다.
- `destination` 속성을 사용하여 redirection할 URL을 지정할 수 있다.
- `permanent` 속성을 사용하여 해당 redirection을 영구 redirection로 설정할 수도 있다.
- 일반적으로, permanent 속성을 true로 설정하면 redirection을 클라이언트 캐시에 저장하고, 301 HTTP 상태 코드를 return한다.
- false로 설정하면 매번 새로운 redirection을 받고, 302 HTTP 상태 코드를 return한다.
- `redirect`를 사용하여 클라이언트를 다른 페이지로 redirection할 때는, 반드시 올바른 HTTP 상태 코드를 사용하여 redirection해야한다.
- 드물지만 구형 HTTP 클라이언트가 올바르게 redirection되도록 custom statusCode를 할당해야 하는 경우가 있다. 이 경우 statusCode 속성을 사용할 수 있지만 permanent 속성과 같이 사용할 수는 없다.
Edge APi Routes
getServerSideProps는 서버리스 런타임과 엣지 런타임 둘다 사용할 수 있다. 그러나 현재 Edge Runtime에서는 응답 객체에 접근 할 수 없다. 접근하기 위해서는 기본 런타임인 Node.js 런타임을 사용해야 한다.
export const config = {
runtime: 'nodejs',
}
References
Next.JS 공식 홈페이지 - https://nextjs.org/docs/api-reference/data-fetching/get-initial-props