안녕하세요! RyuWoong 입니다.
이번 이야기는 Next에서 아~~주 중요한 Data Fetching에 관한 내용입니다.
Next를 사용하는 중요한 이유 중 하나라고 할 수 있습니다!
그럼! 확인해보실까요?
Data Fetching
Next.js에서는 다양한 방식으로 컨텐츠를 렌더링 할 수 있습니다.
크게 Server-side Rendering, Static-site generation, Client-side rendering이 있습니다.
하나씩 살펴보며 어떤 방식을 어떤 때에 사용하는게 좋은지 알아봅시다.
Server-side Rendering
우리가 Next.js를 사용하는 큰 이유중 하나인 Server-side Rendering입니다.
요청과 함께 데이터를 가져와야 하는 페이지를 렌더링 해야 하는 경우에만 사용해야하며, 만약 요청하는 동안 데이터를 렌더링 할 필요가 없는 경우에는 Client Side에서 데이터를 가져오거나 StaticProps를 가져오는 것을 고려해야합니다. 만약 페이지에 자주 업데이트 되는 데이터가 포함 되어 있고 미리 렌더링 할 필요가 없다면 Client-side Rendering을 고려하는 것이 좋습니다.
어떻게 SSR을 사용하는지 알아볼까요?
getServerSideProps
getServerSideProps라는 함수를 사용하면, 해당 Page를 Pre-Rendering 합니다.
이 함수 내에서는 DB에서 직접 데이터를 불러오는 등의 처리를 할 수 있습니다. 왜냐하면 서버 측에서 사용하기때문에 해당 함수 내에서 작성된 내용은 Client 측에서 볼 수 없습니다. 따라서 API-KEY를 입력해도 문제가 없습니다. Next.js에서는 API Routes를 이용해 Serverless한 환경을 만들 수 있는데, 이때 getServerSideProps내에 API Routes를 사용하여 데이터를 요청하는것은 불필요하고 비효율적인 접근 방식이기 때문에 사용하지 말것을 권고합니다. 차라리 API Routes에 작성된 내용을 getServerSideProps내로 가져와서 호출하는 것이 좋은 방법입니다. 짧게 말하자면, API Routes나 getServerSideProps나 서버 측에서 처리하는데, API Routes를 getServerSideProps에서 불러와서 처리하면 서버에서 작업을 2번하게 됩니다.
getServerSideProps 는 아래 예제코드처럼 사용할 수 있습니다.
Page 파일 내 작성되어야 하며, export 되어야합니다.
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
Caching
Server-side Rendering을 좀 더 효율적으로 처리하고 싶으시다면 Caching에 대해 찾아보는 것이 좋습니다. Caching header (Cache-Control)을 사용하여 응답 시간을 향상시키고 외부 서비스에 대한 요청 수를 줄일 수 있습니다. 아래 링크를 참조하세요.
Static-site Generation
정적 사이트 생성을 알아보겠습니다. 만약 Static-site-Generation을 사용하려면 getStaticProps를 이용하여 사전렌더링이 되어야하는 페이지를 생성할 수 있습니다. 만약 getStaticProps와 동적라우팅을 함께 사용하려면 getStaticPaths를 함께 사용하여야 하며 지정한 모든 경로를 정적으로 Pre-render합니다.아래는 정적 사이트 생성을 하기 위해 예제 코드 입니다.
// pages/posts/[id].js
// Generates `/posts/1` and `/posts/2`
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false, // can also be true or 'blocking'
}
}
// `getStaticPaths` requires using `getStaticProps`
export async function getStaticProps(context) {
return {
// Passed to the page component as props
props: { post: {} },
}
}
export default function Post({ post }) {
// Render post...
}
getStaticPaths 와 getStaticProps를 사용할 수 있으며 getStaticProps에 대해 먼저 알아보겠습니다.
getStaticProps
정적페이지(SSG)를 생성하고자하면 getStaticProps 함수를 작성하고 export를 해주어야합니다.
아래와 같이 사용할 수 있습니다. getServerSideProps와 비슷합니다.
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
언제 사용하면 좋을까요?
- server-side 처럼 요청할 때마다 렌더해서 전달하는 것이 아닌, 빌드 시 미리 렌더링 해두어도 상관 없을 경우. (즉 데이터에 변화가 거의 없는 경우)
- 사전에 렌더링 되어야하고 매우 빠르게 전달되어야 하는 경우
- CDN에 캐싱하여 전달하는 경우
- getStaticPaths와 함께 사용하면 오래된 페이지를 검토하고 브라우저에 새 페이지가 제공되는 동안 getStaticProps가 서버측에서 실행됩니다.
- 정적 HTML을 생성하므로 수신 요청(예: 쿼리 매개 변수 또는 HTTP 헤더)에 액세스할 수 없습니다. 페이지에 대한 요청에 액세스하려면 getStaticProps 외에 미들웨어를 사용하세요.
getStaticPaths
동적 라우팅 페이지 중에 빌드 시 Static하게 생성할 페이지를 정하는 함수입니다.
꼭 getStaticProps 와 함께 사용하여야 합니다. 또 페이지 파일 내에서만 사용할 수 있습니다.
paths를 [] 형태로 리턴해야하며, fallback도 필수적으로 리턴해야합니다.
fallback 값으론 true, false, blocking을 사용할 수 있습니다.
- true : getStaticPaths에서 리턴하지 않은 페이지에 접속 시, 먼저 사용자에게 fallback 페이지를 보여주고 서버에서 Static하게 페이지를 생성한 후 페이지를 사용자에게 보여줍니다. 다음부터 이 페이지에 접속하는 사용자에게는 Static한 페이지를 보여줍니다.
- false : 리턴하지 않은 모든 페이지는 404로 연결합니다.
- blocking : 정적 페이지가 수백/ 수천개 라면 빌드 속도가 느려지기 때문에 paths를 빈배열로 두고 설정한다, 이때 페이지를 요청하면 serversiderendering한 static페이지를 보여주며 다음 요청부터는 serverside rendering한 페이지를 보여줍니다.
Incremental Static Regeneration
정적페이지는 Next 빌드시 생성하게 되는데, revalidate prop 을 이용해 페이지를 업데이트 하여 갱신 할 수 있습니다.
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// the path has not been generated.
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return { paths, fallback: 'blocking' }
}
export default Blog
revalidate prop가 있으면 nextjs는 아래와 같은 동작을 합니다.
- 빌드 후 10초 이내에 요청이 있을 경우 빌드 시 생성했던 정적페이지를 전달 합니다.
- 10초 이후 요청이 있을 경우 기존에 있었던 정적페이지를 전달합니다.
- 그리고 백그라운드에서 페이지 재생성을 트리거 합니다.
- 페이지가 성공적으로 생성되면, Nextjs는 캐시를 무효화하고 업데이트 된 페이지를 표시합니다. 백그라운드 재생성이 실패해도 이전 페이지는 변경되지 않습니다.
Client-side Rendering
필요한 리소스를 전부다 내려받은 후 렌더링을 하는 방식입니다. 기존에 CRA를 통해서 웹페이지를 만든 경우를 생각해보면 될 것 같습니다.
이 경우 Data-Fetching은 주로 아래와 같이 useEffect 훅을 이용하여 진행하게 됩니다.
import { useState, useEffect } from 'react'
function Profile() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
fetch('/api/profile-data')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])
if (isLoading) return <p>Loading...</p>
if (!data) return <p>No profile data</p>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
마무리!
이렇게 쭈욱~ Next Data Fetching에 대해 알아 봤습니다.
제 생각은 대부분 페이지는 SSR과 CSR 중 해당 페이지가 어떤 상황에 어울리는지 생각해 잘 선택하는 것이 핵심일 것 같고, 정말 극히 일부 페이지에 SSG를 사용해서 처리할 수 있을거 같다라는 생각이 듭니다. (물론 블로그를 운영하면 SSG를 선택할 확률이 높을 것 같지만!) 아무튼, 이렇게 중요한 Data Feching에 대해 알아 봤습니다! 그럼 다음에 뵙겠습니다.
참조.
'Front-End > Next.js' 카테고리의 다른 글
Next.js에서 React Query 사용하기. (4) | 2023.03.19 |
---|---|
[Next] Routing .01 (0) | 2023.01.29 |
[Next] 시작하기 .00 (0) | 2023.01.18 |
삽질의 기록과 일상을 남기는 블로그입니다. 주로 React Native를 다룹니다.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!