๐ Next.js 13์ฅ: Middleware & Edge Runtime โ ์์ฒญ์ ๊ฐ๋ก์ฑ๋ Edge ๊ฒฝ๋น์
๐ ๊ฐ์
Middleware์ Edge Runtime์ผ๋ก ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ ์ธ์ฆ ๋ฆฌ๋ค์ด๋ ํธ, A/B ํ ์คํธ๋ฅผ ๊ตฌํํฉ๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ middleware.ts ๊ธฐ๋ณธ ๊ตฌ์กฐ โ ๋ชจ๋ ์์ฒญ์ ์ฒซ ๊ด๋ฌธ ๐ข
- ๐ ์ธ์ฆ ๋ฆฌ๋๋ ํธ ํจํด โ ๋ก๊ทธ์ธ ์ ํ ์ฌ๋ ์ฐจ๋จํ๊ธฐ ๐ก
- ๐ Edge Runtime์ด๋ ๋ฌด์์ธ๊ฐ ๐ก
- ๐ ๋ณด์ ํค๋ ์ค์ โ CSP์ HSTS ๐ด
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 15๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 7๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ์์ฒ (์ ์
): "๋ก๊ทธ์ธ ์ ํ ์ฌ์ฉ์๊ฐ
/dashboardํ์ด์ง์ ์ ๊ทผํ๋ ค ํ๋ฉด/login์ผ๋ก ๋ณด๋ด์ผ ํ๋๋ฐ... ๊ฐ ํ์ด์ง ์ปดํฌ๋ํธ ์๋จ๋ง๋ค ์ธ์ ์ฒดํฌ ์ฝ๋๋ฅผ ๋ณต๋ถํด์ผ ํ๋์? ํ์ด์ง๊ฐ 100๊ฐ๋ฉด 100๊ตฐ๋ฐ ๋ค ์จ์ผ ํด์?" - ์ํธ(๋ฆฌ๋): "์ ๋ ๊ทธ๋ฌ๋ฉด ์ ๋์ฃ !
middleware.tsํ์ผ ํ๋๋ฅผ ํ๋ก์ ํธ ๋ฃจํธ์ ๋๋ฉด ๋ผ์. ๋ชจ๋ ์์ฒญ์ด ํ์ด์ง์ ๋์ฐฉํ๊ธฐ ์ ์ ์ด ํ์ผ์ ๋ฌด์กฐ๊ฑด ๊ฑฐ์น๊ฑฐ๋ ์. ๊ฒฝ๋น์์ฒ๋ผ ๋ฑ ํ ๊ณณ์์ ๋ชจ๋ ์ถ์ ์ ํต์ ํ๋ ๊ฑฐ์์."
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
Middleware ์คํ ์์น โ ์ธ์ฆ ๋ฆฌ๋๋ ํธ โ Edge Runtime ๊ฐ๋
โ ๋ณด์ ํค๋ ์ค์
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
-
middleware.ts๋ก ๋ก๊ทธ์ธ ์ฌ๋ถ์ ๋ฐ๋ผ ํ์ด์ง ์ ๊ทผ์ ์ ์ดํ ์ ์๋ค -
matcher๋ก Middleware๊ฐ ์คํ๋ ๊ฒฝ๋ก๋ฅผ ์ ๋ฐํ๊ฒ ์ ์ดํ ์ ์๋ค - Edge Runtime์ด Node.js์ ์ด๋ป๊ฒ ๋ค๋ฅธ์ง ์ค๋ช ํ ์ ์๋ค
๐ค ์ ์์์ผ ํ๋๊ฐ
์๋น์ค๋ฅผ ๋ง๋ค๋ค ๋ณด๋ฉด ๋ฐ๋์ ์ด๋ฐ ์๊ตฌ์ฌํญ์ด ์๊ฒจ:
- ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ง
/dashboard,/my,/settings์ ๊ทผ ๊ฐ๋ฅ - ํน์ ๊ตญ๊ฐ(์ง์ญ)์ ๋ฐ๋ผ ๋ค๋ฅธ ํ์ด์ง๋ก ๋ผ์ฐํ (A/B ํ ์คํธ, ๋ค๊ตญ์ด)
- ๋ชจ๋ ์๋ต์ ๋ณด์ ํค๋(
X-Frame-Options,CSP) ์๋ ์ถ๊ฐ - ํน์ ๋ด ๋๋ IP ์ฐจ๋จ
์ด๊ฑธ ๊ฐ ํ์ด์ง๋ง๋ค ๊ตฌํํ๋ฉด ์ฝ๋ ์ค๋ณต, ๋น ๋จ๋ฆฌ๋ ํ์ด์ง ๋ฐ์, ์ ์ง๋ณด์ ์ง์ฅ์ด ํผ์ณ์ ธ.
Middleware๋ ๋ชจ๋ ์์ฒญ์ ๊ณตํต ๊ด๋ฌธ์ด์ผ. CDN ์ฃ์ง ๋ ธ๋์์ ์คํ๋๊ธฐ ๋๋ฌธ์ ์๋ฒ๋ DB์ ๋๋ฌํ๊ธฐ๋ ์ ์, ์ฌ์ง์ด Next.js ์ฑ ์์ฒด์ ์์ฒญ์ด ๋ฟ๊ธฐ ์ ์ ๋จผ์ ์คํ๋ผ. ์ด๊ฒ ํต์ฌ์ด์ผ.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
์ํํธ ์ ๊ตฌ์ ๊ฒฝ๋น์์ด ์์ด. ๋๊ฐ ๋ค์ด์ค๋ ๋จผ์ ๊ฒฝ๋น์์ ๋ง๋์ผ ํด.
"์ ์ฃผ๋ฏผ ์นด๋ ์์ด์?" โ ์์ผ๋ฉด ๋ชป ๋ค์ด๊ฐ๊ฒ ๋ง์.
"๋ช ๋ ๊ฐ์ธ์?" โ 1๋์ ์ผ์ชฝ, 2๋์ ์ค๋ฅธ์ชฝ์ผ๋ก ์๋ดํด์ค.Middleware๊ฐ ๊ทธ ๊ฒฝ๋น์์ด์ผ. ๋ชจ๋ ์์ฒญ์ด ์ค์ ํ์ด์ง(์ํํธ ๊ฐ ์ธ๋)์ ๋ฟ๊ธฐ ์ ์ ๋ฐ๋์ ๊ฒฝ๋น์์ ๊ฑฐ์ณ์ผ ํด.
Middleware์ ์คํ ์์น:
์ฌ์ฉ์ ๋ธ๋ผ์ฐ์ ์์ฒญ
โ
๐ก๏ธ middleware.ts (Edge์์ ์คํ, ๊ฐ์ฅ ๋จผ์ !)
โ
Next.js ์ฑ ์๋ฒ
โ
layout.tsx โ page.tsx
๐งฉ middleware.ts ๊ธฐ๋ณธ ๊ตฌ์กฐ โ ๋ชจ๋ ์์ฒญ์ ์ฒซ ๊ด๋ฌธ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
middleware.tsํ์ผ์ ํ๋ก์ ํธ ๋ฃจํธ์ ๋ง๋ค์ด ๋ชจ๋ ์์ฒญ์ ๊ฐ๋ก์ฑ ์ ์๋คNextResponse.redirect()์NextResponse.next()์ ์ฐจ์ด๋ฅผ ์๋คmatcher๋ก Middleware ์คํ ๊ฒฝ๋ก๋ฅผ ์ ์ดํ ์ ์๋ค
// middleware.ts (ํ๋ก์ ํธ ๋ฃจํธ โ app ํด๋์ ๊ฐ์ ๋ ๋ฒจ)
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
console.log(`[Middleware] ์์ฒญ ๊ฒฝ๋ก: ${pathname}`)
// ํน์ ์กฐ๊ฑด์์ ๋ฆฌ๋๋ ํธ
if (pathname === '/old-posts') {
// NextResponse.redirect: ๋ค๋ฅธ URL๋ก ์๊ตฌ ๋ณด๋ด๊ธฐ
return NextResponse.redirect(new URL('/posts', request.url))
}
// ์์ฒญ ํค๋์ ์ ๋ณด ์ถ๊ฐ (ํ์ด์ง ์ปดํฌ๋ํธ์์ ์ฝ์ ์ ์์)
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-pathname', pathname)
// NextResponse.next(): "ํต๊ณผ, ๋ค์์ผ๋ก ์งํํด"
return NextResponse.next({
request: { headers: requestHeaders },
})
}
// โ๏ธ matcher: Middleware๋ฅผ ์คํํ ๊ฒฝ๋ก ํจํด ์ค์
export const config = {
matcher: [
// ์๋ ๊ฒฝ๋ก๋ค์ ์ ์ธํ ๋ชจ๋ ๊ฒฝ๋ก์์ ์คํ
// ์ด๋ฏธ์ง, ์ ์ ํ์ผ, API ๋ฑ์ ๋ณดํต ์ ์ธํ๋ ๊ฒ ์ฑ๋ฅ์ ์ข์
'/((?!_next/static|_next/image|favicon.ico|api/webhooks).*)',
],
}matcher ํจํด ์์ฑ๋ฒ:
| ํจํด | ์๋ฏธ |
|---|---|
'/dashboard' | /dashboard ์ ํํ ์ผ์น |
'/dashboard/:path*' | /dashboard ํ์ ๋ชจ๋ ๊ฒฝ๋ก |
| `'/((?!api | _next).*)' ` |
['/dashboard/:path*', '/my/:path*'] | ๋ฐฐ์ด๋ก ์ฌ๋ฌ ๊ฒฝ๋ก ์ง์ |
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
middleware.ts๋ ์ํํธ ๊ฒฝ๋น์.matcher๋ ๊ฒฝ๋น์ ๊ทผ๋ฌด ๊ตฌ์ญ ์ง๋์ผ. ์ง๋์ ์๋ ๊ณณ์ ๊ทธ๋ฅ ํต๊ณผ์์ผ.
๐ ์ธ์ฆ ๋ฆฌ๋๋ ํธ ํจํด โ ๋ก๊ทธ์ธ ์ ํ ์ฌ๋ ์ฐจ๋จํ๊ธฐ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ฟ ํค์ ์ธ์ ํ ํฐ์ ์ฝ์ด ๋ก๊ทธ์ธ ์ฌ๋ถ๋ฅผ ํ๋จํ ์ ์๋ค
- ๋ก๊ทธ์ธ ์ ํ ์ฌ์ฉ์๋ฅผ
/login์ผ๋ก ๋ฆฌ๋๋ ํธํ๋ ํจํด์ ๊ตฌํํ ์ ์๋ค
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
์ธ์ ํ ํฐ์ ์ฟ ํค์ ์์ด. Middleware์์ ์ฟ ํค๋ฅผ ์ฝ์ด์ ์ ํจํ ์ธ์ ์ธ์ง ์ด๋ป๊ฒ ํ์ธํ ์ ์์๊น?
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// ๋ก๊ทธ์ธ ์์ด ์ ๊ทผ ๊ฐ๋ฅํ ๊ณต๊ฐ ๊ฒฝ๋ก ๋ชฉ๋ก
const PUBLIC_PATHS = ['/', '/login', '/signup', '/posts']
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
// ๊ณต๊ฐ ๊ฒฝ๋ก๋ ๋ฐ๋ก ํต๊ณผ
const isPublic = PUBLIC_PATHS.some(
(path) => pathname === path || pathname.startsWith(`${path}/`)
)
if (isPublic) return NextResponse.next()
// ์ฟ ํค์์ ์ธ์
ํ ํฐ ์ฝ๊ธฐ
const sessionToken = request.cookies.get('session')?.value
// ์ธ์
์ด ์์ผ๋ฉด ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋๋ ํธ
if (!sessionToken) {
const loginUrl = new URL('/login', request.url)
// ๋ก๊ทธ์ธ ํ ์๋ ๊ฐ๋ ค๋ ํ์ด์ง๋ก ๋์์ค๊ธฐ ์ํด callbackUrl ํ๋ผ๋ฏธํฐ ์ถ๊ฐ
loginUrl.searchParams.set('callbackUrl', pathname)
return NextResponse.redirect(loginUrl)
}
// โ ๏ธ ์ฃผ์: Middleware๋ Edge Runtime์์ ๋์๊ฐ
// DB ์กฐํ๋ ๋ฌด๊ฑฐ์ด JWT ๊ฒ์ฆ์ ์ฌ๊ธฐ์ ํ๋ฉด ์ ๋ผ
// ๊ฐ๋จํ ํ ํฐ ์กด์ฌ ์ฌ๋ถ ํ์ธ๋ง ํ๊ณ , ์ค์ ๊ฒ์ฆ์ ํ์ด์ง/API์์ ํด
return NextResponse.next()
}
export const config = {
// ์ ์ ํ์ผ, ์ด๋ฏธ์ง, API๋ ์ ์ธ
matcher: ['/((?!_next/static|_next/image|favicon.ico|api).*)'],
}์ด ํจํด์ ํ๊ณ์ ์ฌ๋ฐ๋ฅธ ์ญํ ๋ถ๋ฆฌ:
Middleware (Edge) โ "ํ ํฐ์ด ์๋๊ฐ?" ๋ง ํ์ธ (๋น ๋ฅธ 1์ฐจ ์ฒดํฌ)
โ ํต๊ณผ
ํ์ด์ง / Server Action โ "ํ ํฐ์ด ์ ํจํ๊ฐ? ๊ถํ์ด ์๋๊ฐ?" ์ฌ์ธต ๊ฒ์ฆ (๋๋ฆฌ์ง๋ง ์ ํ)
โ ๏ธ ์ฃผ์: Middleware์์ DB ์ฟผ๋ฆฌ๋ ๋ณต์กํ ์ํธํ ์ฐ์ฐ์ ํ๋ฉด ์ ๋ผ. Edge Runtime์ Node.js API ์ผ๋ถ๊ฐ ์ ํ๋์ด ์๊ณ , ๋ชจ๋ ์์ฒญ๋ง๋ค ์คํ๋๋ฏ๋ก ์ฑ๋ฅ์ ์ง๊ฒฐ๋ผ.
๐ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ
์ธ์ ๊ฒ์ฆ์ ์ฌ์ธต ํจํด (DAL,verifySession())์ ์ฌํ 7์ฅ Authentication Architecture์์ ์์ธํ ๋ค๋ค. ์ง๊ธ์ Middleware์ 1์ฐจ ๊ด๋ฌธ ์ญํ ๋ง ํ์ ํ๋ฉด ์ถฉ๋ถํด.
๐ Edge Runtime์ด๋ ๋ฌด์์ธ๊ฐ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- Edge Runtime๊ณผ Node.js Runtime์ ์ฐจ์ด๋ฅผ ์ค๋ช ํ ์ ์๋ค
- Middleware์์ ์ฌ์ฉํ ์ ์๋ Node.js API๊ฐ ๋ฌด์์ธ์ง ์๊ณ ๋์์ ์ฐพ์ ์ ์๋ค
๐ ์ฉ์ด: Edge Runtime โ ์ฌ์ฉ์์๊ฒ ๊ฐ์ฅ ๊ฐ๊น์ด CDN ์ฃ์ง ์๋ฒ์์ ์ฝ๋๋ฅผ ์คํํ๋ ํ๊ฒฝ์ด์ผ. ์ผ๋ฐ ์๋ฒ(Origin)๊น์ง ์์ฒญ์ด ๋ฟ์ง ์์๋ ๋๋๊น ๋ ์ดํด์๊ฐ ํจ์ฌ ๋ฎ์.
์ผ๋ฐ Node.js ์๋ฒ
์ฌ์ฉ์(์์ธ) โ ์๋ฒ(๋ฏธ๊ตญ) โ ์๋ต ๋ฐํ [๋ ์ดํด์: 200ms+]
Edge Runtime
์ฌ์ฉ์(์์ธ) โ ์์ธ ์ฃ์ง ๋
ธ๋ โ ์๋ต ๋ฐํ [๋ ์ดํด์: 10ms ์ดํ]
Edge Runtime์์ ์ฌ์ฉํ ์ ์๋ ๊ฒ๋ค:
| ์ฌ์ฉ ๋ถ๊ฐ | ์ด์ | ๋์ |
|---|---|---|
fs (ํ์ผ ์์คํ
) | Edge ํ๊ฒฝ์ ์๋ฒ ํ์ผ ์์คํ ์์ | S3 ๋ฑ ์ธ๋ถ ์คํ ๋ฆฌ์ง ์ฌ์ฉ |
| ๋๋ถ๋ถ์ npm ํจํค์ง | Node.js ์ ์ฉ API ์์กด | Edge ํธํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ธ |
๋ฌด๊ฑฐ์ด ์ํธํ (bcrypt) | CPU ์ง์ค ์์ ๊ธ์ง | jose ๊ฐ์ ๊ฐ๋ฒผ์ด ๋์ ์ฌ์ฉ |
| Prisma, Drizzle ๋ฑ DB ํด๋ผ์ด์ธํธ | TCP ์์ผ ์์กด | HTTP ๊ธฐ๋ฐ DB ๋๋ ํ์ด์ง์์ ์ฒ๋ฆฌ |
// โ Middleware์์ ์ฐ๋ฉด ์ ๋๋ ํจํด
import bcrypt from 'bcrypt' // Node.js ์ ์ฉ โ Edge์์ ์๋ฌ!
import { PrismaClient } from '@prisma/client' // TCP ์์ผ โ Edge์์ ์๋ฌ!
// โ
Middleware์์ ์ธ ์ ์๋ ํจํด
import { jwtVerify } from 'jose' // Web Crypto API ๊ธฐ๋ฐ โ Edge ํธํ!
const token = request.cookies.get('session')?.value // Web API โ Edge ํธํ!๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
Edge Runtime์ "์ด๊ฒฝ๋ ์คํ ํ๊ฒฝ"์ด์ผ. ๋น ๋ฅธ ๋์ Node.js์ ๋ฌด๊ฑฐ์ด ๊ธฐ๋ฅ์ ๋ชป ์จ. Middleware๋ ์ฌ๊ธฐ์ ๋์๊ฐ๋ ๊ฐ๋ณ๊ฒ ์จ์ผ ํด.
๐ ๋ณด์ ํค๋ ์ค์ โ CSP์ HSTS ๐ด
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- Middleware์์ ๋ชจ๋ ์๋ต์ ๋ณด์ ํค๋๋ฅผ ์๋์ผ๋ก ์ถ๊ฐํ๋ ํจํด์ ๊ตฌํํ ์ ์๋ค
Middleware๋ ๋ชจ๋ ์๋ต์ ํค๋๋ฅผ ์ถ๊ฐํ๊ธฐ์ ์๋ฒฝํ ์์น์ผ.
// middleware.ts โ ๋ณด์ ํค๋ ์ค์ ํจํด
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
// XSS ๋ฐฉ์ด: ๋ธ๋ผ์ฐ์ ๊ฐ Content-Type์ ๋ฌด์ํ์ง ๋ชปํ๊ฒ ๊ฐ์
response.headers.set('X-Content-Type-Options', 'nosniff')
// ํด๋ฆญ์ฌํน ๋ฐฉ์ด: ๋ค๋ฅธ ์ฌ์ดํธ์ iframe์์ ์ฐ๋ฆฌ ํ์ด์ง ์๋ฒ ๋ ๊ธ์ง
response.headers.set('X-Frame-Options', 'DENY')
// HTTPS ๊ฐ์ : 1๋
๊ฐ HTTPS๋ง ํ์ฉ (HSTS)
response.headers.set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains'
)
// CSP: ์คํฌ๋ฆฝํธยท์ด๋ฏธ์งยทํฐํธ ์์ค ํ์ดํธ๋ฆฌ์คํธ
response.headers.set(
'Content-Security-Policy',
[
"default-src 'self'",
"script-src 'self' 'unsafe-inline'", // Next.js inline script ํ์ฉ
"img-src 'self' data: https:",
"font-src 'self' https://fonts.gstatic.com",
].join('; ')
)
return response
}๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
โ require() of ES modules is not supported
์ธ์ ๋์ค๋๊ฐ?
Error: require() of ES Module ... not supported.
์์ธ: Middleware(Edge Runtime)์์ CommonJS ๋ชจ๋์ importํ์ ๋. bcrypt, jsonwebtoken ๊ฐ์ ํจํค์ง๊ฐ ์ฃผ๋ฒ.
ํด๊ฒฐ์ฑ :
// โ ์๋ฌ ๋ฐ์
import jwt from 'jsonwebtoken' // CJS ํจํค์ง
// โ
Edge ํธํ ๋์
import { jwtVerify, SignJWT } from 'jose' // Web Crypto API ๊ธฐ๋ฐโ Middleware๊ฐ ์ ์ ํ์ผ ์์ฒญ์๋ ์คํ๋์ด ๋๋ ค์ง
์์ธ: matcher๋ฅผ ์ค์ ํ์ง ์์ CSS, JS, ์ด๋ฏธ์ง ํ์ผ ์์ฒญ์๋ Middleware๊ฐ ์คํ๋จ.
ํด๊ฒฐ์ฑ :
export const config = {
matcher: [
// _next ํ์ ์ ์ ํ์ผ๊ณผ favicon์ ์ ์ธ
'/((?!_next/static|_next/image|favicon.ico).*)',
],
}๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
๐ ํต์ฌ ๋ช ๋ น ํจํด
| ์ํฉ | ์ฝ๋ |
|---|---|
| ๋ค๋ฅธ URL๋ก ๋ณด๋ด๊ธฐ | NextResponse.redirect(new URL('/login', request.url)) |
| ๊ทธ๋ฅ ํต๊ณผ | NextResponse.next() |
| ์๋ต ํค๋ ์ถ๊ฐ | response.headers.set('X-Frame-Options', 'DENY') |
| ์ฟ ํค ์ฝ๊ธฐ | request.cookies.get('session')?.value |
| URL ๊ฒฝ๋ก ์ฝ๊ธฐ | request.nextUrl.pathname |
โ ๏ธ ์ ๋ ํ์ง ๋ง ๊ฒ
| ์ํฉ | โ ๋์ ์ | โ ์ข์ ์ |
|---|---|---|
| DB ์กฐํ | Middleware์์ Prisma ์ฌ์ฉ | ํ์ด์ง/API Route์์ ์ฒ๋ฆฌ |
| ๋ฌด๊ฑฐ์ด ์ํธํ | bcrypt.compare() in middleware | jose์ jwtVerify() |
| matcher ์์ | ๋ชจ๋ ๊ฒฝ๋ก์์ ์คํ | ์ ์ ํ์ผ ๊ฒฝ๋ก ์ ์ธ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. Middleware์์ ์ ๋๋ก ํ๋ฉด ์ ๋๋ ๊ฒ์?
- A) ์ฟ ํค์์ ์ธ์ ํ ํฐ ๊ฐ ์ฝ๊ธฐ
- B) ํน์ ๊ฒฝ๋ก๋ก ๋ฆฌ๋๋ ํธํ๊ธฐ
- C) Prisma๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํํ๊ธฐ
- D) ์๋ต ํค๋์ ๋ณด์ ํค๋ ์ถ๊ฐํ๊ธฐ
โ ์ ๋ต: C
์ค๋ต ํด์ค:
- A, D โ ์ฟ ํค ์ฝ๊ธฐ, ํค๋ ์ถ๊ฐ๋ Edge ํธํ ์์
- B โ ๋ฆฌ๋๋ ํธ๋ Middleware์ ํต์ฌ ๊ธฐ๋ฅ ์ค ํ๋
- C โ Prisma๋ TCP ์์ผ ์์กด. Edge Runtime์์ ๋ถ๊ฐ
๐ ํต์ฌ ๊ธฐ์ต๋ฒ: Middleware๋ "๊ฐ๋ณ๊ฒ, ๋น ๋ฅด๊ฒ, DB ์์ด."
Q2. ์๋ ๋น์นธ์ ์ฑ์๋ณด์.
๋ก๊ทธ์ธ ํ์ด์ง(/login)๋ ์ธ์
์์ด๋ ์ ๊ทผ ๊ฐ๋ฅํด์ผ ํ๋ค. ๋น์นธ์ ์ฑ์ ์ฌ๋ฐ๋ฅธ matcher๋ฅผ ์์ฑํด.
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|______).*)'],
}โ ์ ๋ต:
login(๋๋login|signup|api)ํด์ค: ๊ณต๊ฐ ๊ฒฝ๋ก๋ค์ matcher์์ ์ ์ธํด Middleware๊ฐ ์คํ๋์ง ์๊ฒ ํ๊ฑฐ๋, Middleware ๋ด๋ถ์์ PUBLIC_PATHS ๋ฐฐ์ด๋ก ํต๊ณผ ์ฒ๋ฆฌํด์ผ ํด.
Q3. ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
Middleware์ ๊ฐ ํ์ด์ง ์ปดํฌ๋ํธ ์์์ ์ธ์ฆ ์ฒดํฌ๋ฅผ ํ๋ ๊ฒ์ ์ฐจ์ด๋ฅผ ๋น์ ๋ก ์ค๋ช ํด๋ด.
์์ ๋ต๋ณ:
"Middleware๋ ๊ฑด๋ฌผ ์ ๊ตฌ ๊ฒฝ๋น์์ด์ผ. ๋ค์ด์ค๋ ๋ชจ๋ ์ฌ๋์ ํ ๋ฒ์ ์ฒดํฌํด. ๋ฐ๋ฉด ๊ฐ ํ์ด์ง์์ ์ฒดํฌํ๋ ๊ฑด ๊ฐ ๋ฐฉ๋ง๋ค ๊ฒฝ๋น์์ ์ธ์ฐ๋ ๊ฑฐ์ผ. 100๊ฐ ๋ฐฉ์ด๋ฉด 100๋ช . Middleware ํ๋๋ก ์ ๋ถ ๋ง๋ ๊ฒ ํจ์ฌ ํจ์จ์ ์ด์ง."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ์ ๋ง ๋ ๋ ํ '์์ ๊ฒฝ๋น์(Middleware)'์ ๊ณ ์ฉํ ๋ ์ด์ผ! ํ์ด์ง๊ฐ ๋์ด๋ ๋๋ง๋ค "๋ก๊ทธ์ธ ์ฒดํฌ ์ฝ๋๋ฅผ ๋ ์ด๋์ ๋ณต๋ถํด์ผ ํ์ง?" ๊ณ ๋ฏผํ๋ฉฐ ๋จธ๋ฆฌ๊ฐ ์ํ ๋๋ฐ, ํ ๊ณณ์์ ๋ชจ๋ ์ถ์ ์ ํต์ ํ ์ ์๋ค๋ ๊ฒ ์ผ๋ง๋ ํฐ ์ถ๋ณต์ธ์ง ๊นจ๋ฌ์์ด.
๐ก ์ค๋์ ๊ตํ: "๋ชจ๋ ๋ฐฉ๋ง๋ค ๊ฒฝ๋น์์ ์ธ์ฐ์ง ๋ง๊ณ ๊ฑด๋ฌผ ์ ๊ตฌ(Middleware)๋ฅผ ์งํค์. Edge ํ๊ฒฝ์ ๊ฐ๋ณ๊ณ ๋น ๋ฅด๊ฒ ์ ์งํ๋ ๊ฒ์ด ์๋ช ์ด๋ค!"
Edge Runtime์ด๋ผ๋ ์์ํ ํ๊ฒฝ์์ Prisma ๊ฐ์ ๋ฌด๊ฑฐ์ด ๋๊ตฌ๋ฅผ ๋ชป ์จ์ ์กฐ๊ธ ๋นํฉํ๊ธด ํ์ง๋ง, ์คํ๋ ค ๊ทธ ๋๋ถ์ "์ด๋ค ์ฝ๋๊ฐ ๊ฐ๋ณ๊ณ ๋น ๋ฅธ ์ฝ๋์ธ๊ฐ"์ ๋ํด ๊น๊ฒ ์๊ฐํด๋ณด๋ ๊ณ๊ธฐ๊ฐ ๋์ด. ์ค๋ ํ๋ฃจ ์ฐธ ๋ณด๋์ฐผ๋ค! ์ง์ ๊ฐ์ ์์ํ ์บ๋งฅ์ฃผ ํ๋ ๋ฐ๊ณ ์ํ๋ ํ ํธ ๋ณด๋ฉด์ ๊ธ์์ผ ๋ถ์๊ธฐ ๋ด์ผ์ง. ๋ด์ผ์ ๋ '๊ฐ๋ณ๊ณ ๊ฐ๋ ฅํ' ๋ก์ง์ ์ง๋ ๊ฐ๋ฐ์๊ฐ ๋์ด์ผ๊ฒ ์ด. ๐ฃ