frontend
Next.js Middleware로 인증 체크와 리다이렉트를 구현하는 방법
Next.js Middleware를 쓰면 페이지 컴포넌트가 렌더링되기 전에 인증 여부를 확인하고 미로그인 사용자를 로그인 페이지로 보낼 수 있다. matcher 설정, 쿠키 기반 토큰 확인, 조건별 리다이렉트까지 정리했다.

- ·middleware.ts: 프로젝트 루트(app과 같은 레벨)에 위치, 모든 요청 전에 실행
- ·matcher: 미들웨어를 적용할 경로 패턴 지정, 매칭되지 않는 경로는 건너뜀
- ·NextResponse.redirect: 다른 URL로 리다이렉트, NextResponse.next: 요청 통과
- ·Edge Runtime에서 실행: Node.js API 일부 사용 불가, 가벼운 로직만 적합
Next.js 블로그에 관리자 페이지를 만들면서 /admin 경로를 로그인한 사용자만 접근할 수 있게 해야 했다. 처음에 각 페이지 컴포넌트에서 인증 체크를 넣었는데, 페이지가 늘어날수록 같은 코드가 반복됐다. Middleware로 옮기니 /admin으로 시작하는 모든 경로에 한 번에 인증 체크가 적용됐다. 미들웨어가 Edge Runtime에서 실행된다는 걸 처음에 몰라서 JWT 검증에 jsonwebtoken 라이브러리를 쓰다가 오류가 났고, jose 라이브러리로 바꿔서 해결했다.
Next.js Middleware란 무엇인가
Next.js Middleware가 인증 처리에 쓰이는 이유
Next.js Middleware는 요청이 페이지 컴포넌트나 API Route에 도달하기 전에 실행되는 코드다. 프로젝트 루트에 middleware.ts 파일을 만들면 모든 요청에 대해 이 파일의 함수가 먼저 실행된다. 인증 체크, 리다이렉트, 요청 헤더 수정, A/B 테스트 라우팅 같은 작업에 쓰인다. 인증을 미들웨어에서 처리하면 여러 페이지에서 같은 인증 체크 코드를 반복하지 않아도 된다. 특정 경로 패턴에 미들웨어를 적용하면 그 경로에 해당하는 요청은 모두 미들웨어를 거치게 된다. /admin으로 시작하는 모든 경로에 한 번의 설정으로 인증 보호를 걸 수 있다. 미들웨어는 Edge Runtime에서 실행되기 때문에 서버 가까이에서 빠르게 처리된다. 단, Edge Runtime에서는 Node.js API 일부를 사용할 수 없다. Node.js 전용 라이브러리를 쓰면 오류가 난다. JWT 검증에 jsonwebtoken 대신 jose를, 암호화에 crypto 모듈 대신 Web Crypto API를 쓰는 식으로 Edge 호환 라이브러리를 선택해야 한다. 무거운 DB 쿼리나 복잡한 연산은 미들웨어보다 페이지 컴포넌트나 Server Action에서 처리하는 게 적합하다.
Next.js Middleware matcher로 적용 경로를 제어하는 방법
middleware.ts에서 config.matcher를 export하면 미들웨어를 적용할 경로 패턴을 지정할 수 있다. matcher를 설정하지 않으면 모든 요청에 미들웨어가 실행된다. 정적 파일(_next/static), 이미지(_next/image), favicon 같은 요청에도 미들웨어가 실행되면 불필요한 처리가 많아진다. matcher에 경로 패턴을 배열로 지정하면 해당 패턴에 맞는 요청에만 미들웨어가 실행된다. /admin/:path*는 /admin과 그 하위 경로 모두에 매칭된다. 부정 lookahead를 사용하면 특정 경로를 제외할 수 있다. 정적 파일과 API 라우트를 제외하고 모든 페이지에 미들웨어를 적용하는 패턴이 많이 쓰인다. 패턴은 path-to-regexp 문법을 따른다. matcher에 여러 패턴을 배열로 나열하면 모두 적용된다. 예를 들어 ['/admin/:path*', '/dashboard/:path*']처럼 설정하면 두 경로 모두에 미들웨어가 실행된다. 미들웨어 적용 대상을 좁혀두면 성능과 디버깅 모두에 도움이 된다.
Next.js Middleware로 인증 체크 구현하기
Next.js Middleware에서 쿠키로 인증 토큰을 확인하는 방법
미들웨어에서 인증을 처리하는 일반적인 흐름은 다음과 같다. 요청 쿠키에서 인증 토큰을 읽고, 토큰이 없거나 유효하지 않으면 로그인 페이지로 리다이렉트하고, 토큰이 유효하면 요청을 통과시킨다. request.cookies.get('token')으로 쿠키 값을 읽는다. JWT를 쓰고 있다면 Edge 호환 라이브러리인 jose로 토큰을 검증한다. jose의 jwtVerify 함수는 Web Crypto API 기반으로 Edge Runtime에서 정상 동작한다. 토큰이 없거나 검증에 실패하면 NextResponse.redirect로 로그인 페이지로 보낸다. 이때 현재 URL을 callbackUrl 파라미터로 로그인 페이지에 전달해두면 로그인 후 원래 페이지로 돌아올 수 있다. 토큰 검증에 성공하면 NextResponse.next()를 반환해서 요청이 정상적으로 처리되도록 한다. 필요하다면 response.headers.set으로 다음 단계에 필요한 정보를 헤더에 추가할 수 있다. 사용자 ID를 헤더에 담아 서버 컴포넌트에서 request.headers로 읽는 패턴도 자주 쓰인다.
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { jwtVerify } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET)
export async function middleware(request: NextRequest) {
const token = request.cookies.get('token')?.value
if (!token) {
const loginUrl = new URL('/login', request.url)
loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname)
return NextResponse.redirect(loginUrl)
}
try {
await jwtVerify(token, secret)
return NextResponse.next()
} catch {
return NextResponse.redirect(new URL('/login', request.url))
}
}
export const config = {
matcher: ['/admin/:path*', '/dashboard/:path*'],
}Next.js Middleware에서 역할별 접근 제어를 구현하는 방법
단순한 로그인 여부 체크를 넘어 사용자 역할에 따라 다른 페이지에 접근 권한을 주는 경우도 많다. JWT 페이로드에 사용자 역할(role)을 포함시켜두면 미들웨어에서 토큰을 검증하면서 역할도 함께 확인할 수 있다. jwtVerify의 반환값에서 payload를 꺼내고 payload.role로 역할을 읽는다. /admin 경로에는 role이 admin인 사용자만, /dashboard에는 로그인한 모든 사용자가 접근할 수 있도록 분기 처리한다. 역할이 맞지 않으면 403 Forbidden 페이지나 권한 없음 안내 페이지로 리다이렉트한다. 로그인은 됐지만 권한이 없는 경우를 로그인 페이지로 보내면 사용자가 혼란스러울 수 있다. 역할 기반 접근 제어 로직이 복잡해지면 미들웨어 파일이 길어지는데, 경로별 권한 설정을 별도 객체로 분리해서 관리하면 가독성이 좋아진다. 미들웨어에서는 가벼운 토큰 검증만 하고, 더 세밀한 권한 확인은 서버 컴포넌트나 Server Action에서 처리하는 방식으로 역할을 나누는 게 실용적이다.
Next.js Middleware 실용 패턴
Next.js Middleware에서 로그인된 사용자의 접근을 제어하는 방법
미들웨어는 비로그인 사용자를 보호하는 것뿐만 아니라 이미 로그인한 사용자가 불필요한 페이지에 접근하는 것도 막는 데 쓸 수 있다. 로그인 페이지(/login)나 회원가입 페이지(/signup)에 이미 인증된 사용자가 접근하면 대시보드나 홈으로 리다이렉트하는 패턴이 대표적이다. 요청 경로가 /login이고 쿠키에 유효한 토큰이 있으면 /dashboard로 리다이렉트한다. 이 처리가 없으면 로그인된 상태에서 /login에 접속할 때 로그인 폼이 그대로 보여서 UX가 어색해진다. 국제화(i18n)와 미들웨어를 함께 쓰는 경우 미들웨어에서 Accept-Language 헤더나 쿠키를 읽어 사용자 언어에 맞는 로케일 경로로 리다이렉트하는 처리도 할 수 있다. 미들웨어에서 처리하는 로직이 많아질수록 요청 처리 시간이 길어질 수 있다. Edge Runtime에서 실행되지만 미들웨어가 과도하게 복잡해지면 성능에 영향을 줄 수 있다. 핵심 인증/리다이렉트 로직만 미들웨어에 두고 나머지는 서버 컴포넌트에서 처리하는 것이 좋다.
Next.js Middleware 디버깅과 흔히 만나는 오류를 해결하는 방법
미들웨어에서 오류가 발생하면 원인을 파악하기 쉽지 않은 경우가 있다. 가장 흔한 오류는 Edge Runtime 호환성 문제다. Node.js 전용 라이브러리나 API를 쓰면 빌드는 통과해도 런타임에 오류가 난다. 오류 메시지에서 The edge runtime does not support Node.js나 Dynamic Code Evaluation is not allowed 같은 문구가 보이면 Edge 비호환 코드가 있다는 신호다. JWT 처리에 jsonwebtoken 대신 jose를, 해시에 crypto.createHash 대신 crypto.subtle을 쓰는 방식으로 교체해야 한다. 미들웨어에서 console.log는 서버 터미널에 출력된다. 개발 환경에서 next dev를 실행한 터미널을 열어두고 요청을 보내면 미들웨어 실행 흐름을 추적할 수 있다. 리다이렉트 루프가 발생하는 경우도 있다. /login 경로에도 미들웨어가 적용되어 있어서 /login 접근 → 토큰 없음 → /login으로 리다이렉트가 무한 반복되는 경우다. matcher에서 /login 경로를 제외하거나, 미들웨어 안에서 현재 경로가 이미 로그인 페이지인 경우 리다이렉트를 건너뛰는 조건을 추가하면 해결된다.
자주 묻는 질문
미들웨어에서 jsonwebtoken이 동작하지 않는 이유가 무엇인가요?+
jsonwebtoken은 Node.js 전용 라이브러리라 Edge Runtime에서 실행되는 미들웨어에서는 동작하지 않습니다. jose 라이브러리는 Web Crypto API 기반으로 Edge Runtime을 지원합니다. npm install jose로 설치하고 jwtVerify 함수로 교체하면 됩니다.
미들웨어에서 DB 조회를 해도 되나요?+
가능은 하지만 권장하지 않습니다. 미들웨어는 모든 요청마다 실행되기 때문에 DB 조회를 하면 부하가 커집니다. 미들웨어에서는 쿠키나 헤더의 JWT만 검증하고, 세밀한 권한 확인이 필요한 경우 서버 컴포넌트나 Server Action에서 DB를 조회하는 방식을 권장합니다.
인증 후 원래 접근하려던 페이지로 돌아가게 하려면 어떻게 하나요?+
리다이렉트할 때 callbackUrl 파라미터에 현재 경로를 담아 보내면 됩니다. loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname) 형태로 설정하고, 로그인 성공 후 callbackUrl로 이동하는 로직을 로그인 페이지에서 처리하면 됩니다.
middleware.ts와 layout.tsx에서 인증 체크 중 어디에서 해야 하나요?+
둘 다 활용하는 게 좋습니다. 미들웨어에서 토큰 유무와 기본 유효성을 빠르게 확인해 리다이렉트하고, layout.tsx나 서버 컴포넌트에서 DB 기반의 세밀한 권한 확인을 합니다. 미들웨어는 가볍게, 서버 컴포넌트는 정확하게 처리하는 역할을 나누세요.
관련 글
Next.js App Router 메타데이터 완벽 가이드 — generateMetadata로 SEO 최적화하는 방법
Next.js App Router에서는 Head 컴포넌트 대신 Metadata API를 써야 한다. layout.tsx 전역 설정부터 포스트별 generateMetadata, robots.ts와 sitemap.ts까지 정리했다.
Next.js 환경변수 완벽 가이드 — .env.local부터 NEXT_PUBLIC 클라이언트 변수까지
Next.js에서 API 키는 서버에서만 써야 하므로 NEXT_PUBLIC 없이, 브라우저에서도 써야 하면 NEXT_PUBLIC를 붙여야 한다. .env 파일 종류, 서버/클라이언트 변수 구분, 환경별 설정까지 정리했다.
Next.js App Router fetch 캐싱과 revalidate 완벽 가이드 — 언제 데이터가 갱신되나
Next.js App Router에서 fetch는 기본으로 캐싱된다. 언제 캐시를 쓰고 언제 갱신할지 제어하는 cache, next.revalidate, next.tags 옵션과 revalidatePath, revalidateTag를 정리했다.