본문 바로가기
JavaScript/Next

Middleware 미들웨어 사용법

by curious week 2025. 3. 5.

 Next.js Middleware 완벽 가이드

1. Middleware란?

Middleware는 Next.js에서 요청(Request)이 특정 페이지(Route)나 API 핸들러에 도달하기 전에 **전처리(Preprocessing)**를 수행하는 서버 측 함수이다.

Middleware의 역할

  • 사용자 인증: 로그인하지 않은 사용자를 로그인 페이지로 리디렉트
  • A/B 테스트: 특정 트래픽을 랜덤하게 다른 페이지로 분배
  • 보안 강화: 요청을 필터링하거나 차단
  • Locale 처리: 자동으로 언어별 페이지로 리디렉트
  • 쿠키 검사: 특정 쿠키 값을 기반으로 동작 결정

Middleware 실행 흐름

  1. 사용자가 요청을 보냄
  2. Middleware에서 요청을 가로채고 처리함 (request 객체 활용)
  3. NextResponse를 통해 다음 단계로 넘기거나, 리디렉트/차단
  4. 라우트가 요청을 받아 정상적으로 페이지가 렌더링됨

2. Middleware 기본 사용법

1️⃣ middleware.ts 파일 생성

Next.js에서는 **프로젝트 루트 (src/middleware.ts 또는 middleware.ts)**에 middleware.ts 파일을 만들면 자동으로 실행된다.

touch middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// Middleware 함수 정의
export function middleware(req: NextRequest) {
  console.log('Middleware 실행됨:', req.nextUrl.pathname)

  return NextResponse.next() // 요청을 그대로 진행
}

NextRequest 객체로 요청 정보를 가져올 수 있고,
NextResponse.next()를 반환하면 요청을 그대로 진행한다.

NextRequest 더보기

더보기

 NextRequest

1. req.nextUrl

요청된 URL 정보를 포함한 객체.
URL을 분석하고 조작할 때 사용할 수 있다.

export function middleware(req: NextRequest) {
  console.log(req.nextUrl.pathname) // 요청된 경로 (/dashboard)
  console.log(req.nextUrl.search)   // 쿼리 문자열 (?id=123)
  console.log(req.nextUrl.origin)   // 도메인 (http://localhost:3000)
  console.log(req.nextUrl.href)     // 전체 URL (http://localhost:3000/dashboard?id=123)
}

2. req.cookies

요청에 포함된 쿠키 값을 가져오거나 설정할 때 사용된다.

 1️⃣ 특정 쿠키 가져오기

const token = req.cookies.get('auth_token')?.value
console.log(token) // 'abcdef12345'

 2️⃣ 모든 쿠키 가져오기

const allCookies = req.cookies.getAll()
console.log(allCookies) 
// [{ name: 'auth_token', value: 'abcdef12345' }, { name: 'theme', value: 'dark' }]

 3️⃣ 쿠키를 수정하거나 삭제할 수도 있다.

하지만 Middleware에서 응답을 직접 수정하려면 NextResponse를 사용해야 한다.

export function middleware(req: NextRequest) {
  const response = NextResponse.next()
  response.cookies.set('visited', 'true', { maxAge: 60 * 60 * 24 }) // 1일 유지
  return response
}

 3. req.headers

HTTP 요청 헤더를 읽을 때 사용된다.

1️⃣ 특정 헤더 가져오기

const userAgent = req.headers.get('user-agent')
console.log(userAgent) // 브라우저 정보

2️⃣ 모든 헤더 출력

for (const [key, value] of req.headers.entries()) {
  console.log(`${key}: ${value}`)
}

3️⃣ 특정 헤더 값이 없을 경우 기본값 설정

const language = req.headers.get('accept-language') || 'en'
console.log(language) // 예: 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7'

4. req.method

현재 요청의 HTTP 메서드(GET, POST 등)를 확인할 때 사용된다.

console.log(req.method) // 'GET', 'POST' 등

특정 HTTP 메서드에서만 실행하도록 설정할 수도 있다.

export function middleware(req: NextRequest) {
  if (req.method !== 'GET') {
    return new NextResponse('Only GET requests are allowed', { status: 405 })
  }
  return NextResponse.next()
}

405 Method Not Allowed 상태 코드를 반환함.


5. req.ip

클라이언트의 IP 주소를 가져온다.

const userIp = req.ip
console.log(userIp) // '192.168.0.1' 또는 '::1' (IPv6)

IP 기반 차단 기능을 추가할 수 있다.

const blockedIps = ['192.168.1.100', '203.0.113.50']

export function middleware(req: NextRequest) {
  if (blockedIps.includes(req.ip || '')) {
    return new NextResponse('Access Denied', { status: 403 })
  }
  return NextResponse.next()
}

6. req.clone()

NextRequest는 읽기 전용(immutable) 객체이므로, 변경하려면 clone()을 사용해야 한다.

const newRequest = req.clone()
console.log(newRequest.nextUrl.pathname)

하지만 Middleware에서는 보통 clone()이 필요하지 않으며, NextResponse를 활용해 응답을 조작하는 것이 일반적이다.


7. req.json()

만약 Body 데이터를 JSON 형태로 가져오고 싶다면, req.json()을 사용할 수 있다.
📌 단, Middleware에서는 Body를 읽을 수 없다는 점을 주의해야 한다.

export function middleware(req: NextRequest) {
  if (req.method === 'POST') {
    req.json().then((data) => console.log(data))
  }
  return NextResponse.next()
}

하지만 Middleware에서는 요청 본문(Body)를 직접 읽을 수 없으므로 API 라우트에서 처리하는 것이 더 적절하다.

NextResponse 더보기

더보기

NextResponse


1. NextResponse.next() → 요청을 그대로 진행

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(req: NextRequest) {
  console.log('Middleware 실행됨:', req.nextUrl.pathname)
  
  return NextResponse.next() // 요청을 그대로 다음 단계로 전달
}

기능: 요청을 차단하지 않고, 그대로 원래의 페이지로 전달한다.


2. NextResponse.redirect() → 특정 URL로 리디렉트

Middleware에서 특정 경로로 이동시킬 때 사용한다.

1️⃣ /old-page → /new-page로 자동 이동

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname === '/old-page') {
    return NextResponse.redirect(new URL('/new-page', req.url))
  }
  return NextResponse.next()
}

기능: 사용자가 /old-page에 접근하면 /new-page로 이동한다.
브라우저 주소창이 /new-page로 변경됨.


3. NextResponse.rewrite() → 페이지 내용을 변경하지만 URL 유지

rewrite()는 사용자 브라우저의 URL을 변경하지 않으면서, 다른 페이지의 내용을 제공할 때 사용된다.

1️⃣ /blog에 접속하면 /news의 내용을 보여줌

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname.startsWith('/blog')) {
    return NextResponse.rewrite(new URL('/news', req.url))
  }
  return NextResponse.next()
}

기능: 사용자가 /blog에 접근해도 브라우저 URL은 그대로 유지되지만, 실제 렌더링되는 페이지는 /news가 된다.
SEO 최적화가 필요할 때 유용.


4. NextResponse.json() → JSON 데이터 반환

Middleware에서 API 응답을 생성할 수 있다.

1️⃣ 특정 API 요청 차단하고 JSON 응답 반환

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname.startsWith('/api/secret')) {
    return NextResponse.json({ error: 'Access Denied' }, { status: 403 })
  }
  return NextResponse.next()
}

기능: /api/secret에 접근하면 403 Forbidden 응답을 JSON 형태로 반환.


5. NextResponse.cookies → 쿠키 설정 / 삭제

Middleware에서 쿠키를 설정하거나 변경할 수 있다.

1️⃣ 쿠키 설정 (response.cookies.set())

export function middleware(req: NextRequest) {
  const response = NextResponse.next()
  response.cookies.set('visited', 'true', { maxAge: 60 * 60 * 24 }) // 1일 유지
  return response
}

기능: 사용자가 페이지를 방문하면 visited=true 쿠키가 설정됨.

2️⃣ 쿠키 삭제 (response.cookies.delete())

export function middleware(req: NextRequest) {
  const response = NextResponse.next()
  response.cookies.delete('visited')
  return response
}

기능: visited 쿠키를 삭제한다.


6. NextResponse.headers → 응답 헤더 추가 / 수정

Middleware에서 응답 헤더를 설정할 수도 있다.

1️⃣ 특정 헤더 추가

export function middleware(req: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('X-Custom-Header', 'MyValue')
  return response
}

기능: 응답에 X-Custom-Header: MyValue 헤더가 추가됨.


7. NextResponse.error() → 에러 응답 반환

API 요청을 차단하고 커스텀 에러 응답을 반환할 수 있다.

1️⃣ 404 Not Found 에러 반환

export function middleware(req: NextRequest) {
  return NextResponse.error(new Error('Page Not Found'), { status: 404 })
}

기능: 404 상태 코드와 함께 "Page Not Found" 에러 반환.


8. NextResponse.json() vs NextResponse.error()

NextResponse.json() JSON 데이터를 응답으로 반환 API 응답으로 JSON을 반환할 때
NextResponse.error() 에러 응답 반환 특정 요청 차단 및 HTTP 오류 코드 반환

 


3. Middleware로 리디렉트 적용

Middleware의 가장 대표적인 기능이 리디렉트(Redirection) 처리다.

1️⃣ 특정 페이지로 리디렉트

예를 들어, 사용자가 /old-page에 접근하면 /new-page로 보내도록 설정할 수 있다.

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname === '/old-page') {
    return NextResponse.redirect(new URL('/new-page', req.url))
  }

  return NextResponse.next()
}

2️⃣ 인증되지 않은 사용자 로그인 페이지로 리디렉트

예제: 사용자가 /dashboard에 접근하려고 하지만 로그인 쿠키가 없는 경우, /login으로 이동

export function middleware(req: NextRequest) {
  const token = req.cookies.get('auth_token') // 쿠키에서 인증 토큰 가져오기

  if (!token && req.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', req.url))
  }

  return NextResponse.next()
}

4. Middleware에서 응답 변경하기 (Rewriting)

**Rewriting(재작성)**은 사용자가 다른 URL을 요청했지만 실제로 다른 경로를 렌더링하도록 하는 기능이다.

1️⃣ /blog → /news 페이지로 보이게 하기

사용자는 /blog에 접근하지만, 실제로 /news의 내용을 보여준다.

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname.startsWith('/blog')) {
    return NextResponse.rewrite(new URL('/news', req.url))
  }

  return NextResponse.next()
}

브라우저 주소창은 그대로 /blog로 유지되지만, 실제 내용은 /news에서 가져옴.


5. Middleware에서 쿠키 설정 및 응답 수정

Middleware에서 쿠키를 설정할 수도 있다.

1️⃣ 특정 페이지 방문 시 쿠키 설정

사용자가 /welcome 페이지를 방문하면 visited=true 쿠키를 설정한다.

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname === '/welcome') {
    const response = NextResponse.next()
    response.cookies.set('visited', 'true', { maxAge: 60 * 60 * 24 }) // 1일 유지
    return response
  }

  return NextResponse.next()
}

2️⃣ 쿠키를 기반으로 리디렉트 (예: 다크모드 설정 유지)

쿠키에서 theme=dark이면, /dark-theme 페이지로 이동

export function middleware(req: NextRequest) {
  const theme = req.cookies.get('theme')

  if (theme === 'dark') {
    return NextResponse.redirect(new URL('/dark-theme', req.url))
  }

  return NextResponse.next()
}

6. 특정 경로에서만 Middleware 실행

기본적으로 Middleware는 모든 요청에 실행되지만, 특정 경로에서만 동작하도록 설정할 수도 있다.

1️⃣ 특정 경로에서만 실행 (config.matcher)

예를 들어, /dashboard와 /profile에서만 실행되도록 하려면:

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*'], // 특정 경로에서만 실행
}

2️⃣ 특정 파일 요청은 제외 (.png, .jpg 등)

이미지 파일 요청을 Middleware에서 제외하고 싶다면:

export const config = {
  matcher: '/((?!.*\\.(?:png|jpg|jpeg|gif|svg)$).*)',
}

.png, .jpg 등의 파일 요청은 Middleware 실행에서 제외됨.


7. Middleware와 API 라우트 연동 (JSON 응답 처리)

Middleware에서 API 요청을 가로채고, JSON 데이터를 반환할 수도 있다.

1️⃣ 특정 API 요청 차단

예를 들어, /api/secret 요청을 차단하려면:

export function middleware(req: NextRequest) {
  if (req.nextUrl.pathname.startsWith('/api/secret')) {
    return new NextResponse(JSON.stringify({ error: 'Access Denied' }), {
      status: 403,
      headers: { 'Content-Type': 'application/json' },
    })
  }

  return NextResponse.next()
}

/api/secret에 접근하면 403 Forbidden 응답을 반환


8. Middleware + 국제화 (i18n) 처리

Middleware를 활용하여 사용자의 Accept-Language 헤더를 기반으로 자동 리디렉트할 수 있다.

1️⃣ 브라우저 언어에 따라 /en 또는 /ko로 자동 리디렉트

export function middleware(req: NextRequest) {
  const lang = req.headers.get('accept-language')?.split(',')[0] || 'en'

  if (!req.nextUrl.pathname.startsWith(`/${lang}`)) {
    return NextResponse.redirect(new URL(`/${lang}${req.nextUrl.pathname}`, req.url))
  }

  return NextResponse.next()
}

사용자의 언어 설정이 한국어(ko)라면, /ko 페이지로 자동 이동!


9. Middleware에서 Headers 수정

Middleware에서 요청 헤더를 변경할 수도 있다.

export function middleware(req: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('X-Custom-Header', 'MyValue') // 커스텀 헤더 추가
  return response
}

 

'JavaScript > Next' 카테고리의 다른 글

전역 상태 관리 라이브러리 소개 및 사용 시기(useContext, Zustand, Jotai, Recoil, Redux Toolkit, Nuqs)  (0) 2025.03.05
Upstash  (1) 2025.03.05
Drizzle ORM  (0) 2025.02.12
Neon  (0) 2025.02.12
Clerk  (0) 2025.02.12