Next.js 15에서 Redirect 기능 구현하는 방법

Next.js 15에서 Redirect 기능 구현하는 방법
TILPosted On Apr 22, 202512 min read

리다이렉트 (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 리다이렉트는 클라이언트 쪽에서 캐싱이 되므로, 영구적으로 주소가 변경되었을 때 활용하면 좋아요!

ParameterTypeDescription
pathstring리다이렉트할 URL 경로입니다. 상대 경로나 절대 경로 모두 사용할 수 있어요.
type'replace' (기본값) 또는 'push' (Server Actions에서 기본값)리다이렉트 방식입니다.

기본적으로 Server Actions에서는 push 방식을 사용해서 브라우저 히스토리에 새 항목을 추가하고, 그 외 환경에서는 replace 방식으로 현재 URL을 교체합니다. 만약 원하는 동작이 다르다면 type 파라미터를 지정해서 이 동작을 덮어쓸 수 있어요.

참고로, Server Components 내에서 사용할 때는 type 파라미터는 영향을 미치지 않습니다.


여기서 pushreplace에 대해 간단히 정리해볼게요. 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 타입을 사용해서, 그 이후 코드는 실행되지 않는 것으로 처리되거든요.
  • paramsPromise<{ 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으로 서버 액션 지정서버 액션 내에서 리다이렉트 처리 가능

추가 팁

  • useRouterpush 메서드는 클라이언트 사이드 네비게이션을 수행해서 페이지 전환 시 전체 페이지 리로딩이 없고 더 부드러운 사용자 경험을 제공해요.
  • 서버 액션은 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.0redirect 기능 추가

HTTP 리다이렉트는 웹 개발할 때 정말 자주 마주치는 기능 중 하나예요. 간단히 말해서, 특정 URL에서 다른 URL로 자동으로 이동시키는 걸 말하죠. 예를 들어, 예전 주소에서 새 주소로 방문자를 안내하거나, HTTP에서 HTTPS로 보안 연결을 강제할 때도 사용하구요.

이번에 달라진 v13.0.0 버전에서는 redirect 기능이 새롭게 도입됐다고 하네요. 이 기능 덕분에 리다이렉트 구현이 더 간편해졌을 거라 기대됩니다. 코드를 깔끔하게 관리할 수 있고, 사용자 경험도 개선할 수 있으니까요.

만약 여러분이 서버 사이드나 프론트엔드 라우팅에서 리다이렉트를 다루고 있다면, 이번 업데이트 내용을 꼭 체크해 보세요! 혹시 리다이렉트를 처음 접한다면, 기본적인 301(영구 이동), 302(임시 이동) 상태 코드부터 이해하는 게 좋아요. 이를 통해 SEO 최적화도 잘 할 수 있답니다.

더 궁금한 점이나 구현 팁이 필요하면 언제든 물어봐 주세요!