Notice
Recent Posts
Recent Comments
관리 메뉴

즐겁게, 코드

Nest.js 시작하기 : 설치 & 아키텍처 소개 본문

💻 백엔드/Nest.js

Nest.js 시작하기 : 설치 & 아키텍처 소개

Chamming2 2021. 10. 29. 23:35

Nest.jsExpress.js와 유사한 백엔드 프레임워크인데요, 아마 Express.js를 사용해본 경험이 있다면 한번쯤은 이런 고민을 해본적이 있을 것입니다.

npm i express, 프로젝트 준비 끝!
그런데 이젠 뭘 해야하지?

Express는 분명 편리하고 강력한 백엔드 프레임워크지만, 미들웨어와 라우팅 등의 기능을 자유도가 매우 높은 메서드로만 구현했기 때문에 프로젝트의 구조화 가 매우 어렵다는 단점이 있었습니다.

 

Nest는 이런 문제를 해결하기 위해 타입스크립트를 기본으로 사용하고 모듈 단위를 통해 백엔드 코드를 보다 구조적으로 작성할 수 있게 해주는 프레임워크인데요, 오늘은 Nest의 소개와 함께 간단한 API 서버를 만들어보도록 하겠습니다.

Nest 설치하기

Nest는 자체적인 cli 도구를 사용해 새로운 프로젝트를 생성할 수 있습니다.

// 1. nest cli 설치
npm i -g @nestjs/cli

// 2. nest 프로젝트 생성
nest new [프로젝트명]

프로젝트를 처음 설치하면 다음과 같은 폴더 구조를 확인할 수 있는데요, 한번 Nest 프로젝트의 구조를 간단히 소개한 후, 각 부분에서 어떤 역할을 수행하는지 알아보도록 하겠습니다.

  • main.ts : 프로젝트 엔트리 파일입니다. 몇 번 포트에서 서버를 개방할지, 어떤 모듈이나 미들웨어를 사용할지 정의합니다.
  • [모듈명].module.ts : api 모듈을 관리하는 파일입니다.
  • [모듈명].controller.ts : api 엔드포인트를 관리하는 파일입니다.
  • [모듈명].service.ts : api 비즈니스 로직을 관리하는 파일입니다.

Nest 프로젝트의 핵심은 바로 이 모듈 - 컨트롤러 - 서비스 구조입니다.

위에서 언급했던 Express 의 구조화 문제를 해결하기 위해 Nest에서는 이런 구조를 갖게 된 것인데요, 이제부터 각 부분들을 조금 자세히 소개해보도록 하겠습니다.

Tip) 앞으로의 글에서는 nest generate module, nest generate controller, nest generate service 처럼 각각의 요소별로 생성하는 커맨드를 소개하고 있지만, nest generate resource 커맨드로 기본적인 CRUD를 구현한 API 모듈을 쉽게 생성할 수 있습니다.

모듈 (Module)

한 백엔드 프로젝트에는 여러 API가 필요할 수 있습니다.

예전 프로젝트를 예로 들면 학교 정보를 보여주기 위해 "오늘의 명언", "오늘의 날씨", "교내 공지사항" 등의 정보를 서버에서 관리해야 했는데요, Nest에서는 각각의 정보들을 모듈로 관리합니다.

모듈 생성하기

한번 날씨 데이터를 관리할 weather 라는 모듈을 만들어 보겠습니다.

// nest cli를 사용해 모듈을 생성합니다.
nest generate module [모듈명]
import { Module } from '@nestjs/common';

@Module({})
export class WeatherModule {}

첫 번째 모듈이 생성된 모습입니다.

모듈은 뒤에서 설명할 컨트롤러와 서비스를 import해 하나의 완성된 api로 감싸는 역할을 하는데요, 위에서 언급한 "명언", "날씨", "공지사항" 모듈을 구조화하면 아래와 같은 그림이 됩니다.

이제 바로 컨트롤러 소개로 넘어가 보겠습니다.

컨트롤러 (Controller)

컨트롤러는 API의 엔드포인트를 관리하는 부분입니다.

API에는 get, post, putHTTP 메서드로 이루어진 요청을 보낼 수 있는데요, 컨트롤러에서는 어떤 요청이 들어왔을 때 어떤 함수를 실행할지를 정의합니다.

컨트롤러 생성하기

// nest cli를 사용해 컨트롤러를 생성합니다.
// 이때, 생성해둔 모듈과 같은 이름을 가져야 합니다.
nest generate controller [모듈명]
import { Controller } from '@nestjs/common';

@Controller('weather')
export class WeatherController {
}

HTTP 메서드 데코레이터

컨트롤러에는 @Get(), @Post() 등의 데코레이터를 활용해 HTTP 메서드로 구분되는 요청의 분기를 지정할 수 있습니다.

import { Controller, Get, Put } from '@nestjs/common';

@Controller('weather')
export class WeatherController {
    // HTTP 메서드를 데코레이터로 사용합니다.
  @Get()
  getWeather(): string {
    return "today's weather info";
  }

  @Put()
  UpdateWeather(): string {
    return 'weather was updated!';
  }
}

이제 /weather 경로로 get 요청을 보내보면, 컨트롤러에서 정의한 getWeather 함수가 실행되고 그 결과가 화면에 나타나는 것을 확인할 수 있습니다.

@Param 데코레이터

하지만 언제나 /weather 경로만을 사용하지는 않을 것입니다.

예를 들어 오늘 날씨를 얻기 위해 /weather/today/weather/search?day=today 처럼 파라미터나 쿼리스트링이 필요하고, 이를 함수에 전달해야 한다면 추가적인 데코레이터를 활용해야 합니다.

먼저 경로의 파라미터를 함수에 전달하기 위한 @Param 데코레이터입니다.

import { Controller, Get, Param } from '@nestjs/common';

@Controller('weather')
export class WeatherController {
    // 1. HTTP 메서드 데코레이터에서 활용할 파라미터를 포함한 경로를 정의합니다.
  @Get('/:day')
    // 2. 해당 엔드포인트가 호출할 함수의 인자로 경로의 파라미터를 전달합니다.
  getWeather(@Param('day') day: string): string {
    // 3. 이제 엔드포인트의 파라미터를 받아와 비즈니스 로직에 사용할 수 있습니다.
    switch (day) {
      case 'today':
        return "today's weather info";
      case 'tomorrow':
        return "tomorrow's weather info";
      default:
        return 'There was an error';
    }
  }
}

여기서 중요한 부분이 하나 있는데요, 바로 인자의 경로로 주어지는 파라미터의 키와 파라미터 데코레이터의 인자는 반드시 동일해야 한다는 것입니다!

// 1. @Get() 데코레이터에 들어가는 파라미터(day)와
@Get('/:day')
// 2. @Param() 데코레이터에 들어가는 인자는 반드시 동일해야 합니다!
  getWeather(@Param('day') day: string): string {
        ...
    }

@Param 처럼 쿼리를 다루는 @Query나, 요청 본문의 데이터를 의미하는 @Body 데코레이터를 활용할 수도 있습니다.

다만 이는 컨트롤러를 더 자세히 다루는 글에서 소개하도록 하겠습니다.

서비스 (Service)

서비스는 컨트롤러에서 실행할 함수의 비즈니스 로직을 정의하는 부분입니다.

이전 예시에서는 컨트롤러에 직접 실행할 함수를 정의했지만, 실제로 Nest 어플리케이션을 구축할 때는 컨트롤러에서는 엔드포인트만을 정의하고 해당 엔드포인트로 들어오는 요청에 대한 모든 비즈니스 로직은 서비스에서 작성합니다.

서비스 생성하기

// nest cli를 사용해 서비스를 생성합니다.
// 이때, 생성해둔 모듈과 같은 이름을 가져야 합니다.
nest generate service [모듈명]
import { Injectable } from '@nestjs/common';

@Injectable()
export class WeatherService {}

서비스를 사용할 때는 먼저 컨트롤러에 해당 서비스를 등록해야 합니다.

import { Controller, Get, Param } from '@nestjs/common';
import { WeatherService } from './weather.service';

@Controller('weather')
export class WeatherController {
    // 1. 클래스 생성자의 인자로 추상화된 서비스를 전달합니다.
  constructor(private readonly weatherService: WeatherService) {}

  @Get('/:day')
  getWeather(@Param('day') day: string): string {
        // 2. @Get() 요청이 들어오면 호출할 함수를 정의합니다.
        /*
           3. 이제 비즈니스 로직은 서비스로 옮기도록 하겠습니다.
        switch (day) {
        case 'today':
          return "today's weather info";
        case 'tomorrow':
          return "tomorrow's weather info";
        default:
          return 'There was an error';
        */
    }
  }
}

컨트롤러에 있던 비즈니스 로직을 서비스로 옮기도록 하겠습니다.

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

@Injectable()
export class WeatherService {
    // 1. 컨트롤러에 있던 비즈니스 로직입니다.
  getWeather(day: string): string {
    switch (day) {
      case 'today':
        return "today's weather info";
      case 'tomorrow':
        return "tomorrow's weather info";
      default:
        return 'There was an error';
    }
  }
}

그 결과 이전과 동일하게 동작하는 것을 알 수 있습니다.

이제 첫 번째 Nest 어플리케이션이 구성되었는데요, Nest의 핵심은 바로 Express가 갖던 자유도에 모듈 / 컨트롤러 / 서비스 라는 구조를 통해 보다 견고한 어플리케이션을 구축하는 데에 있습니다.

다음 글부터는 모듈 / 컨트롤러 / 서비스의 사용 방법을 보다 구체적으로 알아보도록 하겠습니다.

반응형

'💻 백엔드 > Nest.js' 카테고리의 다른 글

컨트롤러의 데코레이터  (0) 2021.11.10
Comments
소소한 팁 : 광고를 눌러주시면, 제가 뮤지컬을 마음껏 보러다닐 수 있어요!
와!! 바로 눌러야겠네요! 😆