๐Ÿš€ Next.js๋กœ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR) ๊ตฌํ˜„ํ•˜๊ธฐ: ์ดˆ๋ณด์ž๋„ ์‰ฝ๊ฒŒ ๋”ฐ๋ผํ•˜๋Š” ๊ฐ€์ด๋“œ! ๐ŸŽจ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Next.js๋กœ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR) ๊ตฌํ˜„ํ•˜๊ธฐ: ์ดˆ๋ณด์ž๋„ ์‰ฝ๊ฒŒ ๋”ฐ๋ผํ•˜๋Š” ๊ฐ€์ด๋“œ! ๐ŸŽจ

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ํ•ซํ•œ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด์š”. ๋ฐ”๋กœ Next.js๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR)์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด๊ฑฐ ์™„์ „ ํŠธ๋ Œ๋””ํ•˜๊ณ  ์‹ค์šฉ์ ์ธ ์Šคํ‚ฌ์ด๋ผ ๊ผญ ์•Œ์•„๋‘ฌ์•ผ ํ•ด์š”! ๐Ÿ”ฅ

์—ฌ๋Ÿฌ๋ถ„, ํ˜น์‹œ ์›น ๊ฐœ๋ฐœํ•  ๋•Œ ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(CSR)๋งŒ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€ "์•„ ์ด๊ฑฐ ์ข€ ๋Š๋ฆฐ๋ฐ?" ํ•˜๊ณ  ๊ณ ๋ฏผํ•ด๋ณธ ์  ์žˆ๋‚˜์š”? ๊ทธ๋ ‡๋‹ค๋ฉด ์˜ค๋Š˜์˜ ์ฃผ์ œ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์—๊ฒŒ ๋”ฑ์ด์—์š”! SSR์€ ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ์ˆ ์ด๊ฑฐ๋“ ์š”. โœจ

๊ทธ๋Ÿผ ์ด์ œ๋ถ€ํ„ฐ Next.js๋กœ SSR์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ž์„ธํžˆ ์•Œ์•„๋ณผ๊ฒŒ์š”. ์–ด๋ ค์šด ๋‚ด์šฉ๋„ ์žˆ๊ฒ ์ง€๋งŒ, ์ œ๊ฐ€ ์ตœ๋Œ€ํ•œ ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ์„ค๋ช…ํ•ด๋“œ๋ฆด ํ…Œ๋‹ˆ ๋๊นŒ์ง€ ํ•จ๊ป˜ํ•ด์š”!

๐Ÿ’ก ์ฐธ๊ณ : ์ด ๊ธ€์—์„œ ์†Œ๊ฐœํ•˜๋Š” ๊ธฐ์ˆ ๋“ค์€ ์›น ๊ฐœ๋ฐœ์— ๊ด€์‹ฌ ์žˆ๋Š” ๋ถ„๋“ค์—๊ฒŒ ๋งค์šฐ ์œ ์šฉํ•  ๊ฑฐ์˜ˆ์š”. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด ์›น ๊ฐœ๋ฐœ ๊ด€๋ จ ์žฌ๋Šฅ์„ ์ฐพ๊ณ  ๊ณ„์‹œ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ์ž๋“ค์˜ ์žฌ๋Šฅ์„ ๋งŒ๋‚˜๋ณด์„ธ์š”! ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ์— ๋”ฑ ๋งž๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๐Ÿ˜‰

1. Next.js์™€ SSR: ๊ธฐ๋ณธ ๊ฐœ๋… ์ดํ•ดํ•˜๊ธฐ ๐Ÿง 

์ž, ๋จผ์ € Next.js์™€ SSR์ด ๋ญ”์ง€ ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณผ๊นŒ์š”?

  • Next.js: React ๊ธฐ๋ฐ˜์˜ ํ”„๋ ˆ์ž„์›Œํฌ์˜ˆ์š”. ์‰ฝ๊ฒŒ ๋งํ•ด์„œ React๋กœ ์›น ์•ฑ์„ ๋งŒ๋“ค ๋•Œ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” ๋„๊ตฌ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ์š”.
  • SSR(Server-Side Rendering): ์„œ๋ฒ„์—์„œ ํŽ˜์ด์ง€์˜ ๋‚ด์šฉ์„ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด์„œ ๋ธŒ๋ผ์šฐ์ €๋กœ ๋ณด๋‚ด์ฃผ๋Š” ๋ฐฉ์‹์ด์—์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฒซ ํŽ˜์ด์ง€ ๋กœ๋”ฉ์ด ๋นจ๋ผ์ง€๊ณ , ๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™”(SEO)์—๋„ ์ข‹๋‹ต๋‹ˆ๋‹ค.

Next.js๋Š” SSR์„ ์•„์ฃผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜์š”. ๊ทธ๋ž˜์„œ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด Next.js๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฑฐ์ฃ . ํŠนํžˆ ์„ฑ๋Šฅ๊ณผ SEO๊ฐ€ ์ค‘์š”ํ•œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฑฐ์˜ ํ•„์ˆ˜๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์–ด์š”!

๐Ÿค” ์ž ๊น, ์™œ SSR์ด ์ค‘์š”ํ• ๊นŒ์š”?

1. ๋น ๋ฅธ ์ดˆ๊ธฐ ๋กœ๋”ฉ: ์‚ฌ์šฉ์ž๊ฐ€ ์ฒซ ํŽ˜์ด์ง€๋ฅผ ๋นจ๋ฆฌ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

2. SEO ํ–ฅ์ƒ: ๊ฒ€์ƒ‰ ์—”์ง„์ด ์ฝ˜ํ…์ธ ๋ฅผ ๋” ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด์š”.

3. ๋” ๋‚˜์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜: ํŠนํžˆ ๋„คํŠธ์›Œํฌ ์†๋„๊ฐ€ ๋Š๋ฆฐ ํ™˜๊ฒฝ์—์„œ ์œ ์šฉํ•ด์š”.

์ด์ œ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์•Œ์•˜์œผ๋‹ˆ, ๋ณธ๊ฒฉ์ ์œผ๋กœ Next.js๋กœ SSR์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊นŒ์š”? ์ค€๋น„๋˜์…จ๋‚˜์š”? ๊ทธ๋Ÿผ ๊ณ ๊ณ ! ๐Ÿš€

2. Next.js ํ”„๋กœ์ ํŠธ ์‹œ์ž‘ํ•˜๊ธฐ ๐ŸŽฌ

Next.js ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฑด ์ •๋ง ์‰ฌ์›Œ์š”. ํ„ฐ๋ฏธ๋„์„ ์—ด๊ณ  ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด๋ณด์„ธ์š”:

npx create-next-app my-ssr-app
cd my-ssr-app
npm run dev

์™€์šฐ! ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์ธ Next.js ์•ฑ์ด ์ƒ์„ฑ๋˜๊ณ  ์‹คํ–‰๋ผ์š”. localhost:3000์œผ๋กœ ๊ฐ€๋ณด๋ฉด "Welcome to Next.js!" ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ผ ๊ฑฐ์˜ˆ์š”. ์งœ์ž”~ ๐ŸŽ‰

์ด์ œ ์šฐ๋ฆฌ๋งŒ์˜ SSR ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ์ฐจ๋ก€์˜ˆ์š”. pages ํด๋” ์•ˆ์— ์ƒˆ๋กœ์šด ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, pages/ssr-example.js๋ผ๊ณ  ํ•ด๋ณผ๊นŒ์š”?

import React from 'react';

export default function SSRExample({ data }) {
  return (
    <div>
      <h1>SSR ์˜ˆ์ œ ํŽ˜์ด์ง€</h1>
      <p>์„œ๋ฒ„์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ: {data}</p>
    </div>
  );
}

export async function getServerSideProps() {
  // ์—ฌ๊ธฐ์„œ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋กœ์ง์„ ์‹คํ–‰ํ•ด์š”
  const data = "์ด ๋ฐ์ดํ„ฐ๋Š” ์„œ๋ฒ„์—์„œ ์ƒ์„ฑ๋˜์—ˆ์–ด์š”!";
  
  return { props: { data } };
}

์ด ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋กœ SSR์˜ ํ•ต์‹ฌ์ด์—์š”! getServerSideProps ํ•จ์ˆ˜๊ฐ€ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ปดํฌ๋„ŒํŠธ์˜ props๋กœ ์ „๋‹ฌํ•ด์ค˜์š”. ์™„์ „ ์‹ ๊ธฐํ•˜์ง€ ์•Š๋‚˜์š”? ๐Ÿ˜ฒ

๐Ÿ’ก ๊ฟ€ํŒ: getServerSideProps๋Š” ๋งค ์š”์ฒญ๋งˆ๋‹ค ์‹คํ–‰๋ผ์š”. ๊ทธ๋ž˜์„œ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ํŽ˜์ด์ง€์— ๋”ฑ์ด์—์š”!

์ž, ์ด์ œ ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:3000/ssr-example๋กœ ์ ‘์†ํ•ด๋ณด์„ธ์š”. ์„œ๋ฒ„์—์„œ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ™”๋ฉด์— ํ‘œ์‹œ๋  ๊ฑฐ์˜ˆ์š”. ์ถ•ํ•˜ํ•ด์š”! ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฒซ SSR ํŽ˜์ด์ง€๊ฐ€ ์™„์„ฑ๋์–ด์š”! ๐ŸŽŠ

3. SSR์˜ ์žฅ์  ๊ทน๋Œ€ํ™”ํ•˜๊ธฐ ๐Ÿ’ช

SSR์„ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? ๋ช‡ ๊ฐ€์ง€ ํŒ์„ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”:

  1. ๋ฐ์ดํ„ฐ fetching ์ตœ์ ํ™”: ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” ๊ฐ€๋Šฅํ•œ ๋นจ๋ฆฌ ๊ฐ€์ ธ์˜ค์„ธ์š”. ์บ์‹ฑ์„ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์—์š”.
  2. ์ฝ”๋“œ ๋ถ„ํ• : Next.js์˜ ๋™์  ์ž„ํฌํŠธ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด์„œ ํ•„์š”ํ•œ ์ฝ”๋“œ๋งŒ ๋กœ๋“œํ•˜์„ธ์š”.
  3. ์ด๋ฏธ์ง€ ์ตœ์ ํ™”: Next.js์˜ Image ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด๋Ÿฐ ๋ฐฉ๋ฒ•๋“ค์„ ํ™œ์šฉํ•˜๋ฉด SSR์˜ ์žฅ์ ์„ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”. ํŽ˜์ด์ง€ ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋นจ๋ผ์ง€๊ณ , ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๋„ ์ข‹์•„์งˆ ๊ฑฐ์˜ˆ์š”!

๐Ÿš€ ์„ฑ๋Šฅ ํ–ฅ์ƒ ํŒ: Next.js์˜ getStaticProps์™€ getStaticPaths๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ •์  ์ƒ์„ฑ(Static Generation)๋„ ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€์—๋Š” ์ด ๋ฐฉ๋ฒ•์ด ๋” ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค!

4. SSR๊ณผ SEO: ๊ฒ€์ƒ‰ ์—”์ง„์˜ ์‚ฌ๋ž‘์„ ๋ฐ›์•„๋ณด์ž โค๏ธ

SSR์˜ ํฐ ์žฅ์  ์ค‘ ํ•˜๋‚˜๋Š” ๋ฐ”๋กœ SEO(๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™”)์˜ˆ์š”. ๊ฒ€์ƒ‰ ์—”์ง„ ํฌ๋กค๋Ÿฌ๊ฐ€ ์šฐ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”. ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด SSR๋กœ SEO๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

  • ๋ฉ”ํƒ€ ํƒœ๊ทธ ์ตœ์ ํ™”: Next.js์˜ Head ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ ํŽ˜์ด์ง€์˜ ๋ฉ”ํƒ€ ํƒœ๊ทธ๋ฅผ ๋™์ ์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ: JSON-LD ํ˜•์‹์˜ ๊ตฌ์กฐํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์— ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ ๋™์  OG ํƒœ๊ทธ ์ƒ์„ฑ: ์†Œ์…œ ๋ฏธ๋””์–ด ๊ณต์œ  ์‹œ ๋ณด์—ฌ์งˆ ์ด๋ฏธ์ง€์™€ ์„ค๋ช…์„ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด์š”.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฒ€์ƒ‰ ์—”์ง„์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ์‚ฌ์ดํŠธ๋ฅผ ๋” ์ž˜ ์ดํ•ดํ•˜๊ณ , ๋” ๋†’์€ ์ˆœ์œ„๋กœ ๋…ธ์ถœ์‹œ์ผœ์ค„ ๊ฑฐ์˜ˆ์š”. SEO์˜ ๋งˆ๋ฒ•์‚ฌ๊ฐ€ ๋˜๋Š” ๊ฑฐ์ฃ ! ๐Ÿง™โ€โ™‚๏ธ

๐Ÿ’ก ์žฌ๋Šฅ๋„ท ํ™œ์šฉ ํŒ: SEO์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ SEO ์ „๋ฌธ๊ฐ€๋ฅผ ์ฐพ์•„๋ณด์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์›น์‚ฌ์ดํŠธ ์ˆœ์œ„๋ฅผ ๋†’์ด๋Š”๋ฐ ํฐ ๋„์›€์ด ๋  ๊ฑฐ์˜ˆ์š”!

5. SSR์˜ ๋‹จ์ ๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ๐Ÿค”

SSR์ด ์žฅ์ ๋งŒ ์žˆ๋Š” ๊ฑด ์•„๋‹ˆ์—์š”. ๋ช‡ ๊ฐ€์ง€ ๋‹จ์ ๋„ ์žˆ์ฃ . ํ•˜์ง€๋งŒ ๊ฑฑ์ • ๋งˆ์„ธ์š”! ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•๋„ ํ•จ๊ป˜ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”.

  1. ์„œ๋ฒ„ ๋ถ€ํ•˜ ์ฆ๊ฐ€
    • ๋ฌธ์ œ: ๋ชจ๋“  ์š”์ฒญ์„ ์„œ๋ฒ„์—์„œ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์„œ๋ฒ„ ๋ถ€ํ•˜๊ฐ€ ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์š”.
    • ํ•ด๊ฒฐ: ์บ์‹ฑ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, CDN์„ ํ™œ์šฉํ•ด ๋ถ€ํ•˜๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”.
  2. TTFB(Time To First Byte) ์ฆ๊ฐ€
    • ๋ฌธ์ œ: ์„œ๋ฒ„์—์„œ ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์‹œ๊ฐ„ ๋•Œ๋ฌธ์— ์ฒซ ๋ฐ”์ดํŠธ ์ˆ˜์‹  ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚  ์ˆ˜ ์žˆ์–ด์š”.
    • ํ•ด๊ฒฐ: ์„œ๋ฒ„ ์„ฑ๋Šฅ ์ตœ์ ํ™”, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™” ๋“ฑ์œผ๋กœ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  3. ๋ณต์žกํ•œ ์ƒํƒœ ๊ด€๋ฆฌ
    • ๋ฌธ์ œ: ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๊ฐ„์˜ ์ƒํƒœ ๋™๊ธฐํ™”๊ฐ€ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด์š”.
    • ํ•ด๊ฒฐ: Next.js์˜ getInitialProps๋‚˜ Redux์™€ ๊ฐ™์€ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜์„ธ์š”.

์ด๋Ÿฐ ๋‹จ์ ๋“ค์„ ์ž˜ ์ดํ•ดํ•˜๊ณ  ๋Œ€๋น„ํ•œ๋‹ค๋ฉด, SSR์˜ ์žฅ์ ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๊ฐœ๋ฐœ์€ ํ•ญ์ƒ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„์˜ ์—ฐ์†์ด๋‹ˆ๊นŒ์š”! ๐Ÿ˜‰

6. ์‹ค์ „ ์˜ˆ์ œ: SSR๋กœ ๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ ๐Ÿ“

์ž, ์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ํ™œ์šฉํ•ด์„œ ๊ฐ„๋‹จํ•œ ๋ธ”๋กœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊นŒ์š”? SSR์˜ ์žฅ์ ์„ ์‹ญ๋ถ„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ์˜ˆ์ œ๊ฐ€ ๋  ๊ฑฐ์˜ˆ์š”.

๋จผ์ €, ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ ๋ชฉ๋ก ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”:

// pages/index.js
import Link from 'next/link';

export default function Home({ posts }) {
  return (
    <div>
      <h1>My Awesome Blog</h1>
      <ul>
        {posts.map((post) => (
          <li key="{post.id}">
            <link href="%7B%60/post/%24%7Bpost.id%7D%60%7D">
              <a>{post.title}</a>
            
          </li>
        ))}
      </ul>
    </div>
  );
}

export async function getServerSideProps() {
  // ์—ฌ๊ธฐ์„œ๋Š” ์˜ˆ์‹œ๋กœ ํ•˜๋“œ์ฝ”๋”ฉํ–ˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” API๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•ด์š”
  const posts = [
    { id: 1, title: "Next.js ์‹œ์ž‘ํ•˜๊ธฐ" },
    { id: 2, title: "SSR์˜ ์žฅ์ " },
    { id: 3, title: "React vs Next.js" },
  ];
  
  return { props: { posts } };
}

์ด์ œ ๊ฐ ํฌ์ŠคํŠธ์˜ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”:

// pages/post/[id].js
import { useRouter } from 'next/router';

export default function Post({ post }) {
  const router = useRouter();

  // ํด๋ฐฑ ํŽ˜์ด์ง€๋ฅผ ์œ„ํ•œ ์ฒ˜๋ฆฌ
  if (router.isFallback) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
}

export async function getServerSideProps({ params }) {
  // ์‹ค์ œ๋กœ๋Š” API๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ด์š”
  const post = {
    id: params.id,
    title: `ํฌ์ŠคํŠธ ${params.id}์˜ ์ œ๋ชฉ`,
    content: `์ด๊ฒƒ์€ ํฌ์ŠคํŠธ ${params.id}์˜ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. SSR๋กœ ๋ Œ๋”๋ง๋˜์—ˆ์–ด์š”!`,
  };
  
  return { props: { post } };
}

์™€! ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ธฐ๋ณธ์ ์ธ SSR ๋ธ”๋กœ๊ทธ๊ฐ€ ์™„์„ฑ๋ผ์š”. ๊ฐ ํŽ˜์ด์ง€๋Š” ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋˜์–ด SEO์— ์ตœ์ ํ™”๋˜๊ณ , ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๋„ ๋น ๋ฅด๋‹ต๋‹ˆ๋‹ค. ๐Ÿ˜Ž

๐Ÿ’ก Pro Tip: ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ API๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•ด์š”. Next.js๋Š” API ๋ผ์šฐํŠธ ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•˜๋‹ˆ, ๋ฐฑ์—”๋“œ API๋ฅผ ์ง์ ‘ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์–ด์š”!

7. SSR๊ณผ CSR์˜ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ: Next.js์˜ ๊ฐ•์  ๐Ÿ’ช

Next.js์˜ ๋˜ ๋‹ค๋ฅธ ๊ฐ•์ ์€ SSR๊ณผ CSR(Client-Side Rendering)์„ ์ ์ ˆํžˆ ์„ž์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑฐ์˜ˆ์š”. ์ด๊ฒŒ ๋ฐ”๋กœ Next.js๊ฐ€ "ํ•˜์ด๋ธŒ๋ฆฌ๋“œ" ์•ฑ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋Š” ์ด์œ ์ฃ !

์˜ˆ๋ฅผ ๋“ค์–ด, ๋ธ”๋กœ๊ทธ์˜ ๋ฉ”์ธ ํŽ˜์ด์ง€์™€ ํฌ์ŠคํŠธ ์ƒ์„ธ ํŽ˜์ด์ง€๋Š” SSR๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋Œ“๊ธ€ ๊ธฐ๋Šฅ์€ CSR๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ดˆ๊ธฐ ๋กœ๋”ฉ์€ ๋น ๋ฅด๊ฒŒ ํ•˜๋ฉด์„œ๋„, ๋™์ ์ธ ๊ธฐ๋Šฅ์€ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋ณผ๊นŒ์š”?

// pages/post/[id].js
import { useState } from 'react';

export default function Post({ post }) {
  const [comments, setComments] = useState([]);
  const [newComment, setNewComment] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    setComments([...comments, newComment]);
    setNewComment('');
  };

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      
      {/* ๋Œ“๊ธ€ ์„น์…˜ (CSR) */}
      <h2>๋Œ“๊ธ€</h2>
      <ul>
        {comments.map((comment, index) => (
          <li key="{index}">{comment}</li>
        ))}
      </ul>
      <form onsubmit="{handleSubmit}">
        <input type="text" value="{newComment}" onchange="{(e)"> setNewComment(e.target.value)}
          placeholder="๋Œ“๊ธ€์„ ์ž…๋ ฅํ•˜์„ธ์š”"
        />
        <button type="submit">๋Œ“๊ธ€ ์ž‘์„ฑ</button>
      </form>
    </div>
  );
}

export async function getServerSideProps({ params }) {
  // SSR๋กœ ํฌ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
  const post = {
    id: params.id,
    title: `ํฌ์ŠคํŠธ ${params.id}์˜ ์ œ๋ชฉ`,
    content: `์ด๊ฒƒ์€ ํฌ์ŠคํŠธ ${params.id}์˜ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. SSR๋กœ ๋ Œ๋”๋ง๋˜์—ˆ์–ด์š”!`,
  };
  
  return { props: { post } };
}

์ด ์˜ˆ์ œ์—์„œ ํฌ์ŠคํŠธ ๋‚ด์šฉ์€ SSR๋กœ ์ฒ˜๋ฆฌ๋˜๊ณ , ๋Œ“๊ธ€ ๊ธฐ๋Šฅ์€ CSR๋กœ ๊ตฌํ˜„๋˜์—ˆ์–ด์š”. ์™„์ „ ์ฉ”์ง€ ์•Š๋‚˜์š”? ๐Ÿ˜Ž

๐Ÿš€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ: CSR๋กœ ๊ตฌํ˜„๋œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ์ฝ”๋“œ ๋ถ„ํ• (Code Splitting)์„ ์ ์šฉํ•ด๋ณด์„ธ์š”. Next.js์˜ dynamic import๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”!

8. SSR๊ณผ ๋ฐ์ดํ„ฐ fetching: SWR์˜ ๋งˆ๋ฒ• โœจ

Next.js๋กœ SSR์„ ๊ตฌํ˜„ํ•  ๋•Œ ๋ฐ์ดํ„ฐ fetching์€ ์ •๋ง ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด์—์š”. ์—ฌ๊ธฐ์„œ SWR์ด๋ผ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์†Œ๊ฐœํ•ด๋“œ๋ฆด๊ฒŒ์š”!

SWR์€ "stale-while-revalidate"์˜ ์•ฝ์ž๋กœ, Vercel ํŒ€์ด ๋งŒ๋“  ๋ฐ์ดํ„ฐ fetching ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ˆ์š”. SSR๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์„ ๋ฐœํœ˜ํ•œ๋‹ต๋‹ˆ๋‹ค.

SWR์„ ์‚ฌ์šฉํ•œ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋ณผ๊นŒ์š”?

// pages/users.js
import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

export default function Users({ initialData }) {
  const { data } = useSWR('/api/users', fetcher, { initialData });

  return (
    <div>
      <h1>์‚ฌ์šฉ์ž ๋ชฉ๋ก</h1>
      <ul>
        {data.map((user) => (
          <li key="{user.id}">{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

export async function getServerSideProps() {
  const initialData = await fetcher('https://api.example.com/users');
  return { props: { initialData } };
}

์ด ์˜ˆ์ œ์—์„œ๋Š” ์„œ๋ฒ„์—์„œ ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ํด๋ผ์ด์–ธํŠธ์—์„œ SWR์„ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ํ•ด์š”. ์™„์ „ ์ฉ”์–ด! ๐Ÿš€

๐Ÿ’ก SWR์˜ ์žฅ์ :

  • ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ฒ€์ฆ(revalidate)ํ•ด์š”.
  • ์บ์‹ฑ์„ ์ง€์›ํ•ด์„œ ์„ฑ๋Šฅ์ด ์ข‹์•„์š”.
  • ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ์ž๋™์œผ๋กœ ์žฌ์‹œ๋„ํ•ด์š”.
  • ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”.

9. SSR๊ณผ ์Šคํƒ€์ผ๋ง: CSS-in-JS์˜ ์„ธ๊ณ„ ๐ŸŽจ

SSR์„ ๊ตฌํ˜„ํ•  ๋•Œ ์Šคํƒ€์ผ๋ง๋„ ์ค‘์š”ํ•œ ๊ณ ๋ ค์‚ฌํ•ญ์ด์—์š”. Next.js์—์„œ๋Š” CSS-in-JS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค. ํŠนํžˆ styled-components๋‚˜ emotion ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ธ๊ธฐ ์žˆ์–ด์š”.

styled-components๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ๋ฅผ ๋ณผ๊นŒ์š”?

// pages/_document.js
import Document from 'next/document';
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<app></app>),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          >
        ),
      };
    } finally {
      sheet.seal();
    }
  }
}

// pages/index.js
import styled from 'styled-components';

const Title = styled.h1`
  font-size: 50px;
  color: ${({ theme }) => theme.colors.primary};
`;

export default function Home() {
  return <title>์•ˆ๋…•ํ•˜์„ธ์š”, Next.js + styled-components!</title>;
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋  ๋•Œ๋„ ์Šคํƒ€์ผ์ด ์ œ๋Œ€๋กœ ์ ์šฉ๋ผ์š”. CSS-in-JS์˜ ์žฅ์ ์„ SSR์—์„œ๋„ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์ฃ ! ๐Ÿ‘

๐ŸŽจ ์Šคํƒ€์ผ๋ง ํŒ: CSS-in-JS ์™ธ์—๋„ Next.js๋Š” CSS Modules, Sass ๋“ฑ ๋‹ค์–‘ํ•œ ์Šคํƒ€์ผ๋ง ๋ฐฉ์‹์„ ์ง€์›ํ•ด์š”. ํ”„๋กœ์ ํŠธ์˜ ํŠน์„ฑ์— ๋งž๋Š” ๋ฐฉ์‹์„ ์„ ํƒํ•˜์„ธ์š”!

10. SSR๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™”: ๋” ๋น ๋ฅด๊ฒŒ, ๋” ํšจ์œจ์ ์œผ๋กœ! โšก