https://docs.nestjs.com/middleware
미들웨어는 라우트 핸들러(route handler)가 호출되기 전에 실행되는 함수. 미들웨어 함수는 요청(request)과 응답(response) 객체, 그리고 애플리케이션의 요청-응답 주기(request-response cycle)에서 next
미들웨어 함수에 대한 접근 권한을 가지고 있다.
애플리케이션의 요청과 응답 처리를 중간에 가로채고 변경하거나 보완하는 데 사용한다. 이를 통해 코드 중복을 줄이고, 요청 처리에 필요한 공통 작업을 재사용할 수 있다. 또한, 미들웨어는 애플리케이션의 유연성과 확장성을 향상시키는 데에도 도움을 준다고 한다.
req(request)
: 요청 객체로서 클라이언트로부터 받은 요청에 대한 정보를 포함. 미들웨어 함수에서 요청에 대한 작업을 수행하거나 요청의 속성을 읽고 수정할 수 있다.res(response)
: 응답 객체로서 클라이언트로 보낼 응답에 대한 정보를 포함. 미들웨어 함수에서 응답을 생성하거나 응답의 속성을 설정할 수 있다.next()
: 다음 미들웨어 함수를 호출하기 위한 함수. 현재 미들웨어 함수에서 작업을 완료하고 다음 미들웨어 함수로 제어를 전달할 때 사용된다. next()
를 호출하지 않으면 미들웨어 체인이 중단되고 요청 처리가 중지될 수 있습니다.import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
import { AppController } from '@app/app.controller';
import { AppService } from '@app/app.service';
import { TagModule } from '@app/tag/tag.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import ormconfig from '@app/ormconfig';
import { UserModule } from '@app/user/user.module';
import { AuthMiddleware } from './user/middlewares/auth.middleware';
@Module({
imports: [TypeOrmModule.forRoot(ormconfig), TagModule, UserModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
//global middleware
configure(consumer: MiddlewareConsumer) {
consumer.apply(AuthMiddleware).forRoutes({
path: '*',
method: RequestMethod.ALL,
});
}
}
미들웨어를 사용하려면 configure
메서드를 AppModule
에 정의해야 한다. configure
메서드는 Nest의 MiddlewareConsumer
를 사용하여 미들웨어를 등록하고 설정하는 역할을 한다. apply
메서드를 사용하여 AuthMiddleware
를 등록하고, forRoutes
메서드를 사용하여 어떤 경로와 HTTP 메서드에 대해 미들웨어를 적용할지 지정할 수 있다.
AuthMiddleware
를 모든 경로(path: '*')와 모든 HTTP 메서드(method: RequestMethod.ALL)에 대해 등록하고 있다(전역 미들웨어). 애플리케이션의 모든 요청에 대해 인증 미들웨어를 적용하겠다는 의미!
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Response } from 'express';
import { ExpressRequest } from '@app/user/types/expressRequest.interface';
import { verify } from 'jsonwebtoken';
import { JWT_SECRET } from '@app/config';
import { UserServie } from '@app/user/user.service';
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(private readonly userService: UserServie) {}
async use(req: ExpressRequest, _: Response, next: NextFunction) {
console.log('authMiddle', req.headers);
if (!req.headers.authorization) {
req.user = null;
next();
return;
}
const token = req.headers.authorization.split(' ')[1];
try {
const decode = verify(token, JWT_SECRET);
const user = await this.userService.findById(decode.id);
req.user = user;
next();
} catch (err) {
req.user = null;
next();
}
}
}
NestMiddleware
는 Nest.js의 미들웨어 인터페이스.
AuthMiddleware
클래스는 NestMiddleware
를 구현하고 있으므로, use
메서드를 구현해야 한다. use 메서드는 미들웨어의 핵심 로직을 포함(req, res, next)
req.headers.authorization
을 확인하여 토큰이 있는지 확인한다. 토큰이 없으면 req.user
를 null
로 설정하고 next()
를 호출하여 다음 미들웨어로 넘어간다.
토큰이 있는 경우,req.headers.authorization
을 공백으로 분리하여 토큰 부분만 추출. 그리고 jsonwebtoken
패키지의 verify
함수를 사용하여 토큰을 검증한다. 이때 JWT_SECRET
을 사용하여 토큰을 해독한다.
토큰이 유효하면 this.userService.findById(decode.id)
를 사용하여 사용자를 데이터베이스에서 찾고 토큰이 유효하지 않은 경우, req.user
를 null
로 설정.
@Get('user')
async currentUser(
@Req() request: ExpressRequest,
): Promise<UserResponseInterface> {
console.log('current user in controller', request.user);
return this.userService.buildUserResponse(request.user);
}
currentUser
핸들러는 현재 로그인한 사용자의 정보를 반환하는 역할
미들웨어에서 토큰을 해독하고 사용자를 조회하여 request.user
에 저장하면, 이후에 실행되는 컨트롤러에서 request.user
를 활용할 수 있다.
미들웨어를 통해 토큰을 검증하고 사용자를 조회한 후, 컨트롤러에서 해당 정보를 활용하여 사용자의 응답을 구성. 이를 통해 중복된 로직을 줄이고 코드의 재사용성을 높일 수 있음.(미들웨어를 사용하여 토큰 검증과 사용자 조회를 한 번만 작성하면 되기 때문!)
흠..아직은 어렵다..