솔미는 성장중
[모던 리액트 딥다이브] 서버사이드 렌더링 본문
Next.js란 ?
react-page에서 영감을 받아 만들어진 프레임워크. (서버사이드 렌더링)
nextJS는 기본적으로 pre-rendering을 한다!
- Pre-rendering : HTML을 미리 렌더링 ▶︎ hydration (js를 불러오며 인터랙션 가능해짐)
브라우저처럼 동작하지 않는(JS를 실행하지 않는) 검색엔진에서도 데이터를 조회할 수 있기에 SEO 향상된다!
(SSG(빌드타임에 pre-render), SSR(요청타임에 pre-render) 데이터 패칭 방식을 이용해야 함) - No Pre-rendering: JS가 load되어야지만 화면이 보임 (대부분의 SPA)
Next 시작하기
npx create-next-app@latest --ts
Next 기본 파일 (예약어로 관리되는 페이지) 뜯어보기
next.config.js
Next.js 프로젝트의 환경 설정을 담당
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true
};
export default nextConfig;
- reactStrictMode
: 리액트 애플리케이션 내부에서 잠재적인 문제를 개발자에게 알리기 위한 도구 - swcMinify
: 번들링과 컴파일을 더욱 빠르게 수행하기 위한 목적. Rust로 작성되었고, 병렬로 작업을 처리하기에 바벨보다 빠르다.
pages/_app.tsx
- Next.js를 초기화 하는 파일로, Next.js 설정과 관련된 코드를 모아두는 곳이며, 경우에 따라 서버와 클라이언트 모두에서 렌더링 될 수 있다.
- 주로 하는 일
- 에러 바운더리 사용해 전역에서 발생하는 에러 처리
- reset.css 선언
- 모든 페이지에 공통으로 제공해야 하는 데이터 제공 (ex. 공통적인 제목)
예시) 공통 레이아웃 적용하는 방법
import Layout from "../components/Layout";
export default function App({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
pages/_document.tsx
- Next.js로 만드는 웹사이트의 뼈대가 되는 HTML 설정과 관련된 코드를 추가하는 곳
- 무조건 서버에서 실행
- 기본 생성 X (필수 아님)
pages/_error.tsx
- 기본 생성 X
- 개발 모드에서 확인할 수 없는 페이지기 때문에 프로덕션으로 빌드해서 확인해봐야한다.
- 프로젝트 전역에서 발생하는 에러를 적절하게 처리하고 싶다면 이 페이지를 활용
pages/404.tsx
- 404 페이지 정의
pages/500.tsx
- 500 에러 페이지 정의
- _error.tsx와 500.tsx가 모두 있다면 500.tsx가 우선
Next 기본 특징 알아보기
1. Next.js는 라우팅이 파일명으로 이어지는 구조를 갖고 있다.
- /pages/index.tsx : 웹사이트 루트 = localhost:3000
/pages/hello.tsx : hello.tsx나 hello/index.tsx와 같은 곳을 가리킴 = localhost:3000/hello
/pages/hello/[greeting].tsx: [ ]는 어떠한 문자도 올 수 있다는 뜻. greeting이란 변수에 사용자가 접속한 주소명이 옴
localhost:3000/hello/1, localhost:3000/hello/hi 모두 유효 (greeting에 1, hi가 담김)
/pages/hello/[...props].tsx : /hello를 제외한 /hello의 모든 하위 주소가 여기로 온다.
ex) localhost:3000/hello/1, localhost:3000/hello/hi/world/foo
🌸 [ ]의 변수로 지정된 값을 사용하는 방법
// 클라이언트에서 값 가져오는 법 export default function Test({props: serverProps}: {props: string[]}) { const {query: {props}} = useRouter() return( <ul> {serverProps.map((item)=>( <li key={item}> {item} </li> ))} </ul> ) } // 서버에서 값 가져오는 법 export default function Test(context: NextPageContext) => { const {query: {props}} = context return( props: {props} ) }
props에 담기는 값 예시
/hi/1/2 : ['1','2']
/hi/my/name/is : ['my', 'name', 'is']
2. Next.js는 서버 사이드 렌더링 + 클라이언트 라우팅 모두 수행 가능하다!
서버 사이드 렌더링 프레임워크이기 때문에 최초 페이지 렌더링은 서버에서 수행된다.
왜 이렇게 작동할까?
ex) <a> 태그와 <Link> 태그 비교
<a> 태그는 모든 리소스를 처음부터 다 가져온다.
서버에서 렌더링을 수행하고, 클라이언트에서 hydrate하는 과정에서 한 번 더 실행
<Link>를 이용한 경우 서버 사이드 렌더링이 아닌, 클라이언트에서 필요한 자바스크립트만 불러온 뒤 라우팅하는 클라이언트 라우팅/렌더링 방식으로 작동.
❇️ 사용자가 빠르게 볼 수 있는 최초 페이지를 제공한다는 서버 사이드 렌더링의 장점 + 자연스러운 라우팅이라는 싱글 페이지 애플리케이션의 장점을 모두 살리기 위한 방향
- 페이지 이동시에는 2가지 규칙을 지키자!
- <a> 대신 <Link>사용하기
- window.location.push 대신 router.push 사용하기
3. getServerSidePRops를 제거하면 서버 사이드 렌더링이 필요없는 정적인 페이지로 분류된다
getServerSideProps가 없는 경우
- 서버 사이드 렌더링이 필요없는, 빌드 시점에 미리 만들어도 되는 페이지로 간주
- 빌드 시점에 미리 트리쉐이킹을 한다.
❇️ Next.js는 서버 사이드 렌더링 프레임워크이지만 모든 작업이 서버에서 일어나는 것은 아니다!
4. api라고 작성된 디렉토리는 서버의 API를 정의하는 폴더
ex) /pages/api/hello.ts
HTML 요청을 하는 게 아니라 단순히 서버 요청을 주고 받는다.
여기 있는 코드는 오직 서버에서만 실행됨
Data Fetching (데이터 불러오기 전략)
- pages/ 폴더에 있는 라우팅이 되는 파일에서만 사용가능
- 예약어로 지정되어 반드시 정해진 함수명으로 export 해야 한다.
❇️ 서버에서 미리 필요한 페이지를 만들어서 제공하거나 해당 페이지의 요청이 있을 때마다 서버에서 데이터를 조회해서 미리 페이지를 만들어서 제공 가능
대표적인 3가지 함수
- getStaticProps (Static Generation) : 프로젝트가 빌드될 때 데이터를 가져온다.
- getStaticPaths (Static Generation) : 데이터를 기반으로 동적 라우트를 지정한다.
- getServerSideProps (Server-Side Rendering) : 각 요청이 있을 때마다 데이터를 가져온다.
getStaticPaths & getStaticProps - 정적페이지
- 사용자와 관계없이 정적으로 결정된 페이지를 보여주고자 할 때 사용
- getStaticPaths를 사용할 땐 무조건 getStaticProps를 사용해야 함!!!!!!
언제 사용하나요? (When should I use getStaticProps?)
- 페이지에 필요한 데이터가 빌드 시에 사용 가능할 때
- 데이터를 headless CMS에서 가져올 때
: headless CMS란? (headless Content Management System)
보통 데이터를 전송 받을때 head에는 데이터를 보여줄 방법,수단을 명시하고 body에 컨텐츠를 담는다.
즉 headless는 순수 데이터만 오는 컨텐츠 관리 시스템을 말한다. - 모든 사용자에게 같은 데이터를 보여줄 때
- SEO를 위해서 속도 빠른 페이지가 필요할 때
: 사용자가 접근할 수 있는 페이지를 모두 빌드해두고 배포하면 사용자는 굳이 페이지가 렌더링될 것을 기다릴 필요없이 완성된 페이지를 받기만 하면 된다!
=> 매우 빠름 (getStaticProps로 생성된 HTML, JSON파일은 효율성을 위해 CDN에 캐시됨) - Node api(path, fs 등)을 사용해야 할 때
// /pages/post/[id]가 접근 가능한 주소를 정의하는 함수
// /post/1과 /post/2만 접근 가능하고 나머지는 404
export const getStaticPaths: GetStaticPaths = async()=>{
return{
paths: [{params: {id:'1'},{params: {id:'2'}],
fallback: false // true, "blocking"이라는 값을 설정할 수도 있음. 아래 글 참고
}
}
// 앞서 정의한 페이지로 요청이 왔을 때 제공할 props반환하는 함수
// fetchPost(1), fetchPost(2)를 기준으로 각 함수의 응답 결과를 변수로 가져와 {post}로 반환
export const getStaticProps: GetStaticProps = async({params})=>{
const {id} = params
const post = await fetchPost(id)
return{
props: {post}
}
}
// getStaticProps가 반환한 post를 렌더링하는 역할
export default function Post({post}: {post:Post}) {
// post로 페이지 렌더링
}
결국 빌드 타임에 필요한 모든 경로에 대해 페이지를 미리 프리렌더링해둔다는 뜻인거같은데.. 새로운 data가 추가되면 어떻게 되는거지?
그때 사용하는게 바로 fallback !
fallback 옵션
- false : 빌드 타임에 모든 경로를 만들고 찾을 수 없는 경로면 404
- true : 미리 빌드하지 않은 페이지에 접근할 시 fallback 컴포넌트 보여주고, 빌드 완료 후 해당 페이지 보여줌
- "blocking" : 아무 처리하지 않고, 빌드 완료될 때까지 기다리게 하다가 렌더링 완료 후 해당 페이지 보여줌
동적 라우팅 페이지를 static 페이지로 제공해야 할 때 사용.
true를 설정했을 때 이점
1.
미리 빌드해야 할 페이지가 너무 많은 경우 미리 빌드할 것을 path에 지정하고 나머지 페이지가 어떻게 작동할 것인지 지정하는 게 좋다 !
2.
build 후에 새롭게 생성된 페이지는 동시에 Next.js는 이 경로를 미리 렌더링된 페이지 목록에 추가한다. 동일한 경로에 대한 후속 요청은 빌드 시점에 미리 렌더링된 다른 페이지와 마찬가지로 생성된 페이지를 제공한다.
(공식문서피셜: https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-true)
유용하게 사용될 수 있는 페이지
- 마케팅 및 랜딩 홍보 페이지
- 블로그 페이지 및 Docs 페이지와 같은 문서를 담은 페이지
- 이커머스 마켓 상품 페이지들
getServerSideProps - 서버 사이드 렌더링
export default 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 } };
}
- 서버에서 실행되는 함수
- 이 함수가 있다면 무조건 페이지 진입 전에 이 함수 실행되어 응답값에 따라 어떤 액션을 할지 결정함
- 이 함수가 있다면 Next.js는 꼭 서버에서 실행해야 하는 페이지로 분류해 빌드 시에도 서버용 JS파일을 별도로 만든다.
➡️ window, document와 같이 브러우저에서만 접근할 수 있는 객체 접근 불가
➡️ API 호출 시 protocal과 domain 없이 fetch 요청 불가. (반드시 완전한 주소를 제공해야 함)
➡️ 에러 발생시 미리 정의해 둔 에러 페이지로 리다이렉트 (ex. 500.tsx) - 사용자가 매 페이지를 호출할 때마다 실행되고, 이 실행이 끝나기 전까진 어떠한 HTML도 보여줄 수 X !
➡️ 꼭 최초에 보여줘야 하는 데이터가 아니라면 getServerSideProps보단 클라이언트에서 호출하는게 유리 - 어떤 조건에 따라 다른 페이지로 보내고 싶다면 redirect 사용가능
➡️ JS 로딩 이전에 원하는 페이지로 보내버릴 수 있어 클라이언트에서 리다이렉트하는 것보다 자연스럽다
유용하게 사용될 수 있는 페이지
- 데이터 업데이트가 자주 발생하는 페이지
getInitialProps
- getStaticProps & getServerSideProps 나오기 이전에 데이터 불러오기 수단이었는데, 지금은 비추
- _app.tsx 같은 일부페이지에선 얘를 사용할 수 밖에 없음
- 가장 큰 차이점
➡️ 페이지의 루트 함수에 정적 메서드로 추가한다
➡️ props 객체가 아닌 바로 객체를 반환
출처
https://velog.io/@go286/getStaticPath%EA%B0%80-%EB%AD%90%EC%95%BC
https://hong-devbox.tistory.com/13