πŸŽ‰ berenickt λΈ”λ‘œκ·Έμ— 온 κ±Έ ν™˜μ˜ν•©λ‹ˆλ‹€. πŸŽ‰
Front
NextJs
06-redirecting

Redirecting

Next.jsμ—μ„œ λ¦¬λ””λ ‰μ…˜μ„ μ²˜λ¦¬ν•  수 μžˆλŠ” λͺ‡ κ°€μ§€ 방법이 μžˆμŠ΅λ‹ˆλ‹€. 이 νŽ˜μ΄μ§€μ—μ„œλŠ” μ‚¬μš© κ°€λŠ₯ν•œ 각 μ˜΅μ…˜, μ‚¬μš© 사둀 및 λŒ€λŸ‰μ˜ λ¦¬λ””λ ‰μ…˜μ„ κ΄€λ¦¬ν•˜λŠ” 방법을 μ‚΄νŽ΄λ΄…λ‹ˆλ‹€.

APIλͺ©μ μ–΄λ””μ—μ„œ(where)μƒνƒœ μ½”λ“œ
redirectλ³€κ²½ λ˜λŠ” 이벀트 λ°œμƒ ν›„ μ‚¬μš©μž λ¦¬λ””λ ‰μ…˜μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ, μ„œλ²„ μ•‘μ…˜, 라우트 ν•Έλ“€λŸ¬307(μž„μ‹œ) λ˜λŠ” 303(μ„œλ²„ λ™μž‘)
permanentRedirectλ³€κ²½ λ˜λŠ” 이벀트 λ°œμƒ ν›„ μ‚¬μš©μž λ¦¬λ””λ ‰μ…˜μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ, μ„œλ²„ μ•‘μ…˜, 라우트 ν•Έλ“€λŸ¬308 (Permanent)
useRouterν΄λΌμ΄μ–ΈνŠΈ μΈ‘ 탐색 μˆ˜ν–‰ν΄λΌμ΄μ–ΈνŠΈ μ»΄ν¬λ„ŒνŠΈμ˜ 이벀트 ν•Έλ“€λŸ¬N/A
redirects in next.config.js경둜λ₯Ό 기반으둜 λ“€μ–΄μ˜€λŠ” μš”μ²­ λ¦¬λ””λ ‰μ…˜next.config.js file307(μž„μ‹œ) λ˜λŠ” 308(영ꡬ)
NextResponse.redirect쑰건에 따라 μˆ˜μ‹  μš”μ²­ λ¦¬λ””λ ‰μ…˜ν•˜κΈ°MiddlewareAny

1. redirect ν•¨μˆ˜

λ¦¬λ””λ ‰μ…˜ κΈ°λŠ₯을 μ‚¬μš©ν•˜λ©΄ μ‚¬μš©μžλ₯Ό λ‹€λ₯Έ URL둜 λ¦¬λ””λ ‰μ…˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ, 라우트 ν•Έλ“€λŸ¬, μ„œλ²„ μ•‘μ…˜μ—μ„œ λ¦¬λ””λ ‰μ…˜μ„ ν˜ΈμΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ¦¬λ””λ ‰μ…˜μ€ μ’…μ’… λ³€κ²½μ΄λ‚˜ 이벀트 이후에 μ‚¬μš©λ©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, κΈ€ μž‘μ„±κ³Ό 같은 κ²½μš°μž…λ‹ˆλ‹€:

app/actions.tsx
1
'use server'
2
3
import { redirect } from 'next/navigation'
4
import { revalidatePath } from 'next/cache'
5
6
export async function createPost(id: string) {
7
try {
8
// Call database
9
} catch (error) {
10
// Handle errors
11
}
12
13
revalidatePath('/posts') // Update cached posts
14
redirect(`/post/${id}`) // Navigate to the new post page
15
}

μ•Œμ•„λ‘λ©΄ μœ μš©ν•©λ‹ˆλ‹€:

  • redirectλŠ” 기본적으둜 307(μž„μ‹œ λ¦¬λ””λ ‰μ…˜) μƒνƒœ μ½”λ“œλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
    • μ„œλ²„ μ•‘μ…˜μ—μ„œ μ‚¬μš©ν•˜λ©΄ 303(기타 μ°Έμ‘°)을 λ°˜ν™˜ν•˜λ©°,
    • μ΄λŠ” 일반적으둜 POST μš”μ²­μ˜ 결과둜 성곡 νŽ˜μ΄μ§€λ‘œ λ¦¬λ””λ ‰μ…˜ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
  • redirectλŠ” λ‚΄λΆ€μ μœΌλ‘œ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚€λ―€λ‘œ μ‹œλ„/작기 블둝 μ™ΈλΆ€μ—μ„œ ν˜ΈμΆœν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • redirectλŠ” λ Œλ”λ§ ν”„λ‘œμ„ΈμŠ€ 쀑 ν΄λΌμ΄μ–ΈνŠΈ μ»΄ν¬λ„ŒνŠΈμ—μ„œ ν˜ΈμΆœν•  수 μžˆμ§€λ§Œ 이벀트 ν•Έλ“€λŸ¬μ—μ„œλŠ” ν˜ΈμΆœν•  수 μ—†μŠ΅λ‹ˆλ‹€.
    • λŒ€μ‹  useRouter 훅을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • redirectλŠ” μ ˆλŒ€ URL도 ν—ˆμš©ν•˜λ©° μ™ΈλΆ€ 링크둜 λ¦¬λ””λ ‰μ…˜ν•˜λŠ” 데 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ Œλ”λ§ ν”„λ‘œμ„ΈμŠ€ 전에 λ¦¬λ””λ ‰μ…˜ν•˜λ €λ©΄ next.config.js λ˜λŠ” 미듀웨어λ₯Ό μ‚¬μš©ν•˜μ„Έμš”.

μžμ„Έν•œ λ‚΄μš©μ€ redirect API μ°Έμ‘°λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.


2. permanentRedirect ν•¨μˆ˜

permanentRedirect ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ μ‚¬μš©μžλ₯Ό λ‹€λ₯Έ URL둜 영ꡬ적으둜 λ¦¬λ””λ ‰μ…˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ, 라우트 ν•Έλ“€λŸ¬, μ„œλ²„ μ•‘μ…˜μ—μ„œ permanentRedirect을 ν˜ΈμΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.

permanentRedirect은 μ‚¬μš©μž 이름을 λ³€κ²½ν•œ ν›„ μ‚¬μš©μžμ˜ ν”„λ‘œν•„ URL을 μ—…λ°μ΄νŠΈν•˜λŠ” λ“± μ—”ν‹°ν‹°μ˜ 정식 URL을 λ³€κ²½ν•˜λŠ” λ³€κ²½ λ˜λŠ” 이벀트 후에 자주 μ‚¬μš©λ©λ‹ˆλ‹€:

app/actions.ts
1
'use server'
2
3
import { permanentRedirect } from 'next/navigation'
4
import { revalidateTag } from 'next/cache'
5
6
export async function updateUsername(username: string, formData: FormData) {
7
try {
8
// Call database
9
} catch (error) {
10
// Handle errors
11
}
12
13
revalidateTag('username') // Update all references to the username
14
permanentRedirect(`/profile/${username}`) // Navigate to the new user profile
15
}

μ•Œμ•„λ‘λ©΄ μœ μš©ν•©λ‹ˆλ‹€:

  • permanentRedirect은 기본적으둜 308(영ꡬ λ¦¬λ””λ ‰μ…˜) μƒνƒœ μ½”λ“œλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • permanentRedirect은 μ ˆλŒ€ URL도 ν—ˆμš©ν•˜λ©° μ™ΈλΆ€ 링크둜 λ¦¬λ””λ ‰μ…˜ν•˜λŠ” 데 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ Œλ”λ§ ν”„λ‘œμ„ΈμŠ€ 전에 λ¦¬λ””λ ‰μ…˜ν•˜λ €λ©΄, next.config.js λ˜λŠ” 미듀웨어λ₯Ό μ‚¬μš©ν•˜μ„Έμš”.

μžμ„Έν•œ λ‚΄μš©μ€ permanentRedirect API μ°Έμ‘°λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.


3. useRouter() ν›…

ν΄λΌμ΄μ–ΈνŠΈ μ»΄ν¬λ„ŒνŠΈμ˜ 이벀트 ν•Έλ“€λŸ¬ λ‚΄λΆ€μ—μ„œ λ¦¬λ””λ ‰μ…˜ν•΄μ•Ό ν•˜λŠ” 경우, useRouter hook의 push λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄

app/page.tsx
1
'use client'
2
3
import { useRouter } from 'next/navigation'
4
5
export default function Page() {
6
const router = useRouter()
7
8
return (
9
<button type="button" onClick={() => router.push('/dashboard')}>
10
Dashboard
11
</button>
12
)
13
}

μ•Œμ•„λ‘λ©΄ μœ μš©ν•©λ‹ˆλ‹€:

μ‚¬μš©μžλ₯Ό ν”„λ‘œκ·Έλž˜λ° λ°©μ‹μœΌλ‘œ 탐색할 ν•„μš”κ°€ μ—†λŠ” 경우, <Link> μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

μžμ„Έν•œ λ‚΄μš©μ€ useRouter API μ°Έμ‘°λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.


4. next.config.js μ•ˆμ˜ redirects

next.config.js 파일의 redirects μ˜΅μ…˜μ„ μ‚¬μš©ν•˜λ©΄ λ“€μ–΄μ˜€λŠ” μš”μ²­ 경둜λ₯Ό λ‹€λ₯Έ λŒ€μƒ 경둜둜 λ¦¬λ””λ ‰μ…˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. νŽ˜μ΄μ§€μ˜ URL ꡬ쑰λ₯Ό λ³€κ²½ν•˜κ±°λ‚˜ 미리 μ•Œκ³  μžˆλŠ” λ¦¬λ””λ ‰μ…˜ λͺ©λ‘μ΄ μžˆμ„ λ•Œ μœ μš©ν•©λ‹ˆλ‹€.

redirects은 경둜, 헀더, μΏ ν‚€, 쿼리 맀칭을 μ§€μ›ν•˜λ―€λ‘œ μˆ˜μ‹  μš”μ²­μ— 따라 μœ μ—°ν•˜κ²Œ μ‚¬μš©μžλ₯Ό λ¦¬λ””λ ‰μ…˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

redirects을 μ‚¬μš©ν•˜λ €λ©΄, next.config.js νŒŒμΌμ— μ˜΅μ…˜μ„ μΆ”κ°€ν•˜μ„Έμš”:

next.config.js
1
module.exports = {
2
async redirects() {
3
return [
4
// Basic redirect
5
{
6
source: '/about',
7
destination: '/',
8
permanent: true,
9
},
10
// Wildcard path matching
11
{
12
source: '/blog/:slug',
13
destination: '/news/:slug',
14
permanent: true,
15
},
16
]
17
},
18
}

μžμ„Έν•œ λ‚΄μš©μ€ redirects API μ°Έμ‘°λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.

μ•Œμ•„λ‘λ©΄ μœ μš©ν•©λ‹ˆλ‹€:

  • redirects은 영ꡬ μ˜΅μ…˜μœΌλ‘œ 307(μž„μ‹œ λ¦¬λ””λ ‰μ…˜) λ˜λŠ” 308(영ꡬ λ¦¬λ””λ ‰μ…˜) μƒνƒœ μ½”λ“œλ₯Ό λ°˜ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • redirects은 ν”Œλž«νΌμ— 따라 μ œν•œμ΄ μžˆμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.
    • 예λ₯Ό λ“€μ–΄, Vercelμ—μ„œλŠ” λ¦¬λ””λ ‰μ…˜ μˆ˜κ°€ 1,024개둜 μ œν•œλ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.
    • λ§Žμ€ 수의 λ¦¬λ””λ ‰μ…˜(1000개 이상)을 κ΄€λ¦¬ν•˜λ €λ©΄, 미듀웨어λ₯Ό μ‚¬μš©ν•˜μ—¬ μ‚¬μš©μž μ§€μ • μ†”λ£¨μ…˜μ„ λ§Œλ“œλŠ” 것을 κ³ λ €ν•˜μ„Έμš”.
    • μžμ„Έν•œ λ‚΄μš©μ€ λŒ€κ·œλͺ¨ λ¦¬λ””λ ‰μ…˜ κ΄€λ¦¬ν•˜κΈ°μ ˆμ„ μ°Έμ‘°ν•˜μ„Έμš”.
  • redirects은 미듀웨어보닀 λ¨Όμ € μ‹€ν–‰λ©λ‹ˆλ‹€.

5. Middleware μ•ˆμ˜ NextResponse.redirect

미듀웨어λ₯Ό μ‚¬μš©ν•˜λ©΄ μš”μ²­μ΄ μ™„λ£Œλ˜κΈ° 전에 μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그런 λ‹€μŒ λ“€μ–΄μ˜€λŠ” μš”μ²­μ— 따라 NextResponse.redirectλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‹€λ₯Έ URL둜 λ¦¬λ””λ ‰μ…˜ν•©λ‹ˆλ‹€. μ΄λŠ” 쑰건(예: 인증, μ„Έμ…˜ 관리 λ“±)에 따라 μ‚¬μš©μžλ₯Ό λ¦¬λ””λ ‰μ…˜ν•˜κ±°λ‚˜ λ¦¬λ””λ ‰μ…˜ νšŸμˆ˜κ°€ λ§Žμ„ λ•Œ μœ μš©ν•©λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄, μ‚¬μš©μžκ°€ μΈμ¦λ˜μ§€ μ•Šμ€ 경우 /login νŽ˜μ΄μ§€λ‘œ λ¦¬λ””λ ‰μ…˜ν•©λ‹ˆλ‹€:

middleware.ts
1
import { NextResponse, NextRequest } from 'next/server'
2
import { authenticate } from 'auth-provider'
3
4
export function middleware(request: NextRequest) {
5
const isAuthenticated = authenticate(request)
6
7
// If the user is authenticated, continue as normal
8
if (isAuthenticated) {
9
return NextResponse.next()
10
}
11
12
// Redirect to login page if not authenticated
13
return NextResponse.redirect(new URL('/login', request.url))
14
}
15
16
export const config = {
17
matcher: '/dashboard/:path*',
18
}

μ•Œμ•„λ‘λ©΄ μœ μš©ν•©λ‹ˆλ‹€:

λ―Έλ“€μ›¨μ–΄λŠ” next.config.js의 redirects ν›„ λ Œλ”λ§ 전에 μ‹€ν–‰λ©λ‹ˆλ‹€.

μžμ„Έν•œ λ‚΄μš©μ€ 미듀웨어 μ„€λͺ…μ„œλ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.


6. λŒ€κ·œλͺ¨ λ¦¬λ””λ ‰μ…˜ 관리(κ³ κΈ‰)

λ§Žμ€ 수의 λ¦¬λ””λ ‰μ…˜(1000개 이상)을 κ΄€λ¦¬ν•˜λ €λ©΄ 미듀웨어λ₯Ό μ‚¬μš©ν•˜μ—¬ μ‚¬μš©μž μ§€μ • μ†”λ£¨μ…˜μ„ λ§Œλ“œλŠ” 것을 κ³ λ €ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄, μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ‹€μ‹œ 배포할 ν•„μš” 없이 ν”„λ‘œκ·Έλž˜λ° λ°©μ‹μœΌλ‘œ λ¦¬λ””λ ‰μ…˜μ„ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λ ‡κ²Œ ν•˜λ €λ©΄ λ‹€μŒ 사항을 κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€:

  1. λ¦¬λ””λ ‰μ…˜ λ§΅ 생성 및 μ €μž₯.
  2. 데이터 쑰회 μ„±λŠ₯ μ΅œμ ν™”.

Next.js 예제: μ•„λž˜ ꢌμž₯ μ‚¬ν•­μ˜ κ΅¬ν˜„μ— λŒ€ν•œ μ˜ˆμ œλŠ” Bloom ν•„ν„°κ°€ ν¬ν•¨λœ 미듀웨어 예제λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”.


6.1 λ¦¬λ””λ ‰μ…˜ λ§΅ 생성 및 μ €μž₯

λ¦¬λ””λ ‰μ…˜ 맡은 λ°μ΄ν„°λ² μ΄μŠ€(일반적으둜 ν‚€-κ°’ μ €μž₯μ†Œ) λ˜λŠ” JSON νŒŒμΌμ— μ €μž₯ν•  수 μžˆλŠ” λ¦¬λ””λ ‰μ…˜ λͺ©λ‘μž…λ‹ˆλ‹€.

λ‹€μŒ 데이터 ꡬ쑰λ₯Ό κ³ λ €ν•˜μ„Έμš”:

1
{
2
"/old": {
3
"destination": "/new",
4
"permanent": true
5
},
6
"/blog/post-old": {
7
"destination": "/blog/post-new",
8
"permanent": true
9
}
10
}

λ―Έλ“€μ›¨μ–΄μ—μ„œλŠ”, Vercel의 Edge Config λ˜λŠ” Redis와 같은 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 읽고 λ“€μ–΄μ˜€λŠ” μš”μ²­μ— 따라 μ‚¬μš©μžλ₯Ό λ¦¬λ””λ ‰μ…˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

middleware.ts
1
import { NextResponse, NextRequest } from 'next/server'
2
import { get } from '@vercel/edge-config'
3
4
type RedirectEntry = {
5
destination: string
6
permanent: boolean
7
}
8
9
export async function middleware(request: NextRequest) {
10
const pathname = request.nextUrl.pathname
11
const redirectData = await get(pathname)
12
13
if (redirectData && typeof redirectData === 'string') {
14
const redirectEntry: RedirectEntry = JSON.parse(redirectData)
15
const statusCode = redirectEntry.permanent ? 308 : 307
16
return NextResponse.redirect(redirectEntry.destination, statusCode)
17
}
18
19
// No redirect found, continue without redirecting
20
return NextResponse.next()
21
}

6.2 데이터 쑰회 μ„±λŠ₯ μ΅œμ ν™”

λ“€μ–΄μ˜€λŠ” λͺ¨λ“  μš”μ²­μ— λŒ€ν•΄ λŒ€κ·œλͺ¨ 데이터 집합을 μ½λŠ” 것은 느리고 λΉ„μš©μ΄ 많이 λ“€ 수 μžˆμŠ΅λ‹ˆλ‹€. 데이터 쑰회 μ„±λŠ₯을 μ΅œμ ν™”ν•  수 μžˆλŠ” 두 κ°€μ§€ 방법이 μžˆμŠ΅λ‹ˆλ‹€:

  • λΉ λ₯Έ 읽기에 μ΅œμ ν™”λœ λ°μ΄ν„°λ² μ΄μŠ€(예: Vercel Edge Config λ˜λŠ” Redis)λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.
  • λΈ”λ£Έ 필터와 같은 데이터 쑰회 μ „λž΅μ„ μ‚¬μš©ν•˜μ—¬ 더 큰 λ¦¬λ””λ ‰μ…˜ νŒŒμΌμ΄λ‚˜ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 읽기 전에 λ¦¬λ””λ ‰μ…˜μ΄ μ‘΄μž¬ν•˜λŠ”μ§€ 효율적으둜 ν™•μΈν•©λ‹ˆλ‹€.

μ•žμ˜ μ˜ˆμ‹œλ₯Ό κ³ λ €ν•˜λ©΄ μƒμ„±λœ λΈ”λ£Έ ν•„ν„° νŒŒμΌμ„ λ―Έλ“€μ›¨μ–΄λ‘œ κ°€μ Έμ˜¨ λ‹€μŒ λ“€μ–΄μ˜€λŠ” μš”μ²­ 경둜λͺ…이 λΈ”λ£Έ 필터에 μ‘΄μž¬ν•˜λŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

μ‘΄μž¬ν•˜λŠ” 경우 μ‹€μ œ νŒŒμΌμ„ ν™•μΈν•˜κ³  μ‚¬μš©μžλ₯Ό μ μ ˆν•œ URL둜 λ¦¬λ””λ ‰μ…˜ν•˜λŠ” 라우트 ν•Έλ“€λŸ¬λ‘œ μš”μ²­μ„ μ „λ‹¬ν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ λͺ¨λ“  μˆ˜μ‹  μš”μ²­μ˜ 속도λ₯Ό μ €ν•˜μ‹œν‚¬ 수 μžˆλŠ” λŒ€μš©λŸ‰ λ¦¬λ””λ ‰μ…˜ νŒŒμΌμ„ λ―Έλ“€μ›¨μ–΄λ‘œ κ°€μ Έμ˜€λŠ” 것을 λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

middleware.ts
1
import { NextResponse, NextRequest } from 'next/server'
2
import { ScalableBloomFilter } from 'bloom-filters'
3
import GeneratedBloomFilter from './redirects/bloom-filter.json'
4
5
type RedirectEntry = {
6
destination: string
7
permanent: boolean
8
}
9
10
// Initialize bloom filter from a generated JSON file
11
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)
12
13
export async function middleware(request: NextRequest) {
14
// Get the path for the incoming request
15
const pathname = request.nextUrl.pathname
16
17
// Check if the path is in the bloom filter
18
if (bloomFilter.has(pathname)) {
19
// Forward the pathname to the Route Handler
20
const api = new URL(
21
`/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
22
request.nextUrl.origin,
23
)
24
25
try {
26
// Fetch redirect data from the Route Handler
27
const redirectData = await fetch(api)
28
29
if (redirectData.ok) {
30
const redirectEntry: RedirectEntry | undefined = await redirectData.json()
31
32
if (redirectEntry) {
33
// Determine the status code
34
const statusCode = redirectEntry.permanent ? 308 : 307
35
36
// Redirect to the destination
37
return NextResponse.redirect(redirectEntry.destination, statusCode)
38
}
39
}
40
} catch (error) {
41
console.error(error)
42
}
43
}
44
45
// No redirect found, continue the request without redirecting
46
return NextResponse.next()
47
}

그런 λ‹€μŒ Route Handlerμ—μ„œ:

app/redirects/route.ts
1
import { NextRequest, NextResponse } from 'next/server'
2
import redirects from '@/app/redirects/redirects.json'
3
4
type RedirectEntry = {
5
destination: string
6
permanent: boolean
7
}
8
9
export function GET(request: NextRequest) {
10
const pathname = request.nextUrl.searchParams.get('pathname')
11
if (!pathname) {
12
return new Response('Bad Request', { status: 400 })
13
}
14
15
// Get the redirect entry from the redirects.json file
16
const redirect = (redirects as Record<string, RedirectEntry>)[pathname]
17
18
// Account for bloom filter false positives
19
if (!redirect) {
20
return new Response('No redirect', { status: 400 })
21
}
22
23
// Return the redirect entry
24
return NextResponse.json(redirect)
25
}

μ•Œμ•„λ‘λ©΄ μœ μš©ν•©λ‹ˆλ‹€:

  • bloom-filtersλ₯Ό μƒμ„±ν•˜λ €λ©΄ λΈ”λ£Έ 필터와 같은 라이브러리λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹ˆλ‹€.
  • μ•…μ˜μ μΈ μš”μ²­μ„ λ°©μ§€ν•˜κΈ° μœ„ν•΄ 라우트 ν•Έλ“€λŸ¬μ— λŒ€ν•œ μš”μ²­μ˜ μœ νš¨μ„±μ„ 검사해야 ν•©λ‹ˆλ‹€.