4 분 소요

오늘은 본격적으로 Nest.js를 사용하기 앞서 어떠한 구조로 되어 있는가를 살펴보겠다.

Nest.js의 구조

처음 Nest.js로 어플리케이션을 만들게 되면 이런 파일들이 생긴다.

파일들을 하나씩 알아보자.

  • app.controller.ts : 단일 경로가 있는 기본 컨트롤러 이다.
  • app.controller.spec.ts : 컨트롤러에 대한 단일 테스트 파일.
  • app.module.ts : 어플리케이션의 루트 모듈.
  • app.service.ts : 하나의 메소드만 있는 서비스.
  • main.ts : NestFactory를 사용해서 Nest 어플리케이션 인스턴스를 생성하는 어플리케이션의 시작점.

이런식으로 이루어져 있다.

main.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();

main.ts는 비교적 간단하게 작성이 되어 있다. bootstrap이라는 함수를 정의하고 실행을 시킨다. 함수 내에서는 NestFactory를 사용하는데, NestFactroy는 어플리케이션 인스턴스를 생성하게 해준다. create() 메소드를 사용하여 AppModule 객체를 만들어준다. 이 객체는 3000번 포트를 열고 HTTP 요청을 기다리며, 요청이 오면 이를 처리하고 응답을 보내는 웹서버의 역할을 한다.

플랫폼

플랫폼이란, 어느 웹 어플리케이션 프레임워크를 사용할 것이냐 이다. Nest.js는 기본적으로 웹 어플리케이션의 프레임워크인 express를 default로 갖는다. 하지만 fastify도 사용할 수 있는데, express보다 2배 정도가 빠르다고 한다. fastify는 다음에 다시 한번 알아보도록 하자.

  • @nestjs/platform-express
  • @nestjs/platform-fastify

Nestfactory.create() 옵션

create() 메소드의 인자로 사용할 수 있는 옵션에 대해서 살펴 봤다.

옵션으로 cors, bodyParser, httpsOptions 등 선택을 할 수 있는 것 같다. Node.js에서는 cors 라이브러리를 install 하여 사용해야 하는데, 기본적으로 내장이 되어 있는 것 같다. 옵션에 관한 내용은 프로젝트를 하면서 조금 더 알아보자.

app.module.ts

Main.ts에서 만든 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 {}

@Module 데코레이터의 인자로 ModuleMetadata를 받는다. ModuleMetadata의 인터페이스는 이렇게 구성되어 있다.

메타데이터 : 다른 데이터를 설명해 주는 데이터이다.

export interface ModuleMetadata {
  /**
   * Optional list of imported modules that export the providers which are
   * required in this module.
   */
  imports?: Array<
    Type<any> | DynamicModule | Promise<DynamicModule> | ForwardReference
  >;
  /**
   * Optional list of controllers defined in this module which have to be
   * instantiated.
   */
  controllers?: Type<any>[];
  /**
   * Optional list of providers that will be instantiated by the Nest injector
   * and that may be shared at least across this module.
   */
  providers?: Provider[];
  /**
   * Optional list of the subset of providers that are provided by this module
   * and should be available in other modules which import this module.
   */
  exports?: Array<
    | DynamicModule
    | Promise<DynamicModule>
    | string
    | symbol
    | Provider
    | ForwardReference
    | Abstract<any>
    | Function
  >;
}

imports, controllers, providers 으로 이루어져 있다. @Module 데코레이터는 Nest가 애플리케이션 구조를 만들때 사용할 수 있는 메타데이터를 제공하는 역할을 한다.

이 데코레이터는 providers, controllers, imports의 객체가 필요하다.

  • providers : Nest 인젝터(Injector : 의존성을 주입하는 Nest 내부 모듈)가 인스턴스화 시키고 적어도 이 모듈안에서 공유하는 프로바이더
  • controllers : 이 모듈안에서 정의된, 인스턴스화 되어야하는 컨트롤러의 집합
  • imports: 해당 모듈에서 필요한 모듈의 집합. 여기서 들어가는 모듈은 프로바이더를 노출하는 모듈이다.

여기서 데코레이터가 나오게 된다. 데코레이터에 대해 알아보자.

데코레이터

데코레이터는 TypeScript에서 사용되어진다. 그래서 tsconfig.json에서 설정을 해주어야 사용이 가능하다.

tsconfig.json에서 “experimentalDecorators”: true을 해주어야 사용이 가능하다.

기본적으로 ‘@’로 시작을 한다. 자바의 어노테이션과 파이썬의 데코레이터와 유사한 기능을 한다고 한다.

  • 데코레이터는 클래스 선언, 메서드, 접근자, 프로퍼티 또는 매개 변수에 첨부할 수 있는 특수한 종류의 선언이다.
  • 데코레이터 함수에는 target, key, descriptor가 전달이 된다.
  • 메소드나 클래스 인스턴스가 만들어지는 런타임에 실행이된다. 즉, 매번 실행이 되지 않는다.
  • 매개 변수 데코레이터는 생성자에 적용된다.
  • 클래스 데코레이터는 클래스에 적용된다.

설명글을 읽어봤는데 아직은 잘 모르겠다. 실습을 하면서 익혀야 이해가 갈 것 같다.

모듈의 구조

현재 이 AppModuel은 루트 모듈이다.

React의 컴포넌트 트리 구조 처럼 이루어져 있다.

기능 모듈

하위 컴포넌트 모듈을 만들려면 이런식으로 하면 된다.

import { Module } from "@nestjs/common";
import { CatsModule } from "./cats/cats.module";

@Module({
  imports: [featureModule],
})
export class AppModule {}

이런식으로 @Module 데코레이터의 imports에 하위 컴포넌트를 넣어주면 된다.

공유 모듈

공유모듈을 사용하기 위해선 exports를 사용한다.

import { Module } from "@nestjs/common";
import { CatsController } from "./cats.controller";
import { CatsService } from "./cats.service";

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

app.controller.ts

app.controller.ts는 이렇게 작성 되어 있다.

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();
  }
}

먼저 보이는 것은 @Controller() 이다. 이는 컨트롤러의 역할을 한다 라고 명시를 하는 것과 같다. 이 데코레이터에 인자를 전달 할 수 있는데, 라우팅 경로의 prefix를 지정할 수 있다. prefix는 보통 컨트롤러가 맡은 리소스의 이름을 지정하는 경우가 많다.

다음으로 보이는 것은 AppController 클래스 내부에 있는 @Get 데코레이터이다. 이는 HTTP 통신 메소드 중에 하나로 익숙할 것이다. 여기에도 마찬가지로 path를 지정해줄 수 있다.

@Get, @Post, @Delete, @Patch, @Put과 같은 HTTP 메소드들은 MethodDecorator에 속하는 것 같다.

컨트롤러는 우리가 express에서 사용했었던 Route와 비슷하다고 생각하면 될 것 같다.

또한 클래스 내부의 getHello 함수의 인자로 request, response 뿐 아니라 @Qurey, @Param(key?: string), @Body 데코레이터를 이용해서 요청에 포함된 쿼리 파라미터, 패스 파라미터, 본문을 쉽게 받을 수 있도록 해준다.

주의해야 할 점은 컨트롤러에서 비즈니스 로직을 직접 수행하지 않는다.라는 것이다.

app.service.ts

app.service.ts는 이렇게 작성되어 있다.

import { Injectable } from "@nestjs/common";

@Injectable()
export class AppService {
  getHello(): string {
    return "Hello World!";
  }
}

@Injectable() 이라는 데코레이터가 있는데, 이는 메타 데이터를 첨부하여 AppService가 Nest IoC 컨테이너에서 관리할 수 있는 클래스 임을 선언 한다는 뜻이다. 아직은 잘 모르겠지만 다음에 더욱 자세히 알아보자.

마치며

오늘 기본적으로 Nest로 어플리케이션을 생성했을 때, 생성된 파일이 어떠한 역할을 하는지 알아보았다. 내일은 조금 더 자세하게 모듈, 컨트롤러, 프로바이더를 알아보도록 하겠다.

댓글남기기