[Side-Project] Nest.js 구조
오늘은 본격적으로 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로 어플리케이션을 생성했을 때, 생성된 파일이 어떠한 역할을 하는지 알아보았다. 내일은 조금 더 자세하게 모듈, 컨트롤러, 프로바이더를 알아보도록 하겠다.
댓글남기기