5. Redirecting
Next.js에서는 리디렉션을 처리하는 다양한 방법이 존재한다. 이 페이지에서는 각 방법의 사용 가능한 옵션, 사용 사례 및 많은 수의 리디렉션을 관리하는 방법에 대해 설명하고 있다.
📌 리디렉션 방법 및 사용 위치
API | 목적 | 사용 가능한 위치 | 상태 코드 |
redirect |
데이터 변경(mutation or event, 예: 게시글 생성) 후 사용자 리디렉션 | Server Components, Server Actions, Route Handlers | 307(임시) 또는 303(Server Action) |
permanentRedirect |
데이터 변경(mutation or event, 예: 영구적인 URL 변경) 후 사용자 리디렉션 | Server Components, Server Actions, Route Handlers | 308(영구) |
useRouter |
클라이언트 사이드에서의 네비게이션 | 클라이언트 컴포넌트에서의 이벤트 핸들러 | N/A |
redirects in next.config.js |
요청된 경로를 다른 경로로 리디렉션 | next.config.js 파일 |
307(임시) 또는 308(영구) |
NextResponse.redirect |
특정 조건에 따라 리디렉션 | 미들웨어 | 모든 상태 코드 가능 |
🛠 redirect
함수
redirect
함수는 사용자를 특정 URL로 리디렉션하는 함수이다. Server Components, Route Handlers, Server Actions에서 호출 가능하다.
"use server";
import { redirect } from "next/navigation";
import { revalidatePath } from "next/cache";
export async function createPost(id: string) {
try {
// 데이터베이스 호출
} catch (error) {
// 오류 처리
}
revalidatePath("/posts"); // 캐시된 게시글 업데이트
redirect(`/post/${id}`); // 새 게시글 페이지로 이동
}
🔎 Good to know
- 기본적으로
redirect
는 307(Temporary Redirect) 상태 코드를 반환한다. 서버 액션에서 사용될 때는 POST 요청 결과로 성공 페이지로 리디렉션하는 데 일반적으로 사용되는 303(다른 페이지 참조)을 반환한다. redirect
는 내부적으로 예외를 발생시키므로try/catch
블록 밖에서 호출해야 한다.- 클라이언트 컴포넌트의 이벤트 핸들러에서는
redirect
를 사용할 수 없으며, 대신useRouter
를 사용해야 한다. redirect
는 절대 URL을 허용하며, 외부 링크로도 리디렉션할 수 있다.- render process 이전에 리디렉션하려면
next.config.js
또는 미들웨어를 사용하면 된다.
🛠 permanentRedirect
함수
permanentRedirect
함수는 사용자를 영구적으로 다른 URL로 리디렉션할 때 사용된다.
영구 리디렉션 (permanentRedirect)은 엔티티의 표준 URL을 변경하는 변이 (mutation) 또는 이벤트 후에 자주 사용된다. 예를 들어, 사용자가 사용자 이름을 변경한 후 사용자의 프로필 URL을 업데이트하는 경우에 사용된다.
해석 및 설명
permanentRedirect
는 보통 어떤 데이터(엔터티)의 URL이 변경된 후 사용됨.
예를 들어 사용자가 사용자명을 바꾸면 프로필 URL도 바뀔 수 있음.
그럴 때, 기존 URL에서 새로운 URL로 영구적으로 리디렉션(301 Redirect) 해주는 것
📌 예제로 쉽게 이해하기 (상황: 사용자 이름 변경)
1️⃣ 사용자의 기본 프로필 URL은 /profile/johndoe
2️⃣ 사용자가 johndoe → johnsmith로 이름 변경
3️⃣ 이제 새로운 프로필 URL은 /profile/johnsmith
4️⃣ 하지만 기존 URL (/profile/johndoe
)로 접속하면 새로운 URL로 영구 이동해야 함
5️⃣ 이때 permanentRedirect('/profile/johnsmith')
사용
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function updateUsername(username: string, formData: FormData) {
try {
// 데이터베이스 호출
} catch (error) {
// 오류 처리
}
revalidateTag('username') // 모든 관련 캐시 업데이트
permanentRedirect(`/profile/${username}`) // 새 프로필 페이지로 이동
}
🔎 Good to know
permanentRedirect
은 기본적으로 308(영구 리디렉션) 상태 코드를 반환한다.permanentRedirect
은 절대 URL을 허용하며, 외부 링크로 리디렉션할 수 있다.- 렌더링 프로세스 전에 리디렉션하려면
next.config.js
또는 미들웨어를 사용해야 한다.
🛠 useRouter() 훅
을 사용한 클라이언트 사이드 리디렉션
클라이언트 컴포넌트의 이벤트 핸들러에서 리디렉션하려면 useRouter
훅의 push
메서드를 사용해야 한다.
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
대시보드 이동
</button>
)
}
🔎 Good to know
useRouter().push()
를 사용하면 클라이언트 사이드 네비게이션이 수행된다.- 단순히 페이지 이동만 필요하다면
<Link>
컴포넌트를 사용하는 것이 더 적절하다.
🛠 next.config.js
에서 redirects
설정
next.config.js
파일의 redirects
옵션을 사용하면 들어오는 요청 경로를 다른 대상 경로로 리디렉션할 수 있다. 이는 페이지의 URL 구조를 변경하거나 미리 알고 있는 리디렉션 목록이 있는 경우에 유용하다. redirects
는 path, header, cookie 및 query matching를 지원하여, 들어오는 요청을 기반으로 사용자를 리디렉션할 수 있는 유연성을 제공한다.redirects
를 사용하려면 next.config.js
파일에 옵션을 추가하면 된다.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
async redirects() {
return [
// 기본 리디렉션
{
source: '/about',
destination: '/',
permanent: true, // 308 (영구)
},
// 와일드카드 경로 매칭
{
source: '/blog/:slug',
destination: '/news/:slug',
permanent: true,
},
]
},
}
export default nextConfig
🔎 Good to know
permanent
: true 설정 시 308(영구 리디렉션), false일 경우 307(임시 리디렉션) 상태 코드가 반환된다.- Vercel에서는 최대 1,024개의 리디렉션을 지원하며, 더 많은 리디렉션이 필요하면 미들웨어를 사용해야 한다.
🛠 미들웨어에서의 NextResponse.redirect
미들웨어에서는 특정 조건을 기반으로 리디렉션을 진행할 수 있다.
미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있다. 그런 다음 들어오는 요청을 기반으로 NextResponse.redirect
를 사용하여 다른 URL로 리디렉션할 수 있다.
이는 사용자를 조건(예: 인증, 세션 관리 등)에 따라 리디렉션하거나 많은 리디렉션을 수행해야 하는 경우 유용하다. (예를 들어, 로그인되지 않은 사용자를 로그인 페이지로 보내는 경우)
import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'
export function middleware(request: NextRequest) {
const isAuthenticated = authenticate(request)
if (isAuthenticated) {
return NextResponse.next() // 정상 요청 진행
}
return NextResponse.redirect(new URL('/login', request.url)) // 로그인 페이지로 리디렉션
}
export const config = {
matcher: '/dashboard/:path*', // 특정 경로에 대해 실행
}
🔎 Good to know
next.config.js
의 리디렉션보다 미들웨어가 후순위로 실행된다.
📌 대량의 리디렉션 관리 방법 (1000+ 개 이상)
리디렉션이 많아지면 성능 문제가 발생할 수 있다. 이를 해결하기 위해 미들웨어 + 데이터베이스(또는 JSON 파일) 방식으로 리디렉션을 처리할 수 있다.
1️⃣ redirect map (JSON 또는 데이터베이스) 생성 및 저장
redirect map은 데이터베이스(보통 키-값 저장소) 또는 JSON 파일에서 저장할 수 있는 리디렉션 목록
{
"/old": { "destination": "/new", "permanent": true },
"/blog/post-old": { "destination": "/blog/post-new", "permanent": true }
}
미들웨어에서 Vercel의 Edge Config 또는 Redis와 같은 데이터베이스에서 읽을 수 있으며, 들어오는 요청에 따라 사용자를 리디렉션할 수 있다.
2️⃣ 데이터 조회 성능 최적화 (Optimizing data lookup performance)
모든 들어오는 요청에 대해 큰 데이터 세트를 읽는 것은 느리고 비용이 많이 들 수 있다. 데이터 조회 성능을 최적화 할 수있는 두 가지 방법이 존재한다.
- 빠른 읽기 기능이 최적화된 데이터베이스(예: Vercel Edge Config 또는 Redis)를 사용
- Bloom 필터와 같은 데이터 조회 전략을 사용하여, 대규모 리다이렉트 파일 또는 데이터베이스를 읽기 전에 리다이렉트가 있는지 효율적으로 확인
이전 예제를 고려하면, 생성된 Bloom 필터 파일을 미들웨어에 가져와서 들어오는 요청 경로 이름이 Bloom 필터에 있는지 확인할 수 있다.
그렇지 않은 경우, 요청을 라우트 핸들러에 전달하여 실제 파일을 확인하고 사용자를 해당 URL로 리디렉션한다.
이렇게하면 대규모 리디렉션 파일을 미들웨어로 가져 오는 것을 피하면 모든 들어오는 요청이 느려질 수 있습니다.
설명 및 해석
Bloom Filter를 이용해 리디렉션을 최적화하는 방법을 설명 중.
미들웨어(Middleware) 에서 모든 리디렉트 정보를 직접 로드하는 대신, Bloom Filter를 사용해서 빠르게 존재 여부를 확인한 후, 실제로 리디렉션이 필요하면Route Handler
에서 처리하도록 분리하는 방식을 설명하고 있다.
🛠 예제로 쉽게 이해하기
1️⃣ 기본적인 문제: 대량의 리디렉션이 있으면 속도가 느려짐
예를 들어, 10만 개의 리디렉션 URL이 들어 있는 JSON 파일이 있다고 해보자.
이걸 Middleware에서 직접 불러오면, 매 요청마다 무거운 파일을 처리해야 하므로 속도가 느려짐.
2️⃣ 해결책: Bloom Filter 사용
Bloom Filter는 빠르게 데이터가 있는지 없는지만 판단할 수 있는 자료구조
💡 정확한 값을 반환하는 게 아니라 "있을 가능성이 있다" 정도만 확인 가능
💡 매우 가벼워서 속도가 빠름
➡ 이걸 Middleware에서 먼저 체크해서 빠른 필터링을 하고,
필요한 경우만 Route Handler에서 실제 파일을 확인하도록 하는 것!
middleware.ts
(Bloom Filter 미들웨어 (빠른 체크))
import { NextResponse , NextRequest } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'
type RedirectEntry = {
destination : string
permanent : boolean
}
// 생성된 JSON 파일에서 블룸 필터 초기화
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)
export async function middleware(request : NextRequest) {
// 들어오는 요청의 경로를 가져옵니다.
const pathname = request.nextUrl.pathname
// 경로가 블룸 필터에 있는지 확인합니다.
if (bloomFilter.has(pathname)) {
// 경로 관리를 위해 경로 이름을 전달합니다.
const api = new URL(
`/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
request.nextUrl.origin
)
try {
// 경로 관리자로부터 리디렉션 데이터를 가져옵니다.
const redirectData = await fetch(api)
if (redirectData.ok) {
const redirectEntry : RedirectEntry | undefined =
await redirectData.json()
if (redirectEntry) {
// 상태 코드를 결정합니다.
const statusCode = redirectEntry.permanent ? 308 : 307
// 목적지로 리디렉션합니다.
return NextResponse.redirect(redirectEntry.destination, statusCode)
}
}
} catch (error) {
console.error(error)
}
}
// 리디렉션을 찾지 못했습니다. 리디렉션 없이 요청을 계속 진행합니다.
return NextResponse.next()
}
app/api/redirects/route.ts
(실제 리디렉션 처리)
import { NextRequest , NextResponse } from 'next/server'
// 리디렉션 설정이 포함된 JSON 파일을 가져옵니다.
import redirects from '@/app/redirects/redirects.json'
// 리디렉션 엔트리의 타입을 정의합니다.
type RedirectEntry = {
destination: string // 리디렉션될 대상 URL
permanent: boolean // 영구 리디렉션 여부
}
// GET 요청을 처리하는 함수입니다.
export function GET (request: NextRequest) {
// 요청의 쿼리 매개변수에서 'pathname'을 가져옵니다.
const pathname = request.nextUrl.searchParams.get('pathname')
// 'pathname'이 제공되지 않은 경우, 400 Bad Request를 반환합니다.
if (!pathname) {
return new Response('Bad Request', { status: 400 })
}
// redirects.json 파일에서 해당 pathname에 대한 리디렉션 엔트리를 가져옵니다.
const redirect = (redirects as Record<string, RedirectEntry>)[pathname]
// Bloom 필터의 허위 긍정 결과를 고려하여 리디렉션이 없으면 400 반환합니다.
if (!redirect) {
return new Response('No redirect', { status: 400 })
}
// 리디렉션 엔트리를 JSON 형식으로 반환합니다.
return NextResponse.json(redirect)
}
🔎 Good to know
- 블룸 필터를 생성하려면
bloom-filters
와 같은 라이브러리를 사용할 수 있다. - 경로 핸들러에 대한 요청을 검증하여 악성 요청을 방지해야 한다.
결론
이와 같이 Next.js에서는 다양한 방법으로 리디렉션을 관리할 수 있으며, 규모에 따라 적절한 방법을 선택하는 것이 중요하다.
출처
https://nextjs.org/docs/app/building-your-application/routing/redirectin
Routing: Redirecting | Next.js
Learn the different ways to handle redirects in Next.js.
nextjs.org
'웹 프로그래밍 > Next.js' 카테고리의 다른 글
[Next.js 공식문서 정리] Routing : 08. Parallel Routes (1) | 2025.03.29 |
---|---|
[Next.js 공식문서 정리] Routing : 07. Dynamic Routes.md (0) | 2025.03.29 |
[Next.js 공식문서 정리] Routing : 06. Route Groups (0) | 2025.03.29 |
[Next.js 공식문서 정리] Routing : 04. Loading UI and Streaming (2) | 2025.03.29 |
[Next.js 공식문서 정리] Routing : 03. Error Handling (0) | 2025.03.29 |
[Next.js 공식문서 정리] Routing : 02. Linking and Navigating (0) | 2025.03.29 |