■ SSG (Static Side Generation)
▸ 특징 ➜ 미리 html 파일을 만든 후 브라우저에 보여주는 형태의 렌더링 방식, 최초 빌드 시에만 생성이 됨
(* 사전에 미리 정적 페이지를 여러 개 만들어놓음 → 클라이언트가 홈페이지 요청을 하면, 서버에서는 이미 만들어져있는 사이트를 바로 제공! → 클라이언트는 표기만 함)
▸ 장점 ➜ 첫 페이지 로딩 시간이 매우 짧아(TTV) 사용자가 빠르게 페이지를 볼 수 있음, SEO에 유리, CDN(Content Delivery Network) 캐싱 가능
▸ 단점 ➜ 정적인 데이터에만 사용할 수 있음, 사용자와의 상호작용이 서버와의 통신에 의존하므로 클라이언트 사이드 렌더링보다 상호작용이 느릴 수 있음, 또한, 서버 부하가 클 수 있다, 마이페이지 처럼 데이터에 의존하여 화면을 그려주는 경우 사용이 불가함
☀︎ SSG 으로 데이터 불러오기
next.js 에서 기본적으로 아무것도 설정해주지 않았다면 SSG 방식으로 동작함
yarn build && yarn start
* build : production 레벨로 배포하기 전 필요한 빌드 작업 과정을 실행하기 위한 명령어
* start : 만들어진 build 파일을 이용하여 실행시키는 명령어
build, start 후 브라우저 주소로 접속하면 초기에 홈페이지를 로드하고 나서는 다시 로드하지 않음
데이터 불러올 시 리액트에서 사용했었던 useEffect 를 사용할 필요가 없음
→ 왜? 서버컴포넌트안에서는 비동기함수를 직접 사용할 수 있기 때문!
* next.js서버 컴포넌트는 최근에 추가된 fetch api를 통해 최적화하므로 axios 보다는 fetch api 사용을 권장
// SSG : 아무것도 하지 않으면 SSG!
import { Product } from "./types";
export default async function Home() {
const res = await fetch("http://localhost:4000/products");
const data: Product[] = await res.json();
return (
<div>
<h1>Products</h1>
<div className="p-8 m-4">
{data.map((product) => (
<div className="flex border p-4 gap-4 rounded-md" key={product.id}>
<img
className="rounded-sm"
width={150}
height={150}
src={product.images}
alt={product.title}
/>
<div className="flex flex-col justify-between">
<div>
<h2 className="text-xl font-bold">{product.title}</h2>
<p className="text-sm">{product.description}</p>
<p className="mt-4 text-2xl">{product.price.amount}$</p>
</div>
</div>
</div>
))}
</div>
</div>
);
}
■ SSR (Server Side Rendering)
▸ 특징 ➜ SSG, ISR처럼 렌더링 주체가 서버!, 클라이언트의 요청 시 렌더링(C: 이페이지줘!, S: 데이터읽고...등등 후 html 파일 제공)
▸ 장점 ➜ 빠른 로딩 속도(TTV)와 높은 보안성을 제공, SEO 최적화 좋음, 실시간 데이터를 사용, 마이페이지 처럼 데이터에 의존한 페이지 구성 가능
▸ 단점 ➜ 사이트의 콘텐츠가 변경되면 전체 사이트를 다시 빌드해야 하는데, 이 과정이 시간이 오래 걸릴 수 있음 → 서버 과부하 , 요청할 때 마다 페이지를 만들어야 함
☀︎ SSR 으로 데이터 불러오기
방법 (1)
fetch에 옵션 주기
cache: "no-store" 를 추가
* SSR 을 다시 SSG 로 변경 시 cache: "force-cache" 로 바꿔주면 됨.(cache 옵션 미 기재시에도 기본값이 force-cache 임)
방법 (2)
page.tsx 컴포넌트에 dynamic 추가하기(cache 옵션은 삭제)
export const dynamic = "force-dynamic";
// SSR
export default async function Home() {
// fetch의 url 뒤에 옵션 cache: "no-store" 추가
const res = await fetch("http://localhost:4000/products", {
cache: "no-store",
});
const data: Product[] = await res.json();
console.log("render");
return (
<div>
⋮
</div>
);
}
fetch 안에서 캐싱이 되지 않아 새로고침 시 렌더링이 새로 된다. (새로고침 시 console.log("render")가 계속 출력됨)
■ CSR (Client Side Rendering)
▸ 특징 ➜ 브라우저에서 JavaScript를 이용해 동적으로 페이지를 렌더링하는 방식, 렌더링의 주체 - 클라이언트
▸ 장점 ➜ (최초 한번 로드가 끝나면) 사용자와의 상호작용이 빠르고 부드럽다, 서버에게 추가적인 요청을 보낼 필요가 없기 때문에,
사용자 경험이 좋다, 서버 부하가 적다.
▸ 단점 ➜ 첫 페이지 로딩 시간(Time To View)이 길 수 있다, JavaScript가 로딩 되고 실행될 때까지 페이지가 비어있어
검색 엔진 최적화(SEO)에 불리하다.
☀︎ CSR 으로 데이터 불러오기
① 서버 컴포넌트가 아닌 클라이언트 컴포넌트를 생성해줘야한다. (app/_components/ProductList.jsx)
② 생성한 ProductList 의 useEffect 안에서 fetchData를 불러준다.
★ 클라이언트 컴포넌트 최상단에 "use client" 옵션 꼭 넣어줄 것!
* 클라이언트 로직인 useEffect, useState 는 무조건 클라이언트에서만 동작해야만 한다.
"use client";
import { useEffect, useState } from "react";
import { Product } from "../types";
const fetchData = async () => {
const res = await fetch("http://localhost:4000/products");
const data: Product[] = await res.json();
return data;
};
const ProductList = () => {
const [data, setData] = useState<Product[]>([]);
useEffect(() => {
console.log("render");
fetchData().then(setData);
}, []);
return (
<div>
⋮
</div>
);
};
export default ProductList;
③ 생성한 클라이언트 컴포넌트를 import 해준다.
// 최상단의 page.tsx
import ProductList from "./_components/ProductList";
export default async function Home() {
return (
<div>
<ProductList />
</div>
);
}
④ 새로 yarn build && yarn start 를 해보면 서버가 아닌 클라이언트 콘솔에서 console.log("render") 가 출력되는 것을 확인할 수 있다.
■ ISR (Incremental Static Regeneration)
* 특정 타임에만 렌더링이 다시 되도록 하는 SSG와 SSR이 섞여져 있는 렌더링 방식
▸ 특징 ➜ SSG처럼 정적 페이지를 제공, 설정한 주기만큼 페이지를 계속 생성해 줌, 정적 페이지를 먼저 보여주고, 필요에 따라 서버에서 페이지를 재생성하는 방식
▸ 장점 ➜ 정적 페이지를 먼저 제공하므로 사용자 경험이 좋으며, 콘텐츠가 변경되었을 때 서버에서 페이지를 재생성하므로 최신 상태를 (그나마) 유지할 수 있음, CDN 캐싱 가능
▸ 단점 ➜ 동적인 콘텐츠를 다루기에 한계가 있을 수 있음(실시간 페이지X), 마이페이지 처럼 데이터에 의존하여 화면을 그려주는 경우 사용 불가
☀︎ ISR 으로 데이터 불러오기
방법 (1)
fetch에 옵션 주기
next: {revalidate: } 을 추가해서 ISR을 방식으로 만들어줄 수 있다.
방법 (2)
page.tsx 컴포넌트에 revalidate 추가하기(next 옵션은 삭제)
export const revalidate = 5;
// ISR
import { Product } from "./types";
export default async function Home() {
const res = await fetch("http://localhost:4000/products", {
next: {
revalidate: 3, // 3초 마다 렌더링이 업데이트 됨
},
});
const data: Product[] = await res.json();
console.log("render");
return (
<div>
⋮
</div>
);
}
■ 요약
SSG | cache: "force-cache" or 옵션無 |
ISR | next: { revalidate: 5 } // 설정할 주기 입력 or export const revalidate = 5; |
SSR | cache: "no-store" or export const dynamic = "force-dynamic"; |
CSR | 클라이언트 컴포넌트 최상단에 "use client" 추가, 클라이언트 훅 이용한 코드 작성 |
🧐 느낀점
SSG, ISR 등 강의를 듣기만 했을땐 이해하기가 쉽지 않았는데 요약하면서 따라쳐보고 동작원리를 천천히 보다보니 조금 알아가고 있는 것 같다. 아직 갈길이 멀지만..계속 열심히 해보쟈~!
'TIL' 카테고리의 다른 글
타입선언 - 인덱스 시그니처 , undefined 에러처리 (0) | 2024.09.30 |
---|---|
Router Handler & Server Action (0) | 2024.09.26 |
Typescript 개념 공부 (0) | 2024.09.24 |
아웃소싱 팀 프로젝트 ⑥ - 마무리, 회고 (0) | 2024.09.23 |
아웃소싱 팀 프로젝트 ⑤ - 모달창에 지도 불러오기 (0) | 2024.09.21 |