1. NestJS가 왜 필요할까?

실은 이 포스트를 하기 전에, 교재에 나온 Pagination 코드를 익스프레서르 router > controller > service 구조로 따라가며 만들었는데, 이해하기도 어렵고… 결국 NestJS를 사용하기 위한 징검다리치고는 할게 너무 많아 접었다. 어쨋든 필자분이 말하길, “NestJS는 서버 개발 시의 아키텍처를 누구든 비슷하게 설계하도록 아키텍처 문제를 해결하는데 중점을 두고 있다.” 라는 내용 정도만 살피고 넘어가겠다.

2. NestJS 소개

NestJS는 자바스크립트 최신 기능을 사용하는 웹 프레임워크이다. 좋은 구조로 애플리케이션을 작성해 프로젝트의 복잡성을 잘 관리하는 것을 목표로 한다. NestJS로 코드를 작성해보기 전에 우선 NestJS가 무엇인지 왜 만들었는지, 왜 익혀두면 좋은지 알아보자.

  1. Node.js에서 실행하는 서버 사이드 프레임워크이다.
  2. 타입스크립트를 완벽하게 지원한다.
  3. 자바스크립트의 최신 스펙을 사용한다. 그러므로 바닐라 자바스크립트를 사용한다면 babel 사용이 필수이다.
  4. HTTP 요청 부분은 추상화된 코드를 제공해 익스프레스와 패스티파이를 사용할 수 있다.

2-1. 익스프레스와 NestJS 비교

앞에서 했던 실습(페이지네이터-비록 접었지만;)에서 서버를 만들 때 폴더 구조들을 어떻게 잡을지 정하는 내용이 있었다. NestJS에서는 컨트롤러를 어디에 둘지, 서비스를 어디에 둘지, 미들웨어를 어떤 식으로 작성할지 등 개발자의 고민거리를 미리 정리해두었다. 이를 데코레이터 기반으로 제공하고 있어, 배우기가 쉽다.

익스프레스는 최소한의 기능을 제공하는 반면 NestJS는 상대적으로 조금 더 많은 기능을 제공한다.

  익스프레스 NestJS
간략 소개 미니멀리스트 웹 프레임워크 자바스크립트의 최신 기능을 사용해 효율성을 추구하며 상업용 서버 애플리케이션 구축을 목표로 하는 프레임워크
라우터 직접 라우터 함수를 추가하거나, 미들웨어 사용 @Controller() 데코레이터 사용
의존성 주입 없음 잘 만든 의존성 주입 기능을 제공함. 서비스의 의존 관계의 관리가 쉬움.
에러 핸들링 직접 에러 처리를 해야 함 @Catch() 데코레이터를 사용
테스트 직접 테스트 관련 도구들을 설치 및 실행해야 함 jest를 기반으로 한 내장 테스트 모듈을 제공
인기도 Node.js에서 가장 인기 있는 프레임워크 두 번째로 인기있는 프레임워크
아키텍처 특정 아키텍처를 요구하지 않음 컨트롤러, 프로바이더, 모듈을 사용한 애플리케이션 아키텍처 제공

3. NestJS 설치하고 실행하기

NestJS 구동에 필요한 라이브러리를 설치하는 방식은 세가지가 있는데, 필자의 추천대로 우선 첫번째 방식을 따라 진행하겠다.

  1. NestJS 구동에 필요한 라이브러리들을 한땀한땀 설치, 설정하는 방법
  2. nes-cli 패키지를 사용하는 방법
  3. git clone으로 개발자의 디렉터리 내려받아 설정을 변경하는 방법

3-1. 의존성 패키지 설치하기

순서 1. 패키지 설치
npm i @nestjs/core @nestjs/common @nestjs/platform-express reflect-metadata typescript
순서 2. package.json 들여다보기
// chapter8 > hello-nest > package.json
{
  "dependencies": {
    "@nestjs/common": "^10.1.3",
    "@nestjs/core": "^10.1.3",
    "@nestjs/platform-express": "^10.1.3",
    "reflect-metadata": "^0.1.13",
    "typescript": "^5.1.6"
  }
}
  • @nestjs/common: 실제 프로젝트에서 사용할 대부분 코드가 들어있음. 데코레이터로 사용하는 함수들의 클래스들이 대표적
  • @nestjs/core: 가드, 미들웨어, 파이프 등을 만드는 핵심코드가 있음
  • @nestjs/platform-express: HTTP 요청/응답 부분을 감싸서 익스프레스의 req, res 객체를 사용하는 라이브러리

3-2. 타입스크립트 설정하기

NestJS는 타입스크립트를 완벽하게 지원한다고 한다. 그래서 이 교재에서도 타입스크립트로 실습을 진행한다. 아래 경로로 tsconfig.json 파일을 생성한다.

// chapter8 > heelo-nest > tsconfig.json
{
    "compilerOptions": { // 1. 컴파일러 옵션
        "module": "CommonJS", // 2. 모듈 시스템
        "target": "ESNext", // 3. 사용할 ES 버전
        "experimentalDecorators": true, // 4. 데코레이터를 사용할지 여부
        "emitDecoratorMetadata": true // 5. 데코레이터의 메타 데이터를 같이 내보낼지 여부
    }
}
  1. 타입스크립트의 컴파일러 옵션은 complierOptions에 설정한다.
  2. 컴파일 시 모듈 시스템을 선택할 수 있으며 NodeJS의 모듈 시스템은 CommonJS이므로 CommonJS로 넣어준다.
  3. target에는 컴파일 시 사용할 ECMA 버전을 적어준다. ESNEXT를 설정하면 최신 버전으로 컴파일 해준다.
  4. experimentalDecorators와 5. emitDecoratorMetadata는 데코레이터 관련 부분이다. Nest JS에서는 데코레이터를 매우 많이 사용하므로 꼭 필요한 옵션이다.
  5. emitDecoratorMetadata는 타입스크립트를 자바스크립트로 컴파일 시 데코레이터가 설정된 클래스, 함수, 변수, 객체의 메타 데이터를 함꼐 넣어줄지 여부를 선택한다. 메타 데이터에는 데코레이터를 달아준 곳이 함수인지, 클래스인지 변수인지에 대한 여부와 매개변수가 있다면 해당 타입, 그리고 결괏값을 포함한다.

3-3. NestJS의 모듈과 컨트롤러 만들기

NestJS는 HTTP 요청/응답을 처리한다. NestJS에서는 HTTP 요청을 보통 ‘파이프 → 가드 → 콘트롤러 → 서비스 → 리포지토리’ 순서로 처리한다. 이중에 컨트롤러는 필수이다. 콘트롤러는 클라이언트에서 온 요청을 코드에 전달하는 역할을 한다. 따라서 NestJS를 최소한의 코드로 실행시키려면 하나의 모듈과 하나의 컨트롤러가 필요하다.

image-20230819174554104


// chapter8 > hello-nestjs/src/hello.controller.ts

import { Controller, Get } from "@nestjs/common"; // 1. 필요한 함수 임포트

@Controller() // 2. 컨트롤러 데코레이터
export class HelloController { // 3. 외부에서 사용하므로 export를 붙여줌
    @Get() // 4. GET 요청 처리 데코레이터
    hello() {
        return "안녕하세요! NestJS로 만든 첫 애플리케이션입니다.";
    }
}

2번. 앞에 @가 붙어있으면 데코레이터. 매개변수로 경로를 지정할 수 있는데, 현재는 아무것도 붙어있지 않아 localhost:3000을 사용


// chapter8 > hello-nestjs/src/hello.module.ts

import { Module } from "@nestjs/common";
import { HelloController } from "./hello.controller";

@Module({ // 1. 모듈 데코레이터
    controllers: [HelloController],
})
export class HelloModule { }

1번. @Module은 모듈을 설정할 때 사용하는 데코레이터이다. 몇 가지 설정이 있으나 여기서는 controllers만 설정. controllers에는 배열로 모듈에 포함된 컨트롤러들을 설정. 현재는 HelloController하나만 설정됨.

####

3-4. hello-nest 앱 실행시키기

아래 코드는 위에서 만든 콘트롤러(hello.controller.ts)와 모듈(hello.module.ts)을 실행할 main.ts 이다.

import { NestFactory } from "@nestjs/core";
import { HelloModule } from "./hello.module";

// 1. NestJS를 시작시키는 함수
async function bootstrap() {
    // 2. NestFactory를 사용해서 NestApplication 객체 생성
    const app = await NestFactory.create(HelloModule);

    // 3. 3000번 포트로 서버 기동
    await app.listen(3000, () => { console.log("서버 시작!") });
}

bootstrap();

1번. NestJS에서 진입점을 bootstrap()으로 이름짓는 것이 관례

2번. NestFactory는 사실 NestFactoryStatic 클래스이며 create() 함수에 루트 모듈을 넣어서 NestApplication 객체를 생성한다. NestApplication 객체에는 HTTP 부분을 모듈화한 HTTPAdapter가 있다. 기본적으로 익스프레스가 사용된다.

3번. NestJS의 애플리케이션을 실행하는 코드는 익스프레스와 같다.


서버 실행을 위해, ts-node-dev 패키지를 설치한다.

image-20230819181815115

image-20230819181836542

3-5. NestJS의 네이밍 규칙

  • 규칙 1. 파일명은 .으로 연결한다. 모듈이 둘 이상의 단어로 구성되어있으면 대시로 연결한다.
// <모듈명>.<컴포넌트명>.ts
hello.controller.ts
my-first.controller.ts
</>

  • 규칙 2. 클래스명은 카멜케이스를 사용한다.
// <모듈명><컴포넌트명>
HelloController

  • 규칙 3. 같은 디렉터리에 있는 클래스는 index.ts를 통해서 임포트하는 것이 권장됨
// index.ts를 사용하지 않는 경우
import { MyFirstController } from './controllers/my-first.controller'
import { MySecondController } from './controllers/my-second.controller'

// index.ts를 사용하는 경우
import { MyFirstController, MySecondController } from './controllers';

  • 규칙 4. 타입스크립트에서는 인터페이스를 많이 사용한다. 인터페이스는 타입을 정의하는데 사용되고 구체적인 내용은 클래스를 만들고 인터페이스를 상속하는 방식으로 작성한다. 인터페이스 작명법으로 앞에 I를 붙이는 방법이 있다. 예를 들어 Series라는 타입을 정의할 때 ISeries처럼 작명한다. 이 방식은 보기에도 어색한 부분이 있다. 그래서 NestJS에서는 가능하면 Series 인터페이스를 만들고 그 하위 인터페이스 혹은 클래스를 만든다.
interface Series { }
interface BookSeries extends Series { }
class MovieSeries extends Series { }

참고

다음에 다루게 될 것

  • NestJS로 블로그 API 만들기

댓글남기기