낙서장이자 오답 노트이자 컨닝 페이퍼
패스트캠퍼스 환급챌린지 32일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기 본문
패스트캠퍼스 환급챌린지 32일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기
NangIn 2025. 5. 3. 10:07본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
강의 내용 정리
Guard 이론
왜 가드를 배우는가?
- 기본기를 익힌 후 진도를 빠르게 나가기 위함.
- 파트1에서 많은 내용을 소화해야 하므로 깊이 있고 빠르게 커버할 필요 있음.
NestJS의 주요 기능 분류 및 비교
| 기능명 | 주 목적 | 실행 시점 | 사용 포인트 |
| 미들웨어 | 요청 전 비파괴적 작업 | 가장 먼저 실행 | logging, 공통 처리 등 |
| 파이프 | 유효성 검사 및 변환 | 컨트롤러 진입 전 | DTO, transform |
| 가드 | 인가(authorization) 처리 | 컨트롤러 진입 직전 | 권한 체크 |
| 인터셉터 | 요청/응답 가로채기 | 컨트롤러 전/후 | 응답 변경, 로깅 등 |
- 기능은 유사한 일을 할 수 있지만, 각 기능의 문맥(Context)에 맞게 사용하는 것이 협업과 유지보수에 유리함.
가드(Guard)의 정의 및 역할
- 가드는 요청을 보호하는 역할을 한다.
- 요청을 라우트 핸들러에 넘기기 전, 요청자가 자격이 있는지(인가) 판단.
- 이를 Authorization이라고 함 (인증: Authentication과 구분)
- 요청을 통과시키거나 차단할 수 있음 (true/false 반환)
미들웨어 vs 가드
| 항목 | 미들웨어 | 가드 |
| 실행 위치 | 가장 앞단 | 핸들러 직전 |
| 사용 가능 정보 | request, response만 있음 | executionContext 제공 (컨텍스트 풍부) |
| 적절한 용도 | 로깅, 인증 토큰 파싱 등 | 권한 검증, 접근 제어 등 |
가드 구현 방법
- implements CanActivate 인터페이스 구현
- canActivate(context: ExecutionContext): boolean 메서드 정의
- 반환 값
- true: 통과 → 요청 핸들러 실행
- false: 차단 → ForbiddenException (HTTP 403)
가드 적용 방식
- 로컬 적용
- 클래스 전체에 적용
-
@UseGuards(RolesGuard) @Controller('cats') export class CatsController {}
-
- 특정 메서드에만 적용
-
@UseGuards(RolesGuard) @Get() findAll() {}
-
- 클래스 전체에 적용
- 글로벌 적용
- 간단한 글로벌 적용
-
const app = await NestFactory.create(AppModule); app.useGlobalGuards(new RolesGuard());
-
- DI(의존성 주입)가 필요한 경우
-
providers: [ { provide: APP_GUARD, useClass: RolesGuard, }, ]
-
- 간단한 글로벌 적용
Auth Guard 생성하기
목표
- 인증되지 않은 사용자가 특정 라우트(예: 영화 생성, 수정, 삭제 등)에 접근하지 못하도록 가드(Guard) 를 구현한다.
기본 설계 및 구조
- src/us/guards/authentication.guard.ts 생성
- 인증용 가드를 간단하게 구현
- 이미 미들웨어에서 JWT 검증이 완료되었고, 그 결과가 request.user에 담겨 있음
Guard 구현 단계
- 클래스 정의 및 데코레이터 추가
-
@Injectable() export class UsGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { ... } }
-
- ExecutionContext로부터 Request 추출
-
const request = context.switchToHttp().getRequest();
-
- 검증 조건
-
if (!request.user || request.user.tokenType !== 'access') { return false; // → 403 Forbidden } return true; // → 라우트 통과
-
로컬 적용
- 특정 라우트(예: 영화 생성)에만 적용하고 싶은 경우:
-
@UseGuards(UsGuard) @Post() createMovie() {}
-
- NestJS 내장 PassportGuard가 아닌, 직접 만든 Guard를 import 해야 함
기능 테스트 (Postman)
- 토큰 없이 POST 요청 시 → 403 Forbidden
- 올바른 Access Token 포함 시 → 정상적으로 요청 통과
글로벌 적용 방법
- 반복 적용 피하기 위해 AppModule에 글로벌 가드 등록
-
providers: [ { provide: APP_GUARD, useClass: UsGuard, } ]
-
- 이렇게 하면 모든 라우트가 기본적으로 private route가 됨
글로벌 가드 적용 후 주의사항
- 로그인(auth/login), 회원가입(auth/register) 등 퍼블릭 라우트도 막히게 됨
- 의도치 않게 외부 공개되어야 할 API까지 막히는 문제가 발생할 수 있음
Public Custom Decorator 생성하기
문제 상황
- AuthGuard를 전역(Global Guard) 으로 적용하면서, 로그인(auth/login)이나 회원가입(auth/register) 등 퍼블릭 라우트까지 차단되는 문제가 발생함.
목표
- 기본적으로 모든 라우트를 프라이빗(private) 으로 설정하고,
- 특정 라우트만 퍼블릭(public) 으로 제외 처리할 수 있도록 커스텀 데코레이터 + 가드 개선을 통해 해결.
커스텀 데코레이터 만들기
- us/decorators/public.decorator.ts 생성
-
import { SetMetadata } from '@nestjs/common'; export const IS_PUBLIC_KEY = 'isPublic'; export const Public = () => SetMetadata(IS_PUBLIC_KEY, true); - SetMetadata()를 통해 'isPublic'이라는 메타데이터를 true로 설정
- @Public() 데코레이터를 통해 퍼블릭 라우트를 마킹할 수 있게 됨
-
라우트에 데코레이터 적용
@Public()
@Post('login')
login() { ... }
@Public()
@Post('register')
register() { ... }
- 이 데코레이터를 붙이면, 가드 통과를 자동으로 허용할 예정
가드 수정 (us/guards/auth.guard.ts)
- Reflector 주입
-
constructor(private reflector: Reflector) {}
-
- 퍼블릭 여부 확인 후 처리
-
const isPublic = this.reflector.get<boolean>(IS_PUBLIC_KEY, context.getHandler()); if (isPublic) { return true; // 퍼블릭이면 모든 로직 바이패스 } - context.getHandler()를 통해 현재 라우트 핸들러의 메타데이터 접근
- isPublic === true이면 인증 검사를 하지 않고 곧바로 통과시킴
-
Bearer Token Middleware 토큰 검증 개선하기
문제 상황: 퍼블릭 라우트에서 "잘못된 토큰"이 들어올 경우
- @Public() 데코레이터가 붙은 퍼블릭 라우트에 요청 시:
- 토큰이 아예 없는 경우 → 정상 동작
- 잘못된 토큰이 들어온 경우 → 403 Forbidden 에러 발생
- 왜 문제가 되나?
- 퍼블릭 라우트는 누구나 접근 가능해야 함
- "잘못된 토큰"이 있다고 해서 에러가 발생하면 안 됨
- 프론트엔드는 보통 모든 요청에 액세스 토큰을 자동으로 포함하므로,
- 퍼블릭 라우트에도 토큰이 포함되는 경우가 많음
원인 분석
- 이 에러는 미들웨어(BearerMiddleware)에서 토큰 검증 실패 시 에러를 던지기 때문
- 그런데 이미 가드(AuthGuard) 에서 인증 여부 판단을 하고 있음
해결 전략: 토큰 검증 실패 시 "조용히 무시"
- 기존 로직 (문제 있음)
-
try { const payload = verify(token, secret); req.user = payload; } catch (e) { throw new UnauthorizedException(); } - → 토큰이 잘못되면 에러 던짐 → 퍼블릭 라우트도 실패
-
- 변경 후 로직
-
try { const payload = verify(token, secret); req.user = payload; } catch (e) { // 에러 무시하고 next() 호출 } next(); - 검증에 실패해도 req.user = undefined인 채로 다음 단계로 넘어감
- 이후 인증 여부는 AuthGuard 내부에서 판단
-
학습 후기
이번 강의를 통해 NestJS에서 가드가 어떤 위치에서 어떤 역할을 수행하는지 명확히 이해할 수 있었습니다. 처음에는 미들웨어와 가드가 유사하게 느껴졌지만, 미들웨어는 요청을 변형하거나 로그를 출력하는 등 비파괴적인 전처리에 가깝고, 가드는 컨트롤러 직전에서 접근을 허용할지 말지를 결정하는 보안 필터라는 점에서 확실히 역할이 구분된다는 점이 흥미로웠습니다.
직접 AuthGuard를 구현하면서, ExecutionContext에서 request 객체를 가져와 req.user를 기반으로 인증 여부를 판단하는 구조는 NestJS의 IoC 철학에 잘 부합한다고 느꼈습니다. 특히 기존에 Bearer Token을 미들웨어에서 파싱하여 req.user에 주입하는 구조와 연결되어, 미들웨어와 가드가 각자 역할을 나누고 있지만 유기적으로 협력하는 구조가 인상 깊었습니다. 인증은 가드가 담당하고, 미들웨어는 정보를 제공하는 조력자로만 남겨두는 구조가 가장 깔끔하고 확장 가능한 아키텍처라고 느꼈습니다.
또한 글로벌 가드 설정을 통해 모든 라우트를 기본적으로 보호하면서, @Public() 데코레이터를 통해 퍼블릭 라우트를 명시적으로 열어두는 방식은 실무에서 자주 겪는 인증/인가 요구사항을 매우 우아하게 해결해준다고 생각합니다. 특히 Reflector를 이용해 메타데이터 기반으로 가드의 동작을 제어하는 방식은, 이후 역할 기반 권한 시스템이나 세분화된 인증 정책으로 확장할 때에도 매우 유용하게 작동할 것이라 판단됩니다.
실제 실습 중 가장 인상 깊었던 부분은, 퍼블릭 라우트에 잘못된 토큰이 포함된 경우에도 요청이 거부되는 문제를 해결한 방식이었습니다. 처음엔 이 현상이 이상하다고 느껴졌지만, 문제는 가드가 아닌 Bearer 토큰 미들웨어에서 에러를 던진 것이 원인이었습니다. 이 부분을 try-catch로 조용히 무시하고 넘어가며, 인증 여부의 최종 판단은 가드에서 하도록 설계한 점은 책임의 분리를 잘 보여주는 사례였습니다.
결론적으로 이번 강의를 통해 단순히 Guard의 사용법만 익힌 것이 아니라, NestJS 아키텍처의 철학과 역할 분리에 대한 깊은 이해를 얻게 되었습니다. 인증, 인가, 역할 권한 등의 보안 관련 요구사항을 구조적으로 어떻게 풀어야 할지에 대한 감을 잡을 수 있었고, 실제 팀 프로젝트나 실무에서도 매우 유용하게 활용할 수 있을 것이라 확신하게 되었습니다.
학습 인증샷




https://abit.ly/lisbva