1. Next.js의 렌더링 방법
자, 이제 Next.js를 시작해볼까요? 먼저 신규 타입스크립트용 Next.js 프로젝트를 만들어 봅시다.
// 터미널을 켜서 해당 명령어를 입력
$ npx create-next-app@latest --ts 프로젝트명
// @latest > 최신 버전을 의미함
// --ts > 타입스크립트를 의미함
저는 해당 설정으로 신규 프로젝트를 생성하였습니다.
그럼 아래 사진과 같은 파일구조로 프로젝트가 생성될 것입니다.
그럼 이제 생성된 프로젝트를 실행시켜 볼까요?
$ cd next-sample
$ npm run dev
로그의 내용을 보면 http://localhost:3000 에서 서버가 정상적으로 실행 된 것을 확인할 수 있습니다.
브라우저에 URL을 입력해보면 다음과 같은 페이지가 표시되면 성공입니다.
그럼 Next.js은 어떻게 렌더링 되는걸까요? React와 다른점은 무엇일까요?
하나하나 살펴보도록 합시다.
1. React vs Next
React는 CSR(Client-Side-Rendering) 방식입니다.
우선 빈 HTML을 받고 여러 컴포넌트들을 번들링 하여 전달 한 후,
브라우저는 해당 js 파일을 전달받아 Hydration을 하게 됩니다.
처음 로딩되는 HTML 문서가 빈 페이지이기 때문에
사용자는 빈 화면을 보기 때문에 페이지 이동시 '깜박임'인다고 생각하죠.
반면 Next.js에서는 브라우저가 렌더링 할때 기본적으로 '사전 렌더링'을 해줍니다.
사전 렌더링이란 HTML 문서를 서버단에서 렌더링 하는건데,
사전 렌더링이 된 HTML은 자바스크립트가 빠진 상태이기 때문에 빠르게 로딩이 가능합니다.
이후 자바스크립트 요소들이 렌더링 될 때에는 먼저 그려진 HTML 요소에 hydrate 되는것이기에
웹 페이지를 다시 그리는 과정이 일어나지 않습니다.
2. SSR vs SSG
Next.js는 SSR방식, SSG 방식 두가지 사전 렌더링 방식을 가지고 있습니다.
사전 렌더링을 동적으로 하느냐, 정적으로 하느냐의 차이입니다.
이번 포스팅에서는 웹페이지에서 자주 쓰이는 [ 개인 정보 처리방침 페이지 ]를 만들어보면서
세가지 렌더링(SSG , SSR , IRS , CSR) 방식에 세세히 알아봅시다.
1. App router 방식으로 페이지 그리기
우선 Next.js 를 처음 시작할때 자동으로 생성되는 기본 스타일들을 초기화 해줍니다.
app > global.css , app > page.module.css 폴더의 스타일을 삭제해봅시다.
그 다음 사진과 같이 폴더와 파일을 만들어 아래 코드를 복사해 넣습니다.
// app > privacy > privacy.module.css
@import url("https://webfontworld.github.io/sunn/SUIT.css");
.container {
position: relative;
width: 100%;
max-width: 1280px;
padding: 0 40px;
margin: auto;
padding: 80px 0 20px;
max-width: 860px;
font-family: "SUIT";
}
.content {
line-height: 1.5;
}
// app > privacy > page.tsx
import styles from "./pricacy.module.css";
export default function Privacy() {
return (
<main>
<div className={styles.container}>
<h2 className={styles.title}>개인정보 처리방침</h2>
<p className={styles.content}></p>
</div>
</main>
);
}
그럼 http://localhost:3000/privacy 로 접속하면 아마 사진과 같은 페이지가 생성될 것입니다.
우리가 app 폴더 안에다 생성한 폴더 명(privacy) 이 url이 되는것 입니다.
이것이 바로 Next.js 13버전에서 업데이트된 App Router 방식입니다.
2 - 1. 내용 불러오기 (SSG)
[ 개인 정보 처리방침 페이지 ] 안에 들어갈 내용은 Lorem Ipsum API를 사용해 만들어 봅시다.
해당 사이트에서 API 키를 발급 받고, 아래 코드를 붙여 넣어봅시다.
https://api-ninjas.com/api/loremipsum
// app > privacy > page.tsx
import styles from "./pricacy.module.css";
async function getPrivacyData() {
const res = await fetch(
"https://api.api-ninjas.com/v1/loremipsum?paragraphs=10",
{
method: "GET",
cache: "force-cache" //기본값임 생략가능
headers: {
"X-Api-Key": "YOUR_API_KEY",
},
}
);
return res.json();
}
export default async function Privacy() {
const data = await getPrivacyData();
return (
<main>
<div className={styles.container}>
<h2 className={styles.title}>개인정보 처리방침</h2>
<p className={styles.content}>{data.text}</p>
</div>
</main>
);
}
자, 이렇게 하면 여러분들은 지금 SSG 렌더링으로 페이지를 생성한것 입니다. 놀랍죠!
Next.js 13 버전부터는 모든 컴포넌트의 기본값이 서버 컴포넌트이기 때문에
12버전에서 사용되었던 getStaticProps, getServerSideProps 같은 함수가 필요 없어졌습니다. (사용 불가)
대신, fetch 옵션을 통해 사용이 가능하죠.
아래 옵션을 추가하면 정적 페이지 생성 ( = 내용물이 바뀔 필요가 없습니다. 100% 캐시 사용) 이 가능합니다.
{cache: "force-cache"} //기본값임 생략가능
2 - 2. 내용 불러오기 (SSR)
그럼 SSR로도 페이지를 생성해 볼까요? 아래 옵션을 넣어봅시다.
정상적으로 SSR 방식( = 매 요청마다 새로운 렌더링) 으로 렌더링 된 페이지를 확인 할수 있을 겁니다.
{cache: "no-store"}
SSR과 SSG의 차이점이 느껴지시나요?
두 영상을 보시면, SSG는 새로고침해도 내용이 변경되지 않고,
SSR은 새로고침 할때마다 내용이 변경되는것을 확인 할 수 있습니다.
2 - 3. 내용 불러오기 (ISR)
ISR 방식은 '점진적 정적 재생성' 이라고 하는 SSG의 응용된 렌더링 방법입니다.
사전에 페이지를 생성해서 전송하면, 동시에 접근이 발생함에 따라 페이지를 다시 생성해서 전송하는 거죠.
SSG와 SSR의 중간과 같은 렌더링 방법이라 할 수 있습니다.
{next: { revalidate: 10 }} //10초 후 새 요청이 오면 페이지 새로 생성
2 - 4. 내용 불러오기 (CSR)
[ 개인 정보 처리 방침 페이지 ] 의 내용이 너무 길다면 어떨까요? 보기 힘들겠죠.
더보기 버튼을 만들어 페이지를 꾸며봅시다.
// app > privacy > page.tsx
"use client";
import { useEffect, useState } from "react";
import styles from "./privacy.module.css";
export default function Privacy() {
const [content, setContent] = useState<string>();
const [active, setActive] = useState<boolean>(false);
// 개인 정보 처리방침 데이터 호출
const getPrivacyData = async () => {
await fetch(`https://api.api-ninjas.com/v1/loremipsum?paragraphs=` + 10, {
method: "GET",
headers: {
"X-Api-Key": "YOUR_API_KEY",
},
})
.then((res) => res.json())
.then((result) => setContent(result.text));
};
// 더보기 버튼 클릭 시
const handleMoreView = () => {
setActive((prev) => !prev);
};
useEffect(() => {
getPrivacyData();
}, []);
return (
<main>
<div className={styles.container}>
<h2 className={styles.title}>개인정보 처리방침</h2>
<p className={`${styles.content} ${active ? styles.active : ""}`}>
{content}
</p>
<button
type="button"
className={styles.moreView}
onClick={() => handleMoreView()}
>
{active ? "닫기" : "더보기"}
</button>
</div>
</main>
);
}
// app > privacy > privacy.module.css
@import url("https://webfontworld.github.io/sunn/SUIT.css");
.container {
position: relative;
width: 100%;
max-width: 1280px;
padding: 0 40px;
margin: auto;
padding: 80px 0 20px;
max-width: 860px;
font-family: "SUIT";
}
.content {
line-height: 1.5;
height: 500px;
overflow: hidden;
}
.content.active {
height: 100%;
}
.moreView {
display: table;
font-weight: 700;
cursor: pointer;
outline: none;
border: none;
border-radius: 4px;
background-color: darkslateblue;
color: #fff;
padding: 10px;
text-decoration: none;
margin: 30px auto 0;
}
이전 에디님의 포스팅에서 보았던 " use - "로 시작하는 리액트 훅을 사용하려면,
최상단에 " use client " 사용해서 클라이언트 컴포넌트로 선언해 사용해 주어야 합니다.
물론, 클라이언트 컴포넌트로 바뀌니 fetch API 부분도 수정되어야 합니다.
이렇게 Next.js를 시작해보는 시간을 가지게 되었습니다.
사실, 저는 아직도 저는 잘 모르겠습니다. 좀 더 공부하다보면 이것도 적응되리라 생각듭니다.
다음 포스팅에서는 Next.js의 네가지 기능에 대한 정보로 돌아오겠습니다.
저희는 스터디를 통해 글을 기록하고 있습니다. 피드백은 언제나 환영입니다 :)
'프론트엔드' 카테고리의 다른 글
물러가라! Next.js (기초) (0) | 2024.01.10 |
---|---|
Next.js + MongoDB (1) | 2024.01.09 |
React 맛보기 - React Hooks (1) | 2024.01.03 |
Hello Typescript - part.2 (1) | 2024.01.02 |
React 맛보기 - React Component (0) | 2023.12.29 |