NestJS 帶你飛! 시리즈 번역 02# Hello NestJS

2023. 5. 28. 17:12개발 문서 번역/NestJS

728x90
이 포스팅은 「NestJS 기초실무 가이드 : 강력하고 쉬운 Node.js 웹 프레임워크로 웹사이트 만들기」
(서명: NestJS 基礎必學實務指南:使用強大且易擴展的 Node.js 框架打造網頁應用程式)
책이 출간되었습니다. 

Nest 기본구조

Nest는 모듈화를 채택하여 각자 다른 기능을 하는 코드 조각들을 '모듈 (Module, 模組)'로써 패키징할 수 있습니다.

(레고 블럭과 비슷한 개념) 모듈은 최소 1개 이상이여야 하며, 이 모듈은 트리 구조(Tree, 樹狀結構)로 나아가는데 가장 상위 모듈에는 루트 모듈 (Root Module, 根模組)라고 하는것이 존재합니다.

하나의 라우팅 메커니즘이 있는 모듈에는 컨트롤러(Controller, 控制器)와 (서비스Service, 服務)가 구성되며 이들의 관계도는 아래와 같습니다.

 

사진에서 알수 있듯이 컨트롤러, 서비스는 모듈을 통해 관계가 생성되고 서비스는 컨트롤러에서 주입 (Inject, 注入) 되어 사용되고 있는데 이렇게 사용하게 되면 역할마다 분리할 수 있고 응답에 대한 요청(HTTP Status Code 등)을 컨트롤러단에 넘긴다던가 비즈니스 로직만을 서비스에서 처리할 수 있게 되는것이 가장 큰 장점입니다.

이렇게 말하니 너무 추상적이기도 한데요. 응답과 처리 과정을 하나씩 알아보겠습니다.


1. 유저는 Nest App을 통해 HTTP 요청을 보냅니다.

2. 컨트롤러는 이 요청을 받아들이고

3. 주입을 통해 Service에 있는 메서드를 가져와 사용하고, 그 다음 그 뒤의 로직들을 처리합니다.

4. 서비스는 결과를 반환하고, 컨트롤러는 그 결과를 유저에게 제공합니다.


 

사실 이렇게 말하면 더 복잡해보이는거 같네요. 우리가 살고있는 현실에서 비유를 해볼까요?

Nest App은 하나의 세계 요리를 파는 레스토랑이라고 생각하면 됩니다. 

레스토랑에서는 각 나라의 음식들을 하나의 블록(구역)으로 정하고, 각 블록에는 서빙하는 웨이터가 있는거죠.

이 블록에는 모듈과 컨트롤러가 그 웨이터입니다. 서비스는 주방에서 일하는 직원인거죠.

손님이 레스토랑에 들어오면 먹고싶은 음식에 따라 자리를 안내한 후, 주문을 하면 서빙하는 웨이터가 손님에게 음식을 서빙합니다.

(사진에서는 손님(客人)이 홀에 도착해 대만 취두부를 주문(想吃台灣美食臭豆腐) ->

그 주문을 대만음식을 만드는곳에 가져다주고 (帶到台灣美食區)

주방 인원이 다 만들면(服務生 -> 内場人員) ->

홀의 서빙 웨이터가 손님에게 배달합니다 (臭豆腐來囉))

 

지금까지 Nest의 기본 개념을 배워보았습니다. 그럼 이제 어떻게 Nest App를 만들어야 하는지에 대해 알아볼까요?

 

NestCLI 설치

Nest의 공식 사이트는 CLI를 통해 개발 속도를 더 높일수 있도록 지원하고 있는데

이 NestCLI를 통해 기초 뼈대를 자동으로 생성할 수 있습니다. 같은 작업을 CLI로 대체해 자동 생성하므로 개발자가 싫어하는 반복작업을 효과적으로 해결할 수 있습니다. CLI는 npm을 통해 설치하면 되는데 명령어는 다음과 같습니다.

$ npm install -g @nestjs/cli

설치가 완료되었다면 해당 명령어를 통해 도움말을 확인할 수 있습니다.

$ nest --help

 

APP 만들기

NestCLI로 APP을 만드는 명령어 :

주의 : 명령어를 입력하기 전 현재 터미널의 디렉토리가 어디인지 잘 확인 후 입력해주세요.
원하는 위치에 있지 않다면 cd 명령어를 통해 원하는 디렉토리로 이동하면 됩니다.
$ nest new <APP_NAME>

명령어가 실행되고 나면 이런 화면이 등장합니다.

원하는 패키지 매니저를 선택 후 엔터를 눌러줍니다. 

 

Nest의 기본 포트는 3000입니다. http://127.0.0.1:3000 으로 들어가게 되면 ...

쉽게 Hello World를 띄워진 화면을 볼 수 있습니다.

 

폴더 구조

.
├─ dist
├─ node_modules
├─ src
|  ├─ app.controller.ts
|  ├─ app.controller.spec.ts
|  ├─ app.module.ts
|  ├─ app.service.ts
|  └─ main.ts
├─ test
|  ├─ app.e2e-spec.ts
|  └─ jest-e2e.json
├─ .eslintrc.js
├─ .gitignore
├─ .prettierrc
├─ nest-cli.json
├─ package.json
├─ package-lock.json
├─ tsconfig.json
├─ tsconfig.build.json
└─ README.md

NestCLI를 통해 생성된 프로젝트의 폴더 구조는 기본적으로 이렇게 생겼습니다.

 

  • dist: TypeScript 컴파일 후의 생성되는 JavaScript 파일이 담겨있습니다.
  • node_modules: npm install <PACKAGE>로 생성된 패키지들이 담겨있는 폴더입니다.
  • src: 프로젝트의 파일이 담겨있는 곳입니다. 
  • test: 테스트 파일이 담겨있는 폴더입니다.
  • .eslintrc.js: ESLint의 설정파일이며 주로 문법적인 오류나 안티 패턴에 대한 설정이 쓰여 있습니다.
  • .gitignore: 필요없는 파일을 github repository에 올리지 않게 하기 위한 설정 파일입니다.
  • .prettierrc:Prettier의 설정파일입니다. 코드를 주로 정해진 양식에 맞게 설정해줍니다.
  • nest-cli.json: NestCLI의 설정파일입니다.
  • package.json: 프로젝트내의 정보를 담고 있습니다.
  • package-lock.json:  package.json으로는 다 담지못한 의존성 패키지의 버전을 담고 있습니다. (정확한 세부 버전)
  • tsconfig.json: TypeScript의 설정 파일입니다.
  • tsconfig.build.json: TypeScript의 빌드 설정파일입니다. tsconfig.json와 비슷하지만 빌드 환경에서 사용할 tsconfig를 정의할 수 있습니다.

 

코드 분석

주의 : 이 부분은 먼저 간단하게 소개 후 넘어갈 예정입니다. 추후 포스팅에서 각 기능마다 상세하게 설명하겠습니다.

먼저 src 폴더에 테스트용인 app.controller.spec.ts 를 빼고 보면 크게 main.ts, app.module.ts, app.controller.tsapp.service.ts가 보입니다.

 

 

진입점 (Entry Point)

NestJS는 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();

비동기식으로 쓰여진 bootstrap 함수를 진입 합수로써 사용하고 있고 NestFactory.create(AppModule)을 통해 하나의 Nest App의 인스턴스를 생성하는 동시에 예제에 쓰여진 listen(PORT) 를 통해 해당 진입점의 포트를 몇번으로 사용할 것인지 설정할 수 있습니다.

주의 : 포스팅에서는 PORT는 기본값인 3000번으로 사용합니다.

 

루트 모듈

Nest에서는 app.module.ts 를 루트 모듈로써 사용하고 있습니다.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Nest에서는 대부분의 컴포넌트를 데코레이터 (Decorator, 裝飾器) 를 사용하여 메타데이터 (Metadata, 元數據)를 제공합니다. 해당 예제에서는 AppModuleClass 가 정의된 모습을 확인할 수 있는데 데코레이터를 통해 Nest의 모듈로써 사용할 수 있는것입니다.

또한 컨트롤러와 서비스가 각각 controllers, providers에 정의된 모습도 확인할 수 있습니다.

app.controller.ts는 루트 모듈 아래에 등록된 Controller 입니다.

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

이 코드에서는 데코레이터를 포함한 class가 보입니다. 이 안의 방법이 조금 특별한데요. 데코레이터를 통해 정의를 한 후 해당 메서드를 사용할 때 트리거 되어 사용됩니다.

그외에 constructorappService 가 정의되어 있는걸 확인할 수 있습니다.

이는 의존성 주입 (Dependency Injection, DI 依賴注入)을 통해 AppServiceAppController에게 주입함을 의미합니다.

 

app.service.ts는 루트모듈 아래에 Service에 등록되어 있습니다.

 

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

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

Module과 Controller와는 조금 다른 모습입니다. Service에서 사용된 데코레이터는 Injectable인데 이 데코레이터가 사용된 이유는 Service는 추상에 속해있는 개념이기 때문입니다. Nest에서 많은 추상 개념들은 모두 Injectable을 통해 사용됩니다. 이를 통칭하여 프로바이더 (Provider)라고 부릅니다.

 

마치며

Nest는 높은 수준의 모듈화를 제공합니다.

Controller와 Service를 정의하여 Module안에 집어넣기만 하면 되니까요!

퍼즐 맞추기를 생각해봅시다. 관련있는 모듈들을 전부 관련지어 한데 모아 놓으면
유지보수도 쉽고, 결합도도 낮아지며, 확장이 쉬워진 Node.js의 백엔드 애플리케이션을 만들 수 있습니다.

Nest가 만든 NestCLI를 쓴다면 더더욱 시간을 절약할 수 있습니다.