framework/NextJS

๐Ÿš€ Next.js App Router ์™„์ „ ์ดํ•ดํ•˜๊ธฐ – ์‹ค์ „ FAQ ์ค‘์‹ฌ ํ•ด์„ค

octo54 2025. 4. 10. 11:05
๋ฐ˜์‘ํ˜•

๐Ÿš€ Next.js App Router ์™„์ „ ์ดํ•ดํ•˜๊ธฐ – ์‹ค์ „ FAQ ์ค‘์‹ฌ ํ•ด์„ค

Next.js์—์„œ App Router๋Š” ๊ธฐ์กด์˜ Pages Router๋ฅผ ๋Œ€์ฒดํ•˜๋Š” ์ฐจ์„ธ๋Œ€ ์•„ํ‚คํ…์ฒ˜์ž…๋‹ˆ๋‹ค.
React์˜ ์ตœ์‹  ๊ธฐ๋Šฅ๋“ค์ธ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(Server Components), Suspense ๊ธฐ๋ฐ˜ ์ŠคํŠธ๋ฆฌ๋ฐ(Streaming), **์„œ๋ฒ„ ์•ก์…˜(Server Actions)**์„ ์ „๋ฉด ๋„์ž…ํ•˜๋ฉด์„œ,
๋‹จ์ˆœํžˆ “๋ผ์šฐํŒ… ๋ฐฉ์‹”์ด ์•„๋‹ˆ๋ผ ์ „์ฒด ์•ฑ ๊ตฌ์„ฑ ๋ชจ๋ธ์˜ ํŒจ๋Ÿฌ๋‹ค์ž„ ์ „ํ™˜์ด๋ผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ”ฐ App Router๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”?

App Router๋Š” app/ ๋””๋ ‰ํ† ๋ฆฌ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•˜๋ฉฐ, React Server Component ์ค‘์‹ฌ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ์ค‘์‹ฌ์˜ ๋ผ์šฐํŒ… ์ฒด๊ณ„์ž…๋‹ˆ๋‹ค.

โœ… app/page.tsx, app/layout.tsx, app/api/route.ts ๊ฐ™์€ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ,
๋ผ์šฐํŒ…, ๋ฐ์ดํ„ฐ ํŽ˜์นญ, ์ƒํƒœ ๊ด€๋ฆฌ, ์ธ์ฆ๊นŒ์ง€ "Next.js ์•ˆ์—์„œ ํ†ตํ•ฉ์ ์œผ๋กœ" ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


โ“ ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ (FAQ ์ค‘์‹ฌ ์‹ค์ „ ํ•ด์„)


๐Ÿ” Q. layout์—์„œ ์š”์ฒญ ๊ฐ์ฒด(request object)์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

๋ฐ˜์‘ํ˜•

๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

layout.tsx๋Š” ๋ Œ๋”๋ง ํšจ์œจ์„ ์œ„ํ•ด ์žฌ์‚ฌ์šฉ ๋ฐ ์บ์‹ฑ์„ ์ „์ œ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—,
**๋น„๊ฒฐ์ •์ ์ธ ์š”์†Œ(์˜ˆ: ์š”์ฒญ ๊ฐ์ฒด์— ๋”ฐ๋ฅธ ๋™์ž‘)**๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹ค์Œ์€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

  • headers() ๋˜๋Š” cookies() ์‚ฌ์šฉ (์„œ๋ฒ„ ์ „์šฉ ํ•จ์ˆ˜)
  • ์ฟ ํ‚ค ์„ค์ • (Server Actions ๋˜๋Š” ๋ฏธ๋“ค์›จ์–ด์—์„œ)

๐Ÿ“Œ layout์€ ํŽ˜์ด์ง€ ์ด๋™ ์‹œ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ด ๋•๋ถ„์— ๋ธŒ๋ผ์šฐ์ €์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ” Q. ํŽ˜์ด์ง€์—์„œ URL ์ •๋ณด๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐ€์ ธ์˜ค๋‚˜์š”?

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ผ ๊ฒฝ์šฐ (๊ธฐ๋ณธ):

export default function Page({ params, searchParams }) {
  // ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋Š” params
  // ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์€ searchParams
}

ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์ผ ๊ฒฝ์šฐ:

"use client"
import { usePathname, useSearchParams } from 'next/navigation'

const path = usePathname()
const search = useSearchParams()

์ถ”๊ฐ€๋กœ, ์ค‘์ฒฉ๋œ ๋ ˆ์ด์•„์›ƒ ๊ตฌ์กฐ์—์„œ๋Š” useSelectedLayoutSegment()๋‚˜ useSelectedLayoutSegments()๋กœ
ํ•ด๋‹น ์œ„์น˜์˜ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์ง์ ‘ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ” Q. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฆฌ๋””๋ ‰์…˜(redirect)์„ ํ•  ์ˆ˜ ์žˆ๋‚˜์š”?

๋„ค, ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

import { redirect } from 'next/navigation'

redirect('/login') // 307 Temporary Redirect

ํ˜น์€ ์˜๊ตฌ ์ด๋™์˜ ๊ฒฝ์šฐ:

import { permanentRedirect } from 'next/navigation'

permanentRedirect('/new-url') // 308 Permanent Redirect

โš ๏ธ ์ŠคํŠธ๋ฆฌ๋ฐ ๋„์ค‘์— ๋ฆฌ๋””๋ ‰์…˜์ด ๋ฐœ์ƒํ•˜๋ฉด, ํด๋ผ์ด์–ธํŠธ์— ๋ฉ”ํƒ€ ํƒœ๊ทธ๋กœ ์‚ฝ์ž…๋˜์–ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.


๐Ÿ” Q. App Router์—์„œ ์ธ์ฆ ์ฒ˜๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?

 

์ธ์ฆ ์†”๋ฃจ์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ๋“ค์ด App Router๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค:

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์„ธ์…˜ ๊ฒ€์ฆ → redirect() ์ฒ˜๋ฆฌ ํŒจํ„ด์„ ๋งŽ์ด ์”๋‹ˆ๋‹ค.


๐Ÿช Q. ์ฟ ํ‚ค ์„ค์ •์€ ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?

Server Actions ๋˜๋Š” Route Handlers์—์„œ:

import { cookies } from 'next/headers'

cookies().set('token', 'abc123', { path: '/', httpOnly: true })

โš ๏ธ ํŽ˜์ด์ง€(page.tsx)๋‚˜ ๋ ˆ์ด์•„์›ƒ(layout.tsx)์—์„œ๋Š” ์ฟ ํ‚ค ์„ค์ •์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์ด์œ ๋Š” HTTP ์ŠคํŠธ๋ฆฌ๋ฐ์ด ์‹œ์ž‘๋œ ์ดํ›„์—๋Š” ์ฟ ํ‚ค ํ—ค๋”๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ฏธ๋“ค์›จ์–ด์—์„œ ์„ค์ •ํ•˜๋Š” ์˜ˆ:

import { NextResponse } from 'next/server'

export function middleware(request) {
  const response = NextResponse.next()
  response.cookies.set('foo', 'bar')
  return response
}

๐Ÿข Q. ๋ฉ€ํ‹ฐ ํ…Œ๋„Œ์‹œ(Multi-tenant) ์•ฑ๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‚˜์š”?

๋„ค, Next.js๋Š” ํ•˜๋‚˜์˜ ์•ฑ์—์„œ ์„œ๋ธŒ๋„๋ฉ”์ธ ๋˜๋Š” ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ํ…Œ๋„ŒํŠธ ๊ตฌ๋ถ„์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์˜ˆ: myapp.com/company-a, myapp.com/company-b

๊ณต์‹ ๋ฌธ์„œ์—๋Š” ์ด๋ฅผ ์œ„ํ•œ ์˜ˆ์‹œ ๊ตฌ์กฐ๋„ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.


โ™ป๏ธ Q. App Router์˜ ์บ์‹œ ๋ฌดํšจํ™”๋Š” ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?

Next.js๋Š” ์—ฌ๋Ÿฌ ๊ณ„์ธต์˜ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

  • ์„œ๋ฒ„ ์บ์‹œ (fetch ์บ์‹ฑ, revalidation)
  • React ์ปดํฌ๋„ŒํŠธ ์บ์‹œ (RSC)
  • ์ „์ฒด ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ ์บ์‹œ

๋”ฐ๋ผ์„œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค์Œ์„ ๊ณ ๋ คํ•˜์„ธ์š”:

  • revalidate ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ fetch ๊ฒฐ๊ณผ ์บ์‹ฑ ์ œ์–ด
  • POST ์š”์ฒญ์œผ๋กœ ์บ์‹œ ๋ฌดํšจํ™” ์œ ๋„ (mutate ํŠธ๋ฆฌ๊ฑฐ)
  • cache: 'no-store' ์„ค์ •์œผ๋กœ ํ•ญ์ƒ ์ตœ์‹  ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ

๐Ÿ“ฆ Q. App Router ๊ธฐ๋ฐ˜์˜ ์‹ค์ „ ์˜ˆ์ œ๊ฐ€ ์žˆ๋‚˜์š”?

๋„ค, ๊ณต์‹์ ์œผ๋กœ ์•„๋ž˜ ๋‘ ํ”„๋กœ์ ํŠธ๊ฐ€ ์˜คํ”ˆ์†Œ์Šค๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค:

๋‘˜ ๋‹ค ๊ณ ๊ธ‰ App Router ๊ตฌ์„ฑ, ์ธ์ฆ, ํ…Œ๋„Œ์‹œ, ์„ฑ๋Šฅ ์ตœ์ ํ™” ์˜ˆ์ œ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋‹ˆ ๊ฐ•๋ ฅ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค.


โœ… ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝ

๊ธฐ๋Šฅ ํ•ต์‹ฌ ์š”์•ฝ

์š”์ฒญ ์ ‘๊ทผ headers() ๋˜๋Š” cookies() ํ•จ์ˆ˜๋งŒ ํ—ˆ์šฉ
URL ์ •๋ณด params, searchParams ๋˜๋Š” usePathname()
๋ฆฌ๋””๋ ‰์…˜ redirect(), permanentRedirect()๋กœ ์ฒ˜๋ฆฌ
์ธ์ฆ NextAuth.js, Clerk ๋“ฑ ์ง€์›
์ฟ ํ‚ค ์„ค์ • Server Action, ๋ฏธ๋“ค์›จ์–ด์—์„œ๋งŒ ๊ฐ€๋Šฅ
์บ์‹ฑ ์ „๋žต fetch ์žฌ๊ฒ€์ฆ, ๋ฌดํšจํ™”, no-store ์˜ต์…˜
์‹ค์ „ ์˜ˆ์ œ Commerce, Platforms Starter Kit

 

Next.js, App Router, React Server Component, Streaming, ์ฟ ํ‚ค ์„ค์ •, ์ธ์ฆ ์ฒ˜๋ฆฌ, ๋ผ์šฐํŒ… ์ „๋žต, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ, ๋ฆฌ๋””๋ ‰์…˜