ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Node.js] NestJS 주요 기능 알아보기(DTO, Validation, Guard, 인증과 예외처리)
    📘 Node.js 2026. 8. 17. 22:15

    NestJS의 전체적인 구조

    이전에 설명했던 내용들을 하나로 연결하면 아래와 같은 구조가 된다.

    Client
       │
       │ HTTP Request
       ▼
    Controller
       │
       │ 요청 처리
       ▼
    Service
       │
       │ 비즈니스 로직
       ▼
    Repository / Model
       │
       ▼
    Database

     

    그리고 이러한 기능들을 Module이 묶어준다.

    AppModule
    │
    ├── AuthModule
    │   ├── AuthController
    │   └── AuthService
    │
    ├── UserModule
    │   ├── UserController
    │   └── UserService
    │
    └── DocumentModule
        ├── DocumentController
        └── DocumentService

     

    이 구조를 이해하면 NestJS의 기본적인 형태는 대부분 이해했다고 볼 수 있다.

     

    📍  Decorator

    이전 글에도 짧게나마 Decorator에 대해 설명했지만 다시한번 되짚어 보려고 한다.

     

    NestJS 코드를 보면 @로 시작하는 코드가 많이 등장한다.

    @Controller()
    @Get()
    @Post()
    @Body()
    @Injectable()
    @Module()

     

    이것을 Decorator라고 한다.

    @Controller('users')

     

    위 코드는 해당 클래스를 Controller로 지정하고,

    @Get()

     

    위 코드는 해당 메서드가 GET 요청을 처리하도록 지정한다.

     

    즉, Decorator를 통해 코드에 특정 역할을 부여할 수 있다.

    필자가 생각하기에 Spring의 Annotation(어노테이션)과 비슷한 개념인듯 싶다.

     

    📩 데이터를 전달받는 방법

    NestJS에서는 HTTP 요청 데이터를 Decorator를 이용해 받을 수 있다.

    예를 들어 JSON Body를 받는다면

    @Post()
    createUser(@Body() body: any) {
        console.log(body);
    }

     

    위 코드에 아래와 같이 요청이 들어왔을 때,

    POST /users
    Content-Type: application/json
    
    {
        "name": "홍길동",
        "email": "test@example.com"
    }

     

    @Body()를 통해 전달받은 body에는 아래와 같이 데이터가 들어온다.

    {
        name: '홍길동',
        email: 'test@example.com'
    }

     

    URL Parameter도 사용할 수 있다.

    @Get(':id')
    getUser(@Param('id') id: string) {
        return this.userService.getUser(id);
    }

     

    위 코드에 아래와 같이 Get 요청하면

    GET /users/10

     

    그러면 id에는 "10"이 전달된다.

     

    DTO란 ?

    NestJS를 공부하다 보면 DTO(Data Transfer Object)​라는 개념도 자주 만나게 된다.

    예를 들어 회원가입 API가 있다고 해보자.

    export class SignupDto {
        email: string;
        password: string;
        name: string;
    }

     

    Controller에서는 다음과 같이 사용할 수 있다.

    @Post('signup')
    signup(@Body() signupDto: SignupDto) {
        return this.authService.signup(signupDto);
    }

     

    이렇게 하면 회원가입에 필요한 데이터의 형태를 DTO로 명확하게 표현할 수 있다.

    그리고 ValidationPipe와 class-validator를 함께 사용하면 입력값 검증도 쉽게 구성할 수 있다.

    예를 들어:

    import { IsEmail, IsNotEmpty } from 'class-validator';
    
    export class SignupDto {
    
        @IsEmail()
        email: string;
    
        @IsNotEmpty()
        password: string;
    
        @IsNotEmpty()
        name: string;
    }

     

    이제 이메일 형식이나 필수값 등을 코드에 명확하게 표현할 수 있다.

     

    🔏 인증도 구조적으로 관리할 수 있다

    NestJS에서는 인증 기능도 여러 기능으로 나누어 관리할 수 있다.

    예를 들어 JWT 인증을 사용한다면,

    AuthModule
    │
    ├── AuthController
    ├── AuthService
    ├── JwtStrategy
    └── JwtAuthGuard

     

    위와 같이 구성할 수 있다.

     

    그리고 특정 API에 인증이 필요하다면,

    @Get('mypage')
    @UseGuards(JwtAuthGuard)
    getMyPage() {
        return '로그인 사용자만 접근 가능';
    }

     

    위처럼 Guard를 적용할 수 있다.

     

    이처럼 NestJS에서는 인증, 데이터 검증, 예외 처리 등의 기능을 각각의 역할에 맞게 분리할 수 있다.

     

    결론적으로 Express와 NestJS는 무엇이 다를까 ?

    둘 다 Node.js 환경에서 서버를 개발할 수 있지만 개발 방식에는 차이가 있다.

    성격 가볍고 자유로운 웹 프레임워크 구조화된 서버 프레임워크
    기본 언어 JavaScript TypeScript
    프로젝트 구조 자유롭게 구성 일정한 구조 제공
    Routing Router Controller + Decorator
    비즈니스 로직 직접 구조 설계 Service
    의존성 관리 직접 구성 Dependency Injection
    기능 분리 개발자가 결정 Module
    인증 Middleware 등으로 구성 Guard + Strategy 등
    데이터 검증 직접 구현 DTO + Pipe
    진입장벽 비교적 낮음 Express보다 높은 편

     

    다만 Express도 TypeScript를 사용할 수 있고, NestJS도 내부적으로 Express 또는 Fastify를 HTTP 플랫폼으로 사용할 수 있다는 점은 알아두어야 한다.

     

    따라서 단순히

    Express = JavaScript
    NestJS = TypeScript

     

    라고 생각하면 안 되는 것 같다.

     

     

    필자가 Nest.js를 공부하면서 느낀점 중 하나인데,

     

    Nest.js에서는 간단한 API 하나를 만들기 위해서는

    Module
    Controller
    Service
    DTO

     

    등 여러 개념을 접해야 하기 때문에 생각보다 어려운 개념들이 많지만,

     

    각각의 역할을 이해하면 구조는 생각보다 단순한 것 같다.

    Module
    → 기능을 묶는다.
    
    Controller
    → HTTP 요청을 받는다.
    
    Service
    → 비즈니스 로직을 처리한다.
    
    DTO
    → 전달받는 데이터의 형태를 정의한다.
    
    Guard
    → 접근 권한이나 인증을 검사한다.
    
    Pipe
    → 데이터를 변환하거나 검증한다.

     

    결국 핵심은 하나의 파일에 모든 기능을 넣는 것이 아니라 각자의 역할에 맞게 나누는 것이다.

Designed by Tistory.