๐ Next.js App Router ์์ ์ดํดํ๊ธฐ – ์ค์ FAQ ์ค์ฌ ํด์ค
๐ 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๋ฅผ ์ง์ํฉ๋๋ค:
- NextAuth.js
- Clerk
- Auth0
- Stytch
- WorkOS
- ์ธ์ ์ด๋ JWT ์ง์ ๊ด๋ฆฌ (Middleware + Server Action)
์๋ฒ ์ปดํฌ๋ํธ์์๋ ์ธ์ ๊ฒ์ฆ → 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, ์ฟ ํค ์ค์ , ์ธ์ฆ ์ฒ๋ฆฌ, ๋ผ์ฐํ ์ ๋ต, ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ, ๋ฆฌ๋๋ ์