728x90
반응형

[NestJS 인증 흐름] Jwt 토큰 기반 인증 요청 흐름 정리
NestJS는 Spring Security처럼 정교한 요청 처리 체인을 갖고 있으며,
Middleware → Guard → Interceptor → Pipe → Controller → Service → Response
의 구조를 통해 요청을 세밀하게 통제할 수 있다. 이 글에서는 Spring Security의 구성요소들과 NestJS의 흐름이 어떻게 대응되는지를 정리하고, JWT 기반 인증 요청이 들어왔을 때 NestJS 내부에서 어떤 순서로 로직이 실행되는지를 Spring 경험자의 시선에서 상세히 정리해본다.
Spring Security와 NestJS의 대응 표
| Spring Security 구성 | NestJS 대응 | 주요 역할 |
|---|---|---|
Filter |
Middleware |
Express 수준의 요청 전처리 |
SecurityContextHolder |
Guard (→ request.user 주입) |
인증 정보 주입, 인가 검증 |
HandlerInterceptor |
Interceptor |
요청/응답 가로채기, 로깅 등 |
@ControllerAdvice + @ExceptionHandler |
ExceptionFilter |
글로벌 예외 포맷 처리 |
@Valid, @RequestBody |
Pipe, ValidationPipe |
DTO 변환 및 유효성 검증 |
요청 흐름 개요
클라이언트가 Authorization: Bearer <JWT> 헤더를 포함한 API 요청을 보냈을 때, NestJS 내부에서 수행되는 순서를 단계별로 분해하면 다음과 같다:
Request
↓
[Middleware]
↓
[Guards] - JwtAuthGuard
↓
[Interceptors - Before]
↓
[Pipes] - ValidationPipe
↓
[Controller Handler] - @Get('/me')
↓
[Interceptors - After]
↓
Response
※ 오류 발생 시 어느 단계에서든 ExceptionFilter로 이동주요 흐름 설명 및 코드 예시
1. 클라이언트 요청
GET /api/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...2. JwtAuthGuard 실행
JwtAuthGuard는 @UseGuards(JwtAuthGuard) 또는 app.useGlobalGuards()를 통해 등록되어 요청 전에 실행된다. 본인은 글로벌 가드에 등록해 전역적으로 실행하도록 설정하고, 인증이 필요하지 않은 api는 별도 데코레이터를 만들처 처리했다.
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}3. JwtStrategy.validate(payload)
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly userRepository: UserRepository) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'your_jwt_secret',
});
}
async validate(payload: JwtPayload) {
const user = await this.userRepository.findOne({
where: { id: payload.sub },
});
if (!user) {
throw new UnauthorizedException('존재하지 않는 사용자입니다.');
}
return {
id: user.id,
userName: user.userName,
role: user.role,
};
}
}
이 validate()에서 반환된 객체는
request.user로 주입된다.
4. Controller에서 사용자 정보 접근
@Get('/me')
async getProfile(@CurrentUser() user: User) {
return this.userService.getProfile(user.id);
}5. @CurrentUser() 커스텀 데코레이터
export const CurrentUser = createParamDecorator(
(data, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);6. 서비스 레이어에서 사용자 데이터 처리
async getProfile(userId: string) {
return await this.userRepository.findOne({ where: { id: userId } });
}- 테스트/설정 실행 방법, 환경 설정 방법 포함
passport,@nestjs/passport,passport-jwt,jsonwebtoken등의 패키지가 필요하며JwtModule.register()로 시크릿 키를 등록해야 한다.- 테스트 시
Authorization헤더에 유효한 JWT를 포함해야 한다. - 인터셉터, 파이프, 필터는
app.useGlobal...()방식으로 전역 등록하거나 데코레이터 방식으로 라우트에 적용할 수 있다.
- 표/정리로 중요한 부분 요약
| 구성 요소 | NestJS 위치 | 실행 시점 | 주요 역할 |
|---|---|---|---|
| Middleware | Express 수준 | 가장 먼저 | 헤더 파싱 등 |
| Guard | AuthGuard | 요청 전 | 인증/인가 판단, request.user 주입 |
| Interceptor | Nest 인터셉터 | 핸들러 전후 | 로깅, 응답 포맷 처리 등 |
| Pipe | DTO 위 | 요청 바디/파라미터 변환 및 검증 | |
| Controller | 라우터 핸들러 | 실제 요청 처리 | 서비스 호출, 응답 리턴 |
| Exception Filter | 예외 발생 시 | 모든 단계 | 에러 응답 처리 |
✅ 최종 정리
- NestJS의 요청 처리 흐름은 Spring Security FilterChain + Interceptor + AOP 구조와 매우 유사하다.
- JwtAuthGuard를 통해 JWT 토큰을 파싱하고, JwtStrategy에서 유저를 조회한 뒤, 이를 request.user에 주입한다.
- 컨트롤러에서는 이를 커스텀 데코레이터(@CurrentUser)를 통해 간편하게 받아 사용할 수 있다.
- Interceptor와 Pipe는 로직 흐름의 앞뒤를 정리해주며, Exception Filter는 어느 지점에서든 발생한 예외를 일관된 포맷으로 처리할 수 있게 돕는다.
Spring 경험자에게 NestJS의 흐름은 익숙하면서도 구조적으로 더 모듈화된 느낌을 준다. 각 단계를 잘 조합하면 가독성 높고 유연한 인증 흐름을 만들 수 있다.
728x90
반응형
'Nest.js' 카테고리의 다른 글
| [NestJS - 트러블 슈팅] 몽고 DB replicaSet 없이는 트랜잭션 불가능 (4) | 2025.06.19 |
|---|---|
| NestJS 엔티티, 왜 다 public이야? – Spring 개발자의 궁금증 해결 (1) | 2025.05.25 |
| [NestJS 응답/요청 변환기] 내부 camelCase, 외부 snake_case 통일하기 (0) | 2025.05.22 |
| [Nest.js] 제로베이스에서 1주일, 실전까지 반나절 (1) | 2025.05.22 |
| Nest.js 개발에 필요한 주요 CLI 명령어와 활용법 (1) | 2025.05.22 |