nextjs 13 middleware를 사용하여 인가 실패 시 redirect
안녕하세요. 독자님들 ! 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/* 경로
],
};
이 코드는 다음을 수행합니다:
- 미들웨어는
config.matcher
에 지정된 경로에 대한 요청을 가로챕니다. - 가로챈 요청에 대해
getToken
을 이용하여 인증 토큰을 확인합니다. - 토큰이 없거나 유효하지 않으면 사용자를
/auth/sign-in
로그인 페이지로 리다이렉트합니다. - 유효한 토큰이 있으면 원래의 요청을 계속 진행합니다 (
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의 라우팅 시스템을 더욱 풍부하게 만들어주며, 위에서 설명한 방법들을 적절히 활용하면 더욱 강력한 웹 애플리케이션을 구축할 수 있습니다.