리다이렉트 (redirect)
웹 개발할 때, 사용자를 다른 페이지로 옮겨야 할 때가 있죠? 그럴 때 쓰는 게 바로 redirect
함수입니다. 이 함수는 사용자를 지정한 URL로 이동시키는 역할을 해요.
redirect
는 Server Components, Route Handlers, Server Actions 같은 서버 쪽에서 사용할 수 있어요. 그런데 쓰이는 상황에 따라 동작 방식이 약간 달라집니다.
- 스트리밍 컨텍스트에서 사용하면: 클라이언트 쪽에
<meta>
태그를 넣어서 브라우저가 자동으로 리다이렉트 하게끔 합니다. - 서버 액션(Server Action)에서 사용하면: HTTP 303 (See Other) 상태 코드를 보내서 호출자에게 리다이렉트를 하라고 알려줍니다.
- 그 외 상황에서는: HTTP 307 (Temporary Redirect) 상태 코드로 리다이렉트 합니다.
또한, 만약 요청한 리소스가 존재하지 않는 경우에는 redirect
대신 notFound
함수를 써서 404 페이지로 안내하는 게 깔끔한 방법입니다.
참고로 알아두면 좋을 팁
redirect
를 클라이언트 사이드에서만 쓰는 것과 서버 사이드에서 쓰는 것은 완전히 다르므로, 제대로 위치에 맞게 써야 합니다.- 303과 307 상태 코드는 둘 다 리다이렉트이지만, 303은 POST 요청을 GET 요청으로 바꾸어 다시 요청하게 합니다. 반면 307은 원래 요청 메서드를 유지해서 리다이렉트해요.
- next.js 같은 프레임워크에서 이 함수를 사용할 때는 내부 동작을 잘 이해하고 쓰면, 불필요한 데이터 로딩이나 렌더링을 줄일 수 있어 성능에도 도움이 됩니다.
필요할 때 적절하게 써서 사용자 경험을 더 깔끔하게 만들 수 있다는 점, 기억해두세요!
참고할 점: 서버 액션(Server Actions)과 라우트 핸들러(Route Handlers) 내에서 리다이렉트를 할 때는 try/catch 블록이 끝난 후에 호출하는 게 좋아요. 만약 307(임시 이동) 대신 308(영구 이동) HTTP 리다이렉트를 원한다면, redirect 함수 대신 permanentRedirect 함수를 사용하면 됩니다.
redirect 함수 파라미터
redirect 함수는 총 두 개의 인자를 받습니다:
redirect(path, type)
인자 | 설명 |
---|---|
path | 리다이렉트할 경로를 문자열 형태로 지정합니다. |
type | 리다이렉트 HTTP 상태 코드를 숫자로 지정합니다. (예: 307 또는 308) |
TIP!
리다이렉트를 try/catch 안에서 바로 쓰면 의도치 않은 에러가 발생할 수 있으니 반드시 try/catch 블록 밖에서 호출하는 걸 권장해요. 그리고 308 리다이렉트는 클라이언트 쪽에서 캐싱이 되므로, 영구적으로 주소가 변경되었을 때 활용하면 좋아요!
Parameter | Type | Description |
---|---|---|
path | string | 리다이렉트할 URL 경로입니다. 상대 경로나 절대 경로 모두 사용할 수 있어요. |
type | 'replace' (기본값) 또는 'push' (Server Actions에서 기본값) | 리다이렉트 방식입니다. |
기본적으로 Server Actions에서는 push
방식을 사용해서 브라우저 히스토리에 새 항목을 추가하고, 그 외 환경에서는 replace
방식으로 현재 URL을 교체합니다. 만약 원하는 동작이 다르다면 type
파라미터를 지정해서 이 동작을 덮어쓸 수 있어요.
참고로, Server Components 내에서 사용할 때는 type
파라미터는 영향을 미치지 않습니다.
여기서 push
와 replace
에 대해 간단히 정리해볼게요. push
는 사용자가 뒤로 가기 같은 내비게이션을 할 수 있게 히스토리에 새 페이지를 추가하는 방식이에요. 반면 replace
는 지금 페이지를 새로운 URL로 교체하기 때문에 뒤로 가기를 눌러도 이전 URL로 돌아가지 못하죠. 상황에 따라 적절한 방식을 선택하는 게 중요하니 꼭 기억해두세요!
redirect 함수가 값을 반환하지 않는다는 점, 다들 알고 계신가요?
예시
서버 컴포넌트(Server Component)
redirect() 함수를 호출하면 NEXT_REDIRECT라는 에러가 발생하면서, 해당 함수가 호출된 라우트 세그먼트의 렌더링이 바로 종료됩니다.
좀 더 쉽게 설명하자면, 이 redirect() 함수는 단순히 '여기서 멈추고 이 주소로 이동해!'라고 브라우저에게 알려주는 역할이에요. 그래서 값을 돌려주는 게 아니라, 내부적으로 예외를 던져서 렌더링 과정을 중단시키고 리다이렉트를 처리하죠.
이 방식 덕분에 서버 컴포넌트 내에서 복잡한 조건에 따라 바로 리다이렉트가 가능해서, 불필요한 렌더링을 하지 않아 성능 면에서도 이득을 볼 수 있답니다.
참고로, 만약 이 함수를 클라이언트 컴포넌트에서 사용하려고 하면 작동하지 않으니 주의하세요! 서버 컴포넌트 내에서만 제대로 작동하는 점 기억해 주세요.
이 코드는 Next.js 13에서 서버 컴포넌트 내에서 데이터를 불러오고, 만약 데이터가 없으면 리다이렉트를 하는 예제예요.
import { redirect } from 'next/navigation'
async function fetchTeam(id: string) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const team = await fetchTeam(id)
if (!team) {
redirect('/login')
}
// ...
}
핵심 내용
redirect()
함수는next/navigation
에서 제공하며, 조건에 따라 유저를 다른 페이지로 보내줄 때 사용해요.return redirect()
처럼 반환하지 않아도 돼요. 왜냐하면redirect()
함수가 TypeScript의never
타입을 사용해서, 그 이후 코드는 실행되지 않는 것으로 처리되거든요.params
가Promise<{ id: string }>
타입인 이유는 Next.js에서 동적 라우트에서 params를 비동기로 받을 때 이렇게 작성하는 경우가 있어요.- 데이터를 못 받아오면(
team
이 없으면) 로그인 페이지로 리다이렉트합니다.
추가 팁! Client Component에서의 redirect 사용
redirect()
함수는 서버 컴포넌트뿐만 아니라 클라이언트 컴포넌트에서도 바로 사용할 수 있어요. 보통 클라이언트 컴포넌트에서 리다이렉트를 하려면 useRouter()
의 push
메서드를 썼는데, 사실 Next.js 13에서는 redirect()
를 바로 써도 된답니다!
'use client'
import { redirect } from 'next/navigation'
export default function MyClientComponent() {
const isLoggedIn = false
if (!isLoggedIn) {
redirect('/login')
}
return <div>Welcome back!</div>
}
요약
기능 | 설명 |
---|---|
redirect() | 서버/클라이언트 컴포넌트 모두에서 사용 가능 |
반환값 없이 호출 가능 | TypeScript의 never 타입 덕분에 코드 중단 |
데이터 못받았을 때 사용 | 로그인 등 특정 페이지로 유도 가능 |
redirect
쓰면 조건에 따라 유저 경험을 자연스럽게 제어할 수 있어서, 로그인 상태 체크나 접근 권한 관리할 때 아주 유용하니 꼭 잘 익혀두세요!
Next.js 13에서 클라이언트 컴포넌트에서의 리다이렉션 처리 방법에 대해 이야기해볼게요.
기본 예제: 클라이언트에서 조건부 리다이렉트
'use client'
import { redirect, usePathname } from 'next/navigation'
export function ClientRedirect() {
const pathname = usePathname()
if (pathname.startsWith('/admin') && !pathname.includes('/login')) {
redirect('/admin/login')
}
return <div>Login Page</div>
}
이 코드의 핵심은 usePathname
을 사용해서 현재 경로를 가져온 다음, /admin
경로면서 로그인 페이지가 아니면 바로 /admin/login
으로 리다이렉트하는 거예요.
여기서 주의할 점은, 클라이언트 컴포넌트에서
redirect
를 사용할 때 초기 SSR(Server-Side Rendering) 중에 호출되면 서버 사이드에서 리다이렉트가 일어난다는 사실이에요. 그래서 빠르고 깔끔한 리다이렉션 처리가 가능하죠.
이벤트 핸들러에서 리다이렉트하려면?
만약 버튼 클릭 같은 이벤트 핸들러 안에서 리다이렉트를 하고 싶다면, 그냥 이 코드처럼 하면 안 돼요:
const pathname = usePathname();
if (...) {
redirect('/somewhere')
}
왜냐하면 redirect()
는 바로 리다이렉트가 일어나는데, 이벤트 핸들러는 비동기 처리 같은 다른 패턴을 사용해야 하거든요.
대신, useRouter
훅을 써서 프로그래밍적으로 경로를 이동하거나, 서버 액션(Server Action)을 활용하는 방식을 추천해요.
서버 액션과 폼 제출로 리다이렉트 하기
아래 예시는 서버 액션을 이용해서 폼 제출 시 리다이렉트 하는 방식입니다.
'use client'
import { navigate } from './actions'
export function ClientRedirect() {
return (
<form action={navigate}>
<input type="text" name="id" />
<button>Submit</button>
</form>
)
}
이때 navigate
는 서버 액션에서 클라이언트로 리다이렉트를 수행하도록 구현해야 해요. 이렇게 하면 폼을 제출하는 순간 서버 액션이 실행되고, 그 결과로 리다이렉트가 처리될 수 있죠.
정리
상황 | 사용법 | 참고사항 |
---|---|---|
클라이언트 컴포넌트에서 초기 경로 감지 후 리다이렉트 | usePathname + redirect() | 초기 SSR 시 서버에서 리다이렉트 된다 |
이벤트 핸들러 내에서 리다이렉트 | useRouter().push() 또는 서버 액션 활용 | redirect() 직접 호출은 불가, useRouter 가 더 적합 |
폼 제출 후 서버에서 처리하며 리다이렉트 | 폼의 action 으로 서버 액션 지정 | 서버 액션 내에서 리다이렉트 처리 가능 |
추가 팁
useRouter
의push
메서드는 클라이언트 사이드 네비게이션을 수행해서 페이지 전환 시 전체 페이지 리로딩이 없고 더 부드러운 사용자 경험을 제공해요.- 서버 액션은 Next.js 13에서 도입된 개념인데, 클라이언트와 서버 코드를 자연스럽게 연동할 수 있도록 해 줘서 폼 제출이나 데이터 처리 후 리다이렉션, 데이터 갱신 작업에 아주 유용해요.
이렇게 클라이언트 컴포넌트에서 리다이렉트를 다룰 때는 렌더링 시점과 이벤트 처리 방식에 따라 적합한 방법을 선택하는 게 중요해요! 다음 프로젝트에서 바로 써먹어 보세요 :)
'use server'
import { redirect } from 'next/navigation'
export async function navigate(data: FormData) {
redirect(`/posts/${data.get('id')}`)
}
FAQ
redirect가 왜 307과 308 상태 코드를 사용하는 걸까?
redirect() 함수를 사용할 때, 상태 코드로 307(임시 리다이렉트)과 308(영구 리다이렉트)을 볼 수 있는데요. 사실 전통적으로는 임시 리다이렉트에 302, 영구 리다이렉트에 301 코드를 사용했었어요. 그런데 문제는, 많은 브라우저가 302를 이용한 리다이렉트 시 원래 요청 방식이 POST였어도 GET으로 바꿔버린다는 점이에요.
즉, POST 요청을 302 리다이렉트 하면, 브라우저가 POST가 아닌 GET 요청으로 전환해버려 의도한 동작과 달라질 수 있죠. 그래서 HTTP 명세에 따라, 원래 요청 메서드를 그대로 유지하며 리다이렉트 하도록 307(임시)과 308(영구)이 나온 거예요.
이렇게 307과 308 코드를 사용하면 POST 요청이 리다이렉트 될 때도 POST를 유지하기 때문에, 서버와 클라이언트 모두 예상 외 문제 없이 정상 동작할 수 있답니다.
추가로, 만약 여러분이 직접 리다이렉트 코드를 구현하거나 Next.js 같은 프레임워크 내부 동작을 이해하는 데 관심 있다면, 이런 상태 코드의 차이와 의미를 꼭 알아두는 게 좋아요. 특히 API 서버를 설계할 때, 데이터가 POST 요청으로 들어올 때 리다이렉트 후에도 메서드가 유지되어야 하는 상황이라면 307/308 상태 코드를 잘 활용하는 것이 중요합니다!
예를 들어, /users에서 /people로 리다이렉트하는 상황을 생각해볼게요. 만약 새로운 사용자를 만들기 위해 /users에 POST 요청을 보냈는데, 302 임시 리다이렉트 방식을 따르면 요청 메서드가 POST에서 GET으로 바뀌어 버려요. 이건 좀 말이 안 되죠. 왜냐하면, 새로운 사용자를 만들려면 /people에 POST 요청을 해야 하는데, 잘못 GET 요청이 되어버리니까요.
그래서 나온 게 307 상태 코드입니다. 이 코드는 요청 메서드를 POST로 그대로 유지시켜줘요.
상태 코드 | 설명 |
---|---|
302 | 임시 리다이렉트, POST를 GET으로 변경 |
307 | 임시 리다이렉트, 요청 메서드 그대로 유지 (POST 유지) |
그리고 요즘 많이 사용하는 redirect() 메서드는 기본적으로 307 상태 코드를 사용해요. 덕분에 POST 요청이 리다이렉트돼도 메서드가 변경되지 않는 거죠.
참고로 303 상태 코드도 있는데요, 이건 POST 요청 후 다른 페이지로 리다이렉트할 때 주로 사용되고, 이때는 GET 방식으로 변경됩니다. 즉, 303은 'POST-리스폰스-GET' 패턴을 강제하는 느낌이고, 307은 메서드를 그대로 유지하려는 상황에 딱 맞아요.
이게 실제로 개발할 때 헷갈릴 수 있는데, 특히 API 서버를 만들거나, 폼 전송을 리다이렉트해서 처리할 때 꼭 알아두면 좋아요. 307을 사용하면 예상치 못한 요청 메서드 변경 때문에 발생하는 문제를 미연에 방지할 수 있거든요.
HTTP 리다이렉트에 대해 더 알아보기
버전 히스토리
버전 | 변경 사항 |
---|---|
v13.0.0 | redirect 기능 추가 |
HTTP 리다이렉트는 웹 개발할 때 정말 자주 마주치는 기능 중 하나예요. 간단히 말해서, 특정 URL에서 다른 URL로 자동으로 이동시키는 걸 말하죠. 예를 들어, 예전 주소에서 새 주소로 방문자를 안내하거나, HTTP에서 HTTPS로 보안 연결을 강제할 때도 사용하구요.
이번에 달라진 v13.0.0 버전에서는 redirect
기능이 새롭게 도입됐다고 하네요. 이 기능 덕분에 리다이렉트 구현이 더 간편해졌을 거라 기대됩니다. 코드를 깔끔하게 관리할 수 있고, 사용자 경험도 개선할 수 있으니까요.
만약 여러분이 서버 사이드나 프론트엔드 라우팅에서 리다이렉트를 다루고 있다면, 이번 업데이트 내용을 꼭 체크해 보세요! 혹시 리다이렉트를 처음 접한다면, 기본적인 301(영구 이동), 302(임시 이동) 상태 코드부터 이해하는 게 좋아요. 이를 통해 SEO 최적화도 잘 할 수 있답니다.
더 궁금한 점이나 구현 팁이 필요하면 언제든 물어봐 주세요!