nextjs 13 middleware를 사용하여 인가 실패 시 redirect

NextJS 13 Logo
NextJs 13

안녕하세요. 독자님들 ! NextJS 13으로 버전을 업데이트하면서 많은 변화를 가져왔습니다. 사용법 또한 약간씩 차이가 있기에 헷갈릴 수 있는데요. 이번 섹션에서는 NextJS 13에서 NextAuth를 이용하여 인가에 문제가 생겼을 시 로그인을 유도하는 코드 예시를 소개해드리려 합니다.

목차


## NextJS 13 인가(Authorization) 실패 시 리다이렉트 코드

여러분의 시간은 소중하니까 가장 핵심부인 코드를 먼저 살펴보도록 하죠 ! 기본적으로 middleware.ts는 앱의 최상단에 배치합니다. 해당 코드는 next-auth와 함께 사용됩니다. 물론 독자님들이 사용하는 타 인증 라이브러리를 사용해도 원리는 같습니다. 😉

// Next.js 서버 사이드 라이브러리와 next-auth 라이브러리를 import합니다.
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { getToken } from 'next-auth/jwt';

// 미들웨어 함수를 export합니다.
// request 객체를 인자로 받아 처리합니다.
export async function middleware(request: NextRequest) {
  // next-auth 라이브러리를 사용해 token을 받아옵니다.
  // 이때, 비밀 키 (NEXTAUTH_SECRET)는 환경 변수에서 가져옵니다.
  const token = await getToken({
    req: request,
    secret: process.env.NEXTAUTH_SECRET,
  });

  // 토큰이 없을 경우, 로그인 페이지(/auth/sign-in)로 리다이렉트합니다.
  if (!token) {
    return NextResponse.redirect(new URL('/auth/sign-in', request.url));
  }

  // 토큰이 유효하다면, 다음 처리 단계로 넘어갑니다.
  return NextResponse.next();
}

// 미들웨어가 적용될 경로를 설정합니다.
// 이 미들웨어는 아래의 경로에 대해 작동하게 됩니다.
export const config = {
  matcher: [
    '/api/product/:path*',  // 모든 /api/product/* 경로
    '/products/:path*',     // 모든 /products/* 경로
    '/manage/:path*',       // 모든 /manage/* 경로
  ],
};

이 코드는 다음을 수행합니다:

  1. 미들웨어는 config.matcher에 지정된 경로에 대한 요청을 가로챕니다.
  2. 가로챈 요청에 대해 getToken을 이용하여 인증 토큰을 확인합니다.
  3. 토큰이 없거나 유효하지 않으면 사용자를 /auth/sign-in 로그인 페이지로 리다이렉트합니다.
  4. 유효한 토큰이 있으면 원래의 요청을 계속 진행합니다 (NextResponse.next()).

이렇게 하면 특정 경로에 대한 접근을 제어할 수 있으며, 인증이 필요한 경우 로그인 페이지로 사용자를 리다이렉트할 수 있습니다.

Middleware란?

Middleware는 주로 두 개 이상의 소프트웨어 구성 요소 또는 애플리케이션 사이에서 중개 역할을 하는 소프트웨어 레이어를 의미합니다. 이 중개 역할은 다양한 형태와 목적으로 나타날 수 있으며, 일반적으로는 데이터 전송, 애플리케이션 서비스, 메시지 전송 등을 포함합니다. Middleware는 다양한 종류의 네트워크 프로토콜, 데이터 형식, 및 애플리케이션 도메인 간에 원활한 통신과 데이터 공유를 가능하게 하여, 분산 컴퓨팅 환경에서 특히 중요한 역할을 합니다.

예를 들어, 데이터베이스와 웹 서버 사이에 위치한 ORM(Object-Relational Mapping) 미들웨어는 데이터베이스 쿼리를 더 쉽고 효율적으로 처리할 수 있도록 도와줍니다. 또는, 메시지 큐 미들웨어는 비동기 메시지를 관리하고 전달함으로써 다양한 애플리케이션 컴포넌트간의 결합도를 낮춰줍니다.

Middleware는 여러 가지 계층으로 구성될 수 있으며, 각 계층은 특정 목적에 맞는 기능을 제공합니다. 예를 들어, 트랜스포트 계층은 네트워크 프로토콜을 추상화하여 데이터 전송을 담당하고, 애플리케이션 미들웨어 계층은 비즈니스 로직과 서비스를 제공합니다. 이런 계층적 구조 덕분에 미들웨어는 모듈화와 재사용이 용이하며, 애플리케이션의 확장성과 유지 관리성을 향상시킵니다.

결론적으로, 미들웨어는 소프트웨어 아키텍처에서 중심적인 역할을 하는 컴포넌트로, 다양한 애플리케이션과 서비스가 효율적으로 작동하고 상호 작용할 수 있도록 지원합니다.

물론, 이 아티클에서는 미들웨어는 NextJS 13 어플리케이션 계층에서 미들웨어를 어떻게 동작하는지 실전 위주의 내용을 담고있습니다. 😊

NextJS 13 Middleware 사용 방법

NextJS의 Middleware 기능을 이용하면 요청이 완료되기 전에 코드를 실행할 수 있습니다. 이를 통해 들어오는 요청을 기반으로 응답을 수정하거나 직접 응답을 할 수 있습니다. 이 글에서는 NextJS 13에서 Middleware를 어떻게 사용하는지 자세히 알아보겠습니다.

Middleware 파일 구조 및 위치

NextJS 프로젝트에서 Middleware를 정의하려면 루트 디렉토리에 middleware.ts 또는 middleware.js 파일을 생성해야 합니다. 이 파일은 pages, app 등과 동일한 레벨에 위치하거나, 프로젝트 구조에 따라 src 폴더 내부에 있을 수도 있습니다.

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

export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url));
}

위의 예시에서는 간단한 리다이렉션을 수행하는 Middleware를 정의했습니다. middleware 함수는 NextRequest 객체를 매개변수로 받아, NextResponse 객체를 반환합니다. 함수는 async로 선언될 수도 있으며, 그 경우 내부에서 await을 사용할 수 있습니다.

경로 일치(Matching Paths)

NextJS의 Middleware는 프로젝트 내의 모든 라우트에 대해 실행됩니다. 그러나 matcher를 사용하면 특정 경로에 대해서만 Middleware를 실행할 수 있습니다. matcher는 다음과 같은 방식으로 설정할 수 있습니다.

// middleware.ts
export const config = {
  matcher: '/about/:path*',
};

위의 예시에서는 /about/으로 시작하는 모든 경로에 대해 Middleware가 실행됩니다. 또한, 배열 문법을 이용하여 여러 경로에 대해 일치시킬 수 있습니다.

// middleware.ts
export const config = {
  matcher: ['/about/:path*', '/dashboard/:path*'],
};

matcher 설정은 정규표현식도 지원하므로, 복잡한 경로 패턴도 처리할 수 있습니다. 예를 들어, 특정 경로를 제외하고 모든 요청에 대해 적용하려면 다음과 같이 설정할 수 있습니다.

// middleware.ts
export const config = {
  matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
};

조건문을 이용한 Middleware 실행

matcher 외에도 조건문을 이용하여 특정 조건을 만족할 때만 Middleware를 실행할 수 있습니다. 이를 위해 NextRequest 객체의 여러 속성을 확인하고 로직을 수행할 수 있습니다.

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

export function middleware(request: NextRequest) {
  if (request.headers.get('user-agent').includes('Chrome')) {
    return NextResponse.redirect(new URL('/chrome-landing', request.url));
  }

  return NextResponse.next();
}

위 예시에서는 사용자의 User-Agent가 "Chrome"을 포함하면 특정 페이지(/chrome-landing)로 리다이렉트하는 Middleware를 만들었습니다. 조건문을 통해 다양한 로직을 구현할 수 있으므로, 복잡한 요구 사항에도 유연하게 대응할 수 있습니다.

Middleware는 NextJS의 라우팅 시스템을 더욱 풍부하게 만들어주며, 위에서 설명한 방법들을 적절히 활용하면 더욱 강력한 웹 애플리케이션을 구축할 수 있습니다.

마치며

함께 읽으면 좋은 글

Next.js 13 navigation/useRouter는 어떻게 다를까?
Next.js 개발자라면 어플리케이션의 사용자 경험에 라우팅이 얼마나 중요한지 잘 알고 있을 것입니다. Next.js 13에서 useRouter 훅이 훨씬 간편해지고 많은 점이 변경되었습니다 ! 커피 한잔 손에 쥐고 편하게 읽어보세요. Next.js 13에서 무엇이 새로워졌고, Next.js 12와 어떻게 다른지에 대해 가벼우면서도 자세하게 썼으니까요. 😉 Next.js 12의 useRouter: 빠른 회고 기본
성능이 최대 300%라고?: PostgreSQL 16 버전 소식
2023년 9월 14일 - PostgreSQL 글로벌 개발 그룹은 오늘 세계에서 가장 고도로 발전한 오픈소스 데이터베이스인 PostgreSQL 16 버전을 공식적으로 발표했습니다. PostgreSQL 16은 쿼리 병렬 처리, 대량 데이터 로딩, 논리 복제 등에서 성능 향상을 이루었습니다. 이번 릴리스에는 개발자와 관리자 모두를 위한 다양한 기능들이 포함되어 있으며, SQL/JSON 문법 확장, 작업부하를 모니터링하기
당신이 IT 종사자라면 옵시디언(Obsidian) 노트 앱을 꼭 써야하는 이유
노트 작성과 정보 관리는 개발자, 학자, 작가, 그리고 일반인들에게 필수적인 작업입니다. 어쩌면 당신은 Evernote, Notion, 또는 기본 메모 앱을 사용하고 있을지 모릅니다. 하지만 오늘은 특별한 노트앱, 옵시디언(Obsidian)에 대해 이야기해보려 합니다. 이 앱이 왜 주목받고 있는지, 그리고 당신이 이를 사용해야 하는 이유에 대해서 깊게 들어가보겠습니다. 옵시디언(Obsidian)의 기본