NestJS는 Express를 기반으로 돌아가는 (혹은 Fastify) NodeJS의 서버 프레임워크이다. typescript를 네이티브로 지원하기 때문에 typescript에서 사용하기 편하고 데코레이터 클래스를 통해서 다양한 기능을 정형화/구조화 하여 설계할 수 있다. 때문에 코드 스타일이 통일되어 협업 프로젝트 진행 시 각자 다른 스타일로 커뮤니케이션을 좀 더 자연스럽게 할 수 있다. 또한 CLI를 통해 보일러플레이트 코드를 생성하여 간단하게 프로젝트를 시작할 수 있다. 하지만 대부분의 코드가 클래스구문을 통해 설계되기 때문에 자바스크립트 클래스에 대한 확실한 이해가 필요하다.
NestJS는 React와 마찬가지로 보일러플레이트를 사용해 프로젝트를 생성할 수 있다. @nestjs/cli
를 설치하여 CLI도구를 설치해 준다.
yarn global add @nestjs/cli
CLI를 설치한 뒤 다음 명령을 통해서 NestJS 프로젝트를 만들 수 있다. 시간이 조금 걸린다.
nest new *project_name*
명령을 실행한 폴더 내에 project_name에 해당하는 폴더를 생성한 뒤 그 안에서 초기화를 해준다.
폴더 구조를 살펴보면,
src
| main.ts # 앱의 시작점
| app.module.ts # 앱의 루트모듈
| app.controller.ts # 앱의 컨트롤러
| app.service.ts # 앱의 서비스
이런 구조로 되어있다. 하나씩 코드를 살펴보자
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
bootstrap
이라는 함수를 만든 뒤 바로 실행한다. 구조를 보고 생각 해 봤을때는 서버를 동작하게 해주는 말단지점으로 파악된다. 함수명은 임의로 지어도 잘 작동한다. NestFactory
클래스를 이용해 AppModule
클래스를 받아와서 app
객체를 만든다. 그리고 그 listen
메소드를 사용해서 포트를 열어준다. 기존의 express
를 사용할 때와 비슷한 느낌이 있다.
그렇다면 NestFactory
에서 받아오는 AppModule
의 구조를 살펴보자
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
데코레이터가 사용되었다. 모양을 봤을때는 class AppModule
을 꾸며주는 역할로 보인다. 한 눈에 봤을 때는 imports
, controllers
와 providers
를 배열로 받아서 AppModule
클래스를 만들어주는 역할을 하는 것 같다. 여기저기 흩어져있는 코드를 AppModule
로 묶어서 export
한 뒤 NestFactory
에서 받아서 서버를 만들어주는 걸로 예상된다.
먼저 controllers
에서 배열로 받은 AppController
를 확인해 보자
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
이번에도 역시 데코레이터가 사용되고 있다. 아무래도 Nestjs
는 데코레이터를 적극적으로 사용해서 class
구조를 조작하는 듯 하다.
하나씩 살펴보면 @Controller
데코레이터를 달고있는 AppController
클래스에서 AppService
클래스를 인자의 타입으로 받은 뒤 @Get
데코레이터를 통해 AppService
타입 내의 getHello
메소드를 호출해 사용하는 듯 하다. 타입 지정만 받았을 뿐인데, 어떻게 그 안에 있는 메소드를 사용할 수 있는지는 모르겠다. 아마 app.module.ts
내에서 providers
로 전달됐기 때문에 호출되는 게 아닐까 추측
@Controller
데코레이터를 통해 Controller
클래스를 정의해 줄 수 있는 듯하다. Controller
는 Express
에서 Route
기능을 대신 해 주는 듯 하고 각 Controller
안에 @Get
, @Post
, @Put
, @Delete
등의 HTTP request
를 정의하는 모양이다.
마지막으로 providers
의 배열로 받고 AppController
에서도 Controller
에서 전달받은 AppService
클래스를 살펴보자
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
이번에도 역시 @Injectable
데코레이터를 사용해 클래스를 정의한다. 위의 app.controller.ts
에서 사용한 getHello
메소드가 정의되어 있다. 'Hello World!' 라는 문자열을 출력하는데 실제로 서버를 작동시키고 localhost:3000
으로 접속하면 화면에 Hello World! 라는 문자열이 출력된다. 이를 통해 providers
에 전달된 클래스의 메소드를 Controller
를 통해서 사용할 수 있다고 확인할 수 있다.
지금까지의 구조를 대략적인 구조를 이미지로 설명하자면 아래와 같다.
전체적으로 NestJS
는 분리할 수 있는 단위의 기능들을 모듈로 정의해서 가능한 독립적으로 사용한다. 물론, 다른 모듈과 상호작용을 하게 설계할 수도 있는 듯 하다. 가장 근본이 되는 App Module
을 통해 또 다른 모듈을 imports
를 통해 배열로 전달받을 수 있다. 하나의 모듈 내에는 복수의 Controller
와 Service
도 존재할 수 있다. 하지만 하나의 단위 기능은 모듈로 묶어야 NestJS
를 효율적으로 사용할 수 있다.
앞으로 Controller
, Service
, Module
을 하나씩 살펴보자.