08. ๐ŸŒ Next.js App Router์™€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

React Server Components(RSC)์˜ ์ฒ ํ•™๊ณผ App Router ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ†ตํ•ด ํ˜„๋Œ€์ ์ธ ๋ Œ๋”๋ง ์ „๋žต์„ ๋งˆ์Šคํ„ฐํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ ์ด ๋ฉด์ ‘ ํ•ญ๋ชฉ์˜ ๋ชฉํ‘œ

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 26๋ถ„ (ํ•ต์‹ฌ ์š”์•ฝ: 13๋ถ„)

๐Ÿ—บ๏ธ ์ด ์ฑ•ํ„ฐ์˜ ํ๋ฆ„
[๊ฐœ๋… ์‚ฌ์ „] โ†’ [์งˆ๋ฌธ 1: App Router & RSC] โ†’ [์งˆ๋ฌธ 2: ๋ Œ๋”๋ง ์ „๋žต SSR/SSG/ISR] โ†’ [์‹ค์ „ ๋ณ€ํ˜• ์งˆ๋ฌธ]

๐ŸŽฏ ์ด ์ฑ•ํ„ฐ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(RSC)์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ์ฐจ์ด์™€ ์‚ฌ์šฉ ์‹œ์ ์„ ์™„๋ฒฝํžˆ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
  • App Router ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ ํŽ˜์นญ๊ณผ ์บ์‹ฑ์ด ์–ด๋–ป๊ฒŒ ๋ณ€ํ–ˆ๋Š”์ง€ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
  • ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ SSR, SSG, ISR ์ค‘ ์ตœ์ ์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“š ํ•ต์‹ฌ ๊ฐœ๋… ์‚ฌ์ „ (Concept Glossary)

1. ๋ฆฌ์•กํŠธ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ (React Server Components, RSC)

์„œ๋ฒ„์—์„œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜์–ด ๊ฒฐ๊ณผ๋ฌผ(HTML์ด ์•„๋‹Œ ํŠน์ˆ˜ JSON)๋งŒ ์ „์†ก๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ์— ํฌํ•จ๋˜์ง€ ์•Š์•„ ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ํ˜๋ช…์ ์ด๋ฉฐ, DB์— ์ง์ ‘ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

2. ํ•˜์ด๋“œ๋ ˆ์ด์…˜ (Hydration)

์„œ๋ฒ„์—์„œ ๋งŒ๋“ค์–ด์ง„ ์ •์  HTML ์œ„์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž…ํ˜€ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋‹ฌ๊ณ  ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒํ•œ ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” '๋ถ€ํ™œ' ๊ณผ์ •์ž…๋‹ˆ๋‹ค. RSC๋Š” ํ•˜์ด๋“œ๋ ˆ์ด์…˜์ด ํ•„์š” ์—†๋Š” ๋ถ€๋ถ„์ด ๋Š˜์–ด๋‚œ๋‹ค๋Š” ์ ์ด ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

3. ISR (Incremental Static Regeneration)

๋นŒ๋“œ ์‹œ์ ์— ์ •์  ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑ(SSG)ํ•˜๋˜, ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ํ•ด๋‹น ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. '์ •์  ํŽ˜์ด์ง€์˜ ์„ฑ๋Šฅ'๊ณผ '๋ฐ์ดํ„ฐ์˜ ์‹ค์‹œ๊ฐ„์„ฑ'์„ ๋ชจ๋‘ ์žก์€ ์ „๋žต์ž…๋‹ˆ๋‹ค.


๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "์˜ํ˜ธ ๋‹˜! '์˜์ˆ˜๋„ค ๋ฉ”์ธ ํŽ˜์ด์ง€'๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ๋ฌด๊ฑฐ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŽ์ด ์ผ๋”๋‹ˆ ์ดˆ๋กœ๋”ฉ์ด ๋„ˆ๋ฌด ๋А๋ ค์š”. ๊ทธ๋ ‡๋‹ค๊ณ  ๋‹ค ๋นผ๋ฒ„๋ฆด ์ˆ˜๋„ ์—†๊ณ ... ์–ด๋–กํ•˜์ฃ ? ๐Ÿ˜ญ"
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ทธ ์ง์„ ๋‹ค ์งŠ์–ด์งˆ ํ•„์š”๋Š” ์—†์–ด์š”. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(RSC)๋ฅผ ์“ฐ๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ฌด๊ฑฐ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์กด์žฌ์กฐ์ฐจ ๋ชจ๋ฅธ ์ฑ„ ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ฐ›์„ ์ˆ˜ ์žˆ์ฃ . Next.js๋Š” ๋‹จ์ˆœํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹ˆ๋ผ ์„œ๋ฒ„ ์ž์›์„ ์–ด๋–ป๊ฒŒ ํšจ์œจ์ ์œผ๋กœ ์“ธ์ง€ ์•Œ๋ ค์ฃผ๋Š” ๊ฐ€์ด๋“œ๋ผ์ธ์ž…๋‹ˆ๋‹ค."

Q1. ๋ฆฌ์•กํŠธ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(RSC)์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฐ์ •์ ์ธ ์ฐจ์ด์™€, ๊ฐ๊ฐ ์–ธ์ œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•ด ๋ณด์„ธ์š”.

๐ŸŽฏ ์ถœ์ œ ์˜๋„

๋ชจ๋˜ ๋ฆฌ์•กํŠธ์˜ ๊ฐ€์žฅ ํฐ ํŒจ๋Ÿฌ๋‹ค์ž„ ๋ณ€ํ™”์ธ '์„œ๋ฒ„ ์ธก ์‹คํ–‰'์„ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ตœ์ ํ™”ํ•˜๋ ค๋Š” ์•„ํ‚คํ…์ฒ˜์  ๊ณ ๋ฏผ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ Naive ๊ตฌํ˜„ (Bad Case)

์˜์ฒ ์ด๋Š” ์•„๋ฌด ์ƒ๊ฐ ์—†์ด ๋ชจ๋“  ํŒŒ์ผ ์ตœ์ƒ๋‹จ์— 'use client'๋ฅผ ๋ถ™์ด๊ณ  ๋‹ค๋‹™๋‹ˆ๋‹ค.

'use client'; // ๐Ÿฃ ์˜์ฒ : "์ด๊ฒŒ ์—†์œผ๋ฉด ๋‹ค ์—๋Ÿฌ ๋‚˜๋”๋ผ๊ณ ์š”. ๊ทธ๋ƒฅ ๋‹ค ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ํ•˜๋ฉด ์•ˆ ๋˜๋‚˜์š”?"
 
import { format } from 'date-fns'; // โš ๏ธ ๊ฑฐ๋Œ€ํ•œ ๋‚ ์งœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์— ํฌํ•จ๋จ
 
export function PostItem({ post }) {
  // ๋ณ„๋‹ค๋ฅธ ์ธํ„ฐ๋ž™์…˜(onClick ๋“ฑ)์ด ์—†๋Š”๋ฐ๋„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์„ ์–ธํ•จ
  return <div>{format(new Date(post.createdAt), 'yyyy-MM-dd')}</div>;
}

๐Ÿฆ ์˜ํ˜ธ์˜ ํŒฉํญ ์กฐ์–ธ
"์˜์ฒ  ๋‹˜, 'use client'๋Š” ๋ฆฌ์•กํŠธ 18 ์ดํ›„๋ถ€ํ„ฐ '๋น„์ƒ ํƒˆ์ถœ๊ตฌ' ๊ฐ™์€ ๊ฑฐ์˜ˆ์š”. ๋ธŒ๋ผ์šฐ์ €์˜ API(onClick, useState ๋“ฑ)๊ฐ€ ํ•„์š”ํ•  ๋•Œ๋งŒ ์จ์•ผ์ฃ . ๋ฐ์ดํ„ฐ ๊ฐ€๊ณต์ด๋‚˜ ๋ฌด๊ฑฐ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ์€ ์„œ๋ฒ„์—์„œ ๋๋‚ด๊ณ  ๊ฒฐ๊ณผ๋งŒ ๋ณด๋‚ด๋Š” ๊ฒŒ RSC์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค."

๐Ÿฆ ์˜ํ˜ธ์˜ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ (Good Case)

์˜ํ˜ธ ๋ฆฌ๋“œ๊ฐ€ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ํ˜‘์—… ๊ตฌ์กฐ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

// ๐Ÿฆ ์˜ํ˜ธ: "์„œ๋ฒ„๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์š”๋ฆฌํ•˜๊ณ , ํด๋ผ์ด์–ธํŠธ๋Š” ์„œ๋น™์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค."
 
// ๐Ÿข Server Component (๊ธฐ๋ณธ๊ฐ’)
// - DB ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅ
// - ๊ฑฐ๋Œ€ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(date-fns ๋“ฑ)๋ฅผ ์จ๋„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „์†ก ์•ˆ ๋จ!
async function PostList() {
  const posts = await db.post.findMany(); // โœ… ์„œ๋ฒ„๋‹ˆ๊นŒ ์ง์ ‘ fetch!
  
  return (
    <div>
      {posts.map(post => (
        <PostItem key={post.id} post={post} />
      ))}
    </div>
  );
}
 
// ๐Ÿ“ฑ Client Component ('use client')
// - ์ธํ„ฐ๋ž™์…˜์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์ตœ์†Œํ™”ํ•˜์—ฌ ๋ถ„๋ฆฌ
'use client';
function LikeButton() {
  const [liked, setLiked] = useState(false);
  return <button onClick={() => setLiked(!liked)}>์ข‹์•„์š”</button>;
}

๐Ÿ“Š ๋ ˆ๋ฒจ๋ณ„ ๋‹ต๋ณ€ ๊ฐ€์ด๋“œ (Self-Check)

  • Level 1 (Junior): "์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๊ณ , ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ํ›…(useState ๋“ฑ)์„ ์“ฐ๋ ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค."
  • Level 2 (Senior): "ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ(Zero-bundle size) ๊ด€์ ์—์„œ์˜ ์ด์ ์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณด์•ˆ์ƒ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ˆจ๊ธธ ์ˆ˜ ์žˆ๊ณ  ์„œ๋ฒ„ ์ž์›์„ ํ™œ์šฉํ•ด ๋ฐ์ดํ„ฐ ํŽ˜์นญ์„ ๊ฐ€์†ํ™”ํ•  ์ˆ˜ ์žˆ์Œ์„ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋‘ ์ปดํฌ๋„ŒํŠธ ๊ฐ„์˜ '์ง๋ ฌํ™”(Serialization)' ์ œ์•ฝ ์กฐ๊ฑด์„ ์–ธ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค."
  • Level 3 (Specialist): "RSC ํŽ˜์ด๋กœ๋“œ(Payload)์˜ ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ํ•˜์ด๋“œ๋ ˆ์ด์…˜ ๋น„์šฉ์„ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ด๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. 'Boundary'๋ฅผ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•˜๋А๋ƒ์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(LCP ๋“ฑ)์ด ์–ด๋–ป๊ฒŒ ๋ณ€ํ•˜๋Š”์ง€ ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ๋…ผ๋ฆฌ์ ์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค."

Q2. SSR, SSG, ISR์˜ ์ฐจ์ด์ ์„ ์„ค๋ช…ํ•˜๊ณ , ์„œ๋น„์Šค์˜ ์„ฑ๊ฒฉ์— ๋”ฐ๋ผ ์–ด๋–ค ์ „๋žต์„ ์„ ํƒํ• ์ง€ ๊ธฐ์ค€์„ ์ œ์‹œํ•ด ๋ณด์„ธ์š”.

๐ŸŽฏ ์ถœ์ œ ์˜๋„

๋‹ค์–‘ํ•œ ๋ Œ๋”๋ง ์ „๋žต์˜ ์žฅ๋‹จ์ ์„ ๋น„์ฆˆ๋‹ˆ์Šค ์ƒํ™ฉ์— ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค๋ฌด ๋Šฅ๋ ฅ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๋Šฅ(Speed)๊ณผ ์ตœ์‹ ์„ฑ(Freshness) ์‚ฌ์ด์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ์ดํ•ดํ•˜๋Š”์ง€๊ฐ€ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ Naive ๊ตฌํ˜„ (Bad Case)

์˜์ฒ ์ด๋Š” '๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™”(SEO)'๊ฐ€ ์ค‘์š”ํ•˜๋‹ค๊ณ  ํ•ด์„œ ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๋ฌด์กฐ๊ฑด SSR๋กœ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

// ๐Ÿฃ ์˜์ฒ : "์„œ๋ฒ„์—์„œ ๋‹ค ๊ทธ๋ ค์„œ ์ฃผ๋‹ˆ๊นŒ ๋ฌด์กฐ๊ฑด SSR์ด ์ข‹์€ ๊ฑฐ ์•„๋‹Œ๊ฐ€์š”?"
export async function getServerSideProps() { // โš ๏ธ ๊ตฌํ˜• Pages Router ๋ฐฉ์‹์˜ ๊ณ ์งˆ์  SSR
  const data = await fetchLargeData();
  return { props: { data } };
}
 
// โš ๏ธ ๋ฌธ์ œ: ์‚ฌ์šฉ์ž๊ฐ€ ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„๊ฐ€ ๋งค๋ฒˆ ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ํŽ˜์นญํ•˜๋А๋ผ 
// ์‘๋‹ต ์†๋„(TTFB)๊ฐ€ ๋А๋ ค์ง€๊ณ  ์„œ๋ฒ„ ๋น„์šฉ์ด ํญ๋ฐœํ•จ

๐Ÿฆ ์˜ํ˜ธ์˜ ํŒฉํญ ์กฐ์–ธ
"์˜์ฒ  ๋‹˜, ์•ˆ ๋ณ€ํ•˜๋Š” ํŽ˜์ด์ง€๋ฅผ ์™œ ๋งค๋ฒˆ ์ƒˆ๋กœ ๊ทธ๋ฆฌ๋‚˜์š”? ์ •์  ํŽ˜์ด์ง€(SSG)๋กœ ๋ฏธ๋ฆฌ ๊ตฌ์›Œ๋‘๋ฉด CDN์„ ํƒ€๊ณ  ๋น›์˜ ์†๋„๋กœ ์ „๋‹ฌ๋  ํ…๋ฐ ๋ง์ด์ฃ . SSR์€ '๋‚˜๋งŒ์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ'๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์“ฐ๋Š” ๋„๊ตฌ์˜ˆ์š”."

๐Ÿฆ ์˜ํ˜ธ์˜ ์•„ํ‚คํ…์ฒ˜ ๊ฐ€์ด๋“œ (Good Case)

์˜ํ˜ธ ๋ฆฌ๋“œ๊ฐ€ ์ƒํ™ฉ๋ณ„ ์ตœ์  ๋ Œ๋”๋ง ๋งคํŠธ๋ฆญ์Šค๋ฅผ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

// ๐Ÿฆ ์˜ํ˜ธ: "์‚ฌ์šฉ์ž ์œ ํ˜•๊ณผ ๋ฐ์ดํ„ฐ์˜ ๋นˆ๋„๋ฅผ ๋จผ์ €๋ณด์„ธ์š”."
 
// 1. SSG (Static Site Generation): ์„œ๋น„์Šค ์†Œ๊ฐœ, ๋ธ”๋กœ๊ทธ ๊ธ€
// - ๋นŒ๋“œ ์‹œ์ ์— ๋”ฑ ํ•œ ๋ฒˆ ์ƒ์„ฑ. ๊ฐ€์žฅ ๋น ๋ฆ„.
export const dynamic = 'force-static';
 
// 2. ISR (Incremental Static Regeneration): ์‡ผํ•‘๋ชฐ ์ƒํ’ˆ ๋ชฉ๋ก, ์‹ค์‹œ๊ฐ„์„ฑ ๊ด‘๊ณ 
// - ์ •์  ํŽ˜์ด์ง€์˜ ์žฅ์ ์„ ๊ฐ€์ง€๋ฉด์„œ๋„ ์ผ์ • ์‹œ๊ฐ„๋งˆ๋‹ค ์ž๋™ ๊ฐฑ์‹ .
export const revalidate = 60; // 60์ดˆ๋งˆ๋‹ค ๊ฐฑ์‹ 
 
// 3. SSR (Server Side Rendering): ๋งˆ์ดํŽ˜์ด์ง€, ์žฅ๋ฐ”๊ตฌ๋‹ˆ
// - ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅผ ๋•Œ. ์ตœ์‹ ์„ฑ 100% ๋ณด์žฅ.
export const dynamic = 'force-dynamic';

๐Ÿ“Š ๋ ˆ๋ฒจ๋ณ„ ๋‹ต๋ณ€ ๊ฐ€์ด๋“œ (Self-Check)

  • Level 1 (Junior): "SSR์€ ์ ‘์†ํ•  ๋•Œ๋งˆ๋‹ค ์„œ๋ฒ„๊ฐ€ ๊ทธ๋ฆฌ๊ณ , SSG๋Š” ๋นŒ๋“œํ•  ๋•Œ ๋ฏธ๋ฆฌ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ISR์€ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋‹ค์‹œ ๋งŒ๋“ญ๋‹ˆ๋‹ค."
  • Level 2 (Senior): "๊ฐ ์ „๋žต์ด ์›น ์ง€ํ‘œ(Core Web Vitals) ์ค‘ ํŠนํžˆ TTFB์™€ LCP์— ์–ด๋–ค ์˜ํ–ฅ์„ ์ฃผ๋Š”์ง€ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์˜ ์‹ค์‹œ๊ฐ„์„ฑ๊ณผ ์„œ๋ฒ„ ๋ถ€ํ•˜ ๊ฐ„์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„๋ฅผ ๊ณ ๋ คํ•˜์—ฌ ์ „๋žต์„ ์„ ํƒํ•˜๋Š” ๋…ผ๋ฆฌ๋ฅผ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค."
  • Level 3 (Specialist): "App Router์—์„œ์˜ 'Full Route Cache'์™€ 'Request Memoization' ๊ฐœ๋…์„ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ํŽ˜์ด์ง€ ๋‹จ์œ„์˜ ์ „๋žต์„ ๋„˜์–ด, ํ•˜๋‚˜์˜ ํŽ˜์ด์ง€ ์•ˆ์—์„œ ํŠน์ • ์ปดํฌ๋„ŒํŠธ๋Š” ISR๋กœ, ์–ด๋–ค ๋ถ€๋ถ„์€ Streaming์„ ํ†ตํ•œ SSR๋กœ ๊ตฌ์„ฑํ•˜๋Š” 'ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•„ํ‚คํ…์ฒ˜' ์„ค๊ณ„ ์—ญ๋Ÿ‰์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค."

Q112. Next.js์˜ 'Streaming'๊ณผ 'Suspense'๊ฐ€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๋Š” ์›๋ฆฌ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

  • ๐ŸŽฏ ์ถœ์ œ ์˜๋„: ๋ฐ์ดํ„ฐ ํŽ˜์นญ์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ „์ฒด ํŽ˜์ด์ง€ ๋ Œ๋”๋ง์„ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ , ๋น„์–ด์žˆ๋Š” ํ™”๋ฉด์„ ์ตœ์ ํ™”ํ•˜๋Š” ๊ธฐ์ˆ ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ก ํ•ต์‹ฌ ์›๋ฆฌ & ๋‹ต๋ณ€: ๊ธฐ์กด SSR์€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ๋‘ ์ค€๋น„๋  ๋•Œ๊นŒ์ง€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋นˆ ํ™”๋ฉด๋งŒ ๋ณด์—ฌ์ฃผ์—ˆ์œผ๋‚˜(All-or-nothing), ์ŠคํŠธ๋ฆฌ๋ฐ์€ ุขู…ุงุฏู‡๋œ ๋ถ€๋ถ„(๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š” ์—†๋Š” ์ •์  ๋ ˆ์ด์•„์›ƒ ๋“ฑ)๋ถ€ํ„ฐ ๋จผ์ € ๋ธŒ๋ผ์šฐ์ €์— ์ „์†กํ•ฉ๋‹ˆ๋‹ค. ๋А๋ฆฐ ์ปดํฌ๋„ŒํŠธ๋Š” Suspense ๋ฐ”์šด๋”๋ฆฌ๋กœ ๊ฐ์‹ธ ์Šค์ผˆ๋ ˆํ†ค UI๋ฅผ ๋ณด์—ฌ์ฃผ๊ณ , ๋ฐ์ดํ„ฐ๊ฐ€ ๋„์ฐฉํ•˜๋Š” ๋Œ€๋กœ ํ•ด๋‹น ๋ถ€๋ถ„๋งŒ ์กฐ๊ฐ์กฐ๊ฐ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ํ˜๋ ค๋ณด๋ƒ…๋‹ˆ๋‹ค(Stream). ์ด๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ํŽ˜์ด์ง€๊ฐ€ ๋” ๋นจ๋ฆฌ ๋ฐ˜์‘ํ•œ๋‹ค๋Š” ๋А๋‚Œ(Perceived Performance)์„ ์ค๋‹ˆ๋‹ค.

Q120. next/image ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹จ์ˆœํ•œ <img> ํƒœ๊ทธ๋ณด๋‹ค ๋‚˜์€ ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

  • ๐ŸŽฏ ์ถœ์ œ ์˜๋„: ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋ผ๋Š” ๊ตฌ์ฒด์ ์ธ ์„ฑ๋Šฅ ๊ฐœ์„  ๋„๊ตฌ์˜ ๋‚ด๋ถ€ ๋™์ž‘์„ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ก ํ•ต์‹ฌ ์›๋ฆฌ & ๋‹ต๋ณ€: ์ฒซ์งธ, ์ž๋™ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ๊ธฐ๊ธฐ์— ๋งž์ถฐ WebP ๊ฐ™์€ ํ˜„๋Œ€์ ์ธ ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ํฌ๊ธฐ๋ฅผ ๋ฆฌ์‚ฌ์ด์ง•ํ•ฉ๋‹ˆ๋‹ค. ๋‘˜์งธ, Lazy Loading์„ ๊ธฐ๋ณธ ์ ์šฉํ•˜์—ฌ ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ์ด๋ฏธ์ง€๋Š” ๋‚˜์ค‘์— ๋กœ๋”ฉํ•ฉ๋‹ˆ๋‹ค. ์…‹์งธ, Layout Shift ๋ฐฉ์ง€์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์ „์—๋„ ์ •ํ™•ํ•œ ์ž๋ฆฌ๋ฅผ ํ™•๋ณดํ•˜์—ฌ LCP์™€ CLS ์ง€ํ‘œ๋ฅผ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ CDN ์บ์‹ฑ์„ ํ†ตํ•ด ์›๋ณธ ์„œ๋ฒ„์˜ ๋ถ€ํ•˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์—ฌ์ค๋‹ˆ๋‹ค.

Q135. ๋ฏธ๋“ค์›จ์–ด(Middleware)๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค๋ฌด์ ์ธ ์‚ฌ๋ก€๋ฅผ ๋“ค์–ด๋ณด์„ธ์š”.

  • ๐ŸŽฏ ์ถœ์ œ ์˜๋„: ์š”์ฒญ๊ณผ ์‘๋‹ต ์‚ฌ์ด์˜ ๊ณตํ†ต ๋กœ์ง์„ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ€๋กœ์ฑ„๊ธฐ(Interceptor) ํŒจํ„ด์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ๐Ÿ’ก ํ•ต์‹ฌ ์›๋ฆฌ & ๋‹ต๋ณ€: ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์‚ฌ๋ก€๋Š” **์ธ์ฆ ๋ฐ ๊ถŒํ•œ ์ฒดํฌ(Auth Guard)**์ž…๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ์ด ๋˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋ณดํ˜ธ๋œ ํŽ˜์ด์ง€์— ์ ‘๊ทผํ•  ๋•Œ ๋ Œ๋”๋ง์ด ์‹œ์ž‘๋˜๊ธฐ๋„ ์ „์— ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์‚ฌ์šฉ์ž์˜ ๊ตญ๊ฐ€ ์ •๋ณด์— ๋”ฐ๋ฅธ ๊ตญ์ œํ™”(i18n) ๋ผ์šฐํŒ…, A/B ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์ฟ ํ‚ค ๊ธฐ๋ฐ˜ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ, ํ˜น์€ ๋ด‡ ์ฐจ๋‹จ๊ณผ ๊ฐ™์€ ๋ณด์•ˆ ๋กœ์ง์„ ํŽ˜์ด์ง€ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ ์ „์—ญ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿฃ ์˜์ฒ ์ด์˜ ๋ณต๊ธฐ ์ผ๊ธฐ

์˜ค๋Š˜ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ "์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์˜ ์ „์œ ๋ฌผ์ด ์•„๋‹ˆ๊ฒŒ ๋˜์—ˆ๋‹ค"๋Š” ์‚ฌ์‹ค์„ ๋ผˆ์ €๋ฆฌ๊ฒŒ ๋А๊ผˆ๋‹ค. ์˜ํ˜ธ ๋‹˜์ด ๋ง์”€ํ•˜์‹  '์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ์˜ ํ˜‘์—…'์€ ๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋ฅผ ๋‚˜๋ˆ  ์งœ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ๋Œ€ํ•œ ๊ฐ€๋ฒผ์šด ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ณด๋‚ด๋ ค๋Š” ๊ฐœ๋ฐœ์ž์˜ ๋ฐฐ๋ ค๋ผ๋Š” ๊ฑธ ์•Œ์•˜๋‹ค.

๐Ÿ’ก "์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ๋œ์–ด๋‚ด๋Š” ๋ฐ์„œ ์‹œ์ž‘ํ•˜๊ณ , ๊ทธ ๋œ์–ด๋ƒ„์˜ ๋์— ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ๋‹ค."

๋‚ด์ผ์€ ์›น์˜ ์„ฑ์ ํ‘œ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š” '์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ Web Vitals'๋ฅผ ๋ฐฐ์šด๋‹ค. ์ ์ˆ˜ 1์ ์„ ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•œ ์ฒ˜์ ˆํ•œ ์‚ฌํˆฌ(?)๊ฐ€ ๋ฒŒ์–ด์งˆ ๊ฒƒ ๊ฐ™์€ ์˜ˆ๊ฐ์ด ๋“ค์ง€๋งŒ, ์˜์ฒ ์•„ ๋„Œ ํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ“ˆ๐Ÿ”ฅ