Syw.Frontend

๐Ÿ“˜ Next.js ๊ธฐ์ดˆ ๊ฐ•์ขŒ

1๋‹จ๊ณ„. Next.js ์ž…๋ฌธ (๊ฐœ๋… & ํ™œ์šฉ ๊ธฐ์ดˆ)

1-3. ๋ฐ์ดํ„ฐ ํŒจ์นญ ๊ธฐ์ดˆ

1. Next.js์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹

Next.js๋Š” React์™€ ๋‹ฌ๋ฆฌ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋จผ์ € ๊ฐ€์ ธ์™€์„œ ํŽ˜์ด์ง€๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€ํ‘œ์ ์ธ ๋ฐฉ์‹์€ 2๊ฐ€์ง€์˜ˆ์š”:

  1. Server Component (๊ธฐ๋ณธ๊ฐ’)
    • ์„œ๋ฒ„์—์„œ ์‹คํ–‰ โ†’ ๋ฐ์ดํ„ฐ ํŒจ์นญ ๊ฐ€๋Šฅ (fetch ๋ฐ”๋กœ ์‚ฌ์šฉ)
    • ํด๋ผ์ด์–ธํŠธ์—์„  HTML๋งŒ ๋ฐ›์•„ ๋น ๋ฅธ ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๊ฐ€๋Šฅ
    • ์˜ˆ: ๋ธ”๋กœ๊ทธ ๊ธ€ ๋ชฉ๋ก, ์ƒํ’ˆ ๋ฆฌ์ŠคํŠธ
  2. Client Component (use client ์„ ์–ธ)
    • ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰ โ†’ useState, useEffect ๊ฐ™์€ ํ›… ์‚ฌ์šฉ ๊ฐ€๋Šฅ
    • ๋ฐ์ดํ„ฐ๋Š” API ์š”์ฒญ ๋“ฑ์„ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ์—์„œ ๊ฐ€์ ธ์˜ด
    • ์˜ˆ: ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ, ๋‹คํฌ๋ชจ๋“œ ํ† ๊ธ€

๐Ÿ‘‰ ์ •๋ฆฌ:

  • ๋ฐ์ดํ„ฐ ํ‘œ์‹œ = Server Component
  • ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ž™์…˜ = Client Component

2. Server Component์—์„œ ๋ฐ์ดํ„ฐ ํŒจ์นญ

(1) app/posts/page.tsx ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ

// Server Component (๊ธฐ๋ณธ)
export default async function PostsPage() {
  // JSONPlaceholder API (๋ฌด๋ฃŒ ์˜ˆ์ œ API)์—์„œ ๊ธ€ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await res.json();

  return (
    <main>
      <h1>Posts ๋ชฉ๋ก</h1>
      <ul>
        {posts.slice(0, 5).map((post: any) => (
          <li key={post.id}>
            <strong>{post.title}</strong>
            <p>{post.body}</p>
          </li>
        ))}
      </ul>
    </main>
  );
}

๐Ÿ‘‰ ๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:3000/posts ์ ‘์† โ†’ ๊ธ€ 5๊ฐœ ๋ชฉ๋ก์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

Next.js๋Š” ์ด fetch๋ฅผ ์„œ๋ฒ„์—์„œ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค.


3. Client Component์—์„œ ๋ฐ์ดํ„ฐ ํŒจ์นญ

๋•Œ๋กœ๋Š” **์‚ฌ์šฉ์ž ์•ก์…˜(๋ฒ„ํŠผ ํด๋ฆญ)**์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ Client Component๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

(1) app/posts-client/page.tsx

"use client"; // ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์„ ์–ธ

import { useState } from "react";

export default function PostsClientPage() {
  const [posts, setPosts] = useState<any[]>([]);

  const loadPosts = async () => {
    const res = await fetch("https://jsonplaceholder.typicode.com/posts");
    const data = await res.json();
    setPosts(data.slice(0, 5));
  };

  return (
    <main>
      <h1>Client Fetch Example</h1>
      <button onClick={loadPosts}>๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ</button>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <strong>{post.title}</strong>
            <p>{post.body}</p>
          </li>
        ))}
      </ul>
    </main>
  );
}

๐Ÿ‘‰ http://localhost:3000/posts-client ์ ‘์† โ†’ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ API ์š”์ฒญ โ†’ ๊ธ€ ๋ชฉ๋ก ํ‘œ์‹œ


4. SSR vs SSG ์ฐจ์ด (๊ฐ„๋‹จ ์†Œ๊ฐœ)

  • SSR (Server-Side Rendering): ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ์ตœ์‹ ํ™” โ†’ fetch ๊ธฐ๋ณธ๊ฐ’
  • SSG (Static Site Generation): ๋นŒ๋“œ ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์™€์„œ ์ •์ ์œผ๋กœ ์ €์žฅ โ†’ fetch์— { next: { revalidate: false } } ์˜ต์…˜
  • ISR (Incremental Static Regeneration): ์ผ์ • ์‹œ๊ฐ„๋งˆ๋‹ค ์ •์  ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ ๊ฐฑ์‹  โ†’ { next: { revalidate: 60 } } ์˜ต์…˜

์˜ˆ์‹œ:

// 60์ดˆ๋งˆ๋‹ค ํŽ˜์ด์ง€ ์ƒˆ๋กœ ์ƒ์„ฑ (ISR)
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
  next: { revalidate: 60 },
});

5. ์˜ค๋Š˜์˜ ์ •๋ฆฌ

  • Server Component โ†’ ์„œ๋ฒ„์—์„œ ์‹คํ–‰, fetch ๊ฐ€๋Šฅ, ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๋น ๋ฆ„
  • Client Component โ†’ useState, useEffect ๊ฐ€๋Šฅ, ์‚ฌ์šฉ์ž ์•ก์…˜ ์ฒ˜๋ฆฌ
  • Next.js ๋ฐ์ดํ„ฐ ํŒจ์นญ ๋ฐฉ์‹: SSR / SSG / ISR
  • ์‹ค์Šต: /posts, /posts-client ๋ผ์šฐํŠธ์—์„œ ๊ฐ๊ฐ ๊ตฌํ˜„ ์™„๋ฃŒ

๐Ÿ“š ์ €์žฅ์†Œ

GitHub - heroyooi/next-tutorial at ch1_3

๐Ÿ’ฌ ๋Œ“๊ธ€

    โ€ป ๋กœ๊ทธ์ธ ํ›„ ๋Œ“๊ธ€์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.