라우팅 방식이 page Router
에서 app Router
로 전환되면서
렌더링 종류에 따라 구현하는 방법이 변경되어 정리해보고자 한다.
각 렌더링에 대한 자세한 설명보다는 변경점 위주로 작성할 것이다.
렌더링 종류에는 크게 CSR | SSR | SSG | ISR
4가지 종류가 있다.
page Router
에서는 getServerSideProps | getStaticProps | getStaticPaths
등의 함수를 사용해 이를 구현해 왔지만
app Router
에서는 기본이 되는 컴포넌트가 서버 컴포넌트
라는 점에 따라 데이터 Cache를 활용해 구현한다.
시작하기에 앞서, 다른 게시물에서 많이 사용한 단어인 Pre-Rendering
에 대해 간단하게 짚고 넘어가보자 👨🏻🔬
그림에서 보는것과 같이 (프론트)서버에서 DOM 요소들을 빌드하여 HTML 문서로 사전에 렌더링 하는 것을 말한다.
Pre-Rendering
이 일어나는 시점에 따라 SSG | SSR
을 나눌 수 있다.
SSG
: 빌드 시점에 이루어져 완성된 정적 파일을 생성한다. SSR
: 사용자의 요청(url 진입 등)이 있을 때마다 이루어진다.SSG
의 경우에는 빌드 시점의 데이터들로 화면이 구성되어, 실제로 데이터가 변경되더라도 화면에 반영되지 않는다.
SSR
은 서버로부터 html 파일
을 전달받아 화면에 빠르게 보여주고 함께 다운로드한 JS 번들
로 hydration
작업을 걸쳐 최종적으로 보여준다.
💡 Hydrate 란?
클라이언트가 서버에서 랜더링된html 파일
과JS 번들
을 다운로드 완료하면, html 파일에 JS 코드를 매칭 시키는 작업이다.
이 과정을 통해 이벤트가 매핑되고 스타일이 적용된다.
SSG
의 경우에는 빌드타임에 query가 없기 때문에 런타임에서 query에 대한 Hydrate 가 일어난다.
💡 SSG 테스트 환경
SSG 페이지를 확인하려면npm run dev
가 아닌npm run build
후npm run start
로 실행해야 한다.
SEO
(Search Engine Optimization, 검색 엔진 최적화) 가 필요없는 화면이나 데이터 업데이트가 빈번하게 일어나는 화면인 경우 CSR
로 구성한다.
SSR
의 경우 데이터 업데이트가 자주 발생하게 되면 프론트 서버의 피로도가 높아질 수 있기 때문이다.
작동하는 방식은 위와 같으며
page Router
의 경우 평소대로 스크립트 영역에서 데이터를 호출하면 된다.
app Router
의 경우 컴포넌트를 client component
로 만들어주면 된다.
SEO
(Search Engine Optimization, 검색 엔진 최적화) 가 적용되며 실시간 데이터가 반영된 화면을 볼 수 있다.
또한, Pre-Rendering
된 HTML 파일
을 내려받기 때문에 CSR
보다 빠르게 화면이 생성되어 사용자 경험을 향상시킬 수 있다.
✅ 알아두기
사실,Server Component
라는 개념이 생기면서app Router
에서SSR
을 잘 사용하지 않는다. ( 주관적인 생각.. )
Pre-Rendering
시HTML
파일이 아닌직렬화된 Stream
을 내려줘
JS 번들
이 포함되지 않아 용량이 적은Server Component
를 사용한다.서버 컴포넌트 정리 글을 참고하면 좋을 것 같다.
작동하는 방식은 위와 같으며
page Router
의 경우 getServerSideProps()
함수를 사용한다.
function SsrPage({ data }) {
// 파라미터로 데이터를 받아 화면을 구성한다.
return ...
}
export async function getServerSideProps() {
// 가장 먼저 실행되어 데이터를 받아온다. ( 매 요청마다 실행된다.)
const res = await fetch(`https://.../data`)
const data = await res.json()
// 받아온 데이터를 컴포넌트에 전달한다.
return { props: { data } }
}
export default SsrPage
app Router
의 경우 Server Component
에서 데이터 Cache
옵션으로 구현한다.
fetch('https://...', { cache: 'no-store' }
데이터를 받아올 때 no-store
옵션을 주게되면 매 요청마다 서버에서 실시간 데이터를 받아온다.
SSR
과 마찬가지로 SEO
가 적용되며 빌드 시점의 최신 데이터를 가지고 정적 화면을 만들어 캐시에 저장한다.
Client
에서 요청이 있더라도 캐시된 정적 페이지를 제공해 실시간 데이터를 확인할 수 없지만, 가장 빠르게 화면을 사용자에게 노출시켜준다.
작동하는 방식은 위와 같으며
page Router
의 경우 getStaticProps()
함수를 사용한다.function SsgPage({ data }) {
// 파라미터로 데이터를 받아 화면을 구성한다.
return (
<ul>
{data.map((item) => (
<li>{item.title}</li>
))}
</ul>
)
}
export async function getStaticProps() {
// build 시점에 가장 먼저 실행된다.
const res = await fetch('https://.../posts')
const posts = await res.json()
// 받아온 데이터를 컴포넌트에 전달한다.
return { props: {posts} }
}
export default SsgPage
app Router
의 경우 Server Component
에서 데이터 Cache
옵션으로 구현한다.fetch('https://...', { cache: 'force-cache' }
fetch 에서 force-cache
가 default option
이므로 생략이 가능하다.ISR
렌더링 방식은 SSG
와 동일하지만 설정한 시간 단위로 데이터를 최신화 시킨다.
page Router
의 경우 getStaticProps()
함수와 revalidate
옵션을 사용한다.
function IsrPage({ data }) {
// 파라미터로 데이터를 받아 화면을 구성한다.
return ...
}
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: { posts },
revalidate: 10, // 초 단위
}
}
export default IsrPage
app Router
의 경우 Server Component
에서 데이터 revalidate
옵션으로 구현한다.
fetch('https://...', { next: { revalidate: 10} })
데이터를 받아올 때 next: { revalidate: 10}
옵션을 주게되면 설정한 시간 단위로 최신 데이터로 정적 페이지를 생성한다.