The Basics of Nest

onyoo·2023년 1월 2일
0

NestJs

목록 보기
1/8
post-thumbnail

프로젝트 깃허브 참고

시작하기에 앞서..

Nest 애플리케이션을 만들 때 마다 Nest Cli 도구를 사용하려고합니다.

이것은 터미널에서 새 프로젝트를 생성하고 실행하는데 사용되는 도구로 이러한 작업을 할 때 이와 같은 cli 도구를 사용 할 예정이지만,

처음으로 작성하는 프로젝트의 경우 직접 필요한 모든 코드를 작성할 예정입니다.

이렇게 하는데엔 이유가 있는데 첫번째 프로젝트는 조금 어려울 예정이고. 우리는 네스트가 내부적으로 어떻게 작동하는지 이해하기 위함입니다.

Nest 의 BTS 측면에 초점을 맞출 것 !

Project Setup & Project Configuration

우리가 사용할 모듈을 먼저 깔아보자

npm install @nestjs/common@7.6.17 @nestjs/core@7.6.17 @nestjs/p
latform-express@7.6.17 reflect-metadata@0.1.13 typescript@4.3.2

모듈이 설치되고 우리는 package.json 파일에서 어떤 모듈이 설치되어있는지 확인할 수 있고. 이것들이 프로젝트를 위해 어떤동작을 하는지 이해할 수도있다.

package.json

{
  "name": "scrtch",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@nestjs/common": "^7.6.17",
    "@nestjs/core": "^7.6.17",
    "@nestjs/platform-express": "^7.6.17",
    "reflect-metadata": "^0.1.13",
    "typescript": "^4.3.2"
  }
}

우리가 살펴볼 종속성은 네가지로 다음과 같다

첫번째로 @nestjs/common

Nest 라이브러리 자체는 여러 패키지로 나누어지지만, 애플리케이션을 구축하는데에 사용할 함수,클래스 및 기타 항목의 대부분은 이 공통 패키지에서 제공된다. 따라서 이 전체과정에서 해당 패키지를 자주 사용할 예정이다.

두번째로 @nestjs/platform-express

Nest 자체는 수신요청을 내부적으로 처리하지 않는다. 대신에 Nest는 요청을 처리하기 위해선 외부 구현에 의존해야하는데, 그때 우리는 HTTP 구현을 코드 내부에 배치해야한다.

즉, 수신 요청을 처리해야할 서버를 제공해야한다는 것이다.

Nest에서 HTTP 서버를 구현하기 위해서는 두가지 옵션이 존재하는데

Express

Fastify

이 두가지 이다. 이 두가지 HTTP 서버 구현 중 하나를 선택하고 선택하지 않는 것은 삭제해야 사용할 수 있다. 우리의 기본값은 express로 프로젝트를 생성할 때 해당 옵션이 올바른지 확인하고 시작해야한다.

번외로, Express 와 Nest 사이의 어댑터를 사용하면 두개가 함께 작동할 수 있다 그러면 Nest는 뒤에서 들어오는 모든 HTTP 요청을 처리하기 위해 익스프레스를 사용할 것이다.

세번째로 reflect-metadata 가 있다.

데코레이터에 익숙하지 않을때 도와주는 확장이다. 사용하는 데코레이터와 연결된 라이브러리이다.

네번째는 typescript 이다.

타입스크립트 컴파일러이다 !

우리는 지금까지 종속성에 대해 설치를 하고 알아보았다. 그 다음으로 할 일은 타입스크립트 컴파일러 구성 파일을 설정하는 것이다.

여기에는 타입스크립트에 컴파일 방법을 알려주기 위해 내부에 몇가지 설정을 작성할 예정이다.

만약 우리가 nest cli를 사용하여 프로젝트를 만들 경우 1번과 2번 과정을 따로 수행할 필요는 없다 !

tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2017",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}

특히 여기서 이 두가지 특징은 Nest 애플리케이션을 작성하는데에 있어서 핵심적인 역할을 하고 있다.

"experimentalDecorators": true,
"emitDecoratorMetadata": true

Creating Controller

서버를 구성하는데에는 프레임워크,언어와 상관없이 다음과 같은 일련의 과정을 거친다.

  1. 들어온 데이터의 유효성 검증
  2. 요청을 통해 사용자가 요청을 전송할 수 있는 인증 혹은 권한이 있는지 확인할 수 있다
  3. 해당 요청을 특정 기능으로 라우팅하여 다음과 같은 요청을 처리한다
  4. 특정한 비즈니스 로직을 실행하고, 결과적으로는 어떤 정보에 접근하거나 저장한다.

이러한 전체 과정의 결과로 클라이언트와 서버가 응답을 주고받는다!

이러한 일련의 과정을 해결하는데에 있어 Nest에서는 특별한 도구를 제공해준다.

첫번째 파이프의 경우 들어오는 요청에 대한 유효성을 검사한다.

두번째 가드는 들어오는 요청이 인증되거나 권한이 있는 사용자로부터 오는지 확인한다.

세번째 컨트롤러에는 라우팅 논리가 포함되어있다.

네번째 서비스에는 프로그램이 돌아가게 하기 위한 비즈니스 로직이 담겨있다.

다섯번째 리포지토리는 데이터베이스에 접근하기 위한 코드들이 담겨있다.

Nest에 포함된 이러한 Tool 들은 조각을 모으듯이 여러개로 나누어진 것을 하나로 모으기 위해 돕는 역할을 한다.

위에서 언급되었던 다섯가지의 도구 외에도 다른 도구가 몇가지 있다.

이러한 도구들을 이용하여 우리에게 들어오는 요청을 처리할 수 있도록 도와준다.

가장 간단한 Nest 프로그램을 구성하기 위해서 필요한 두가지는 바로 모듈과 컨트롤러이다. 지금까지 생성한 모든 중첩 애플리케이션의 내부에는 적어도 하나의 모듈과 하나의 컨트롤러가 있다. 즉 가장 간단한 기본앱을 만드는데에 있어서 필요한 두가지라는 소리이다.

이제 코드를 본격적으로 작성해보자.

우리가 모듈을 만들기 위해서는 일단, 앱 컨트롤러를 먼저 만들어야 한다. 그 다음 앱 컨트롤러는 모듈에 등록해보겠습니다.

@Controller() // 애플리케이션 내부에서 컨트롤러 역할을 할 클래스를 생성
class AppController {
    //한 종류의 들어오는 요청을 처리함
    //내부에 또 다른 경로를 원할 때 마다 추가 방법을 추가 할 예정
    @Get()
    getRootRoute() {
        return 'hi there!';
    } //누가 요청을 할 때마다 해당 요청을 이 메서드로 라우팅 할 때 사용
}

이런식으로 작성한 컨트롤러는 어떤 방식으로 들어오는 요청을 처리할 것인지 데코레이터로 알려준 후 그에 알맞는 요청 처리 로직을 처리합니다.

이 앱컨트롤러는 모듈에 추가해보겠습니다. 애플리케이션이 시작될 때 마다 이 앱모듈을 모두가 바라볼 것인데요, 한번 보면.

@Module({
    controllers: [AppController], // 컨트롤러 등록
})
class AppModule {}

여기에 등록된 컨트롤러를 보고 Nest가 해당 컨트롤러의 인스턴스를 생성한다. 더불어 컨트롤러 내부에서 사용한 다른 데코레이터를 살펴보고 루트를 정한다.

다음으로는 애플리케이션을 시작할때마다 실행할 함수를 추가하는 것이다.

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    await app.listen(3000);
    //app instatnce
    //특정포트에서 들어오는 트래픽을 수신하게끔 지시하기
}

bootstrap();
//애플리케이션의 인스턴트를 생성한 다음 들어오는 트래픽을 수신하도록 지시하는 것

애플리케이션을 시작할때마다 실행할 함수를 일반적으로 bootstrap이라고 한다.

이러한식으로 factory생성해서 AppModule을 호출해준다. 그것을 우리는 3000번 포트에서 수신하겠다고 설정해준 것이다.

//하나의 파일안에 모든 로직을 다 담을 예정
import { Controller, Module, Get } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';

@Controller() // 애플리케이션 내부에서 컨트롤러 역할을 할 클래스를 생성
class AppController {
    //한 종류의 들어오는 요청을 처리함
    //내부에 또 다른 경로를 원할 때 마다 추가 방법을 추가 할 예정
    @Get()
    getRootRoute() {
        return 'hi there!';
    } //누가 요청을 할 때마다 해당 요청을 이 메서드로 라우팅 할 때 사용
}

@Module({
    controllers: [AppController],
})
class AppModule {}

async function bootstrap() {
    const app = await NestFactory.create(AppModule);

    await app.listen(3000);
    //app instatnce
    //특정포트에서 들어오는 트래픽을 수신하게끔 지시하기
}

bootstrap();
//애플리케이션의 인스턴트를 생성한 다음 들어오는 트래픽을 수신하도록 지시하는 것

전체 코드를 보면 우리가 불러온 NestFactory가 불러온 라이브러리는 nestjs/core로 기존 common 이랑 다르다.

95퍼센트 정도의 임포트는 nestjs/common 에서 가져올 것이고, 몇안되는 주요 파일을 가져올 수 있는 것이 바로 nestjs/core 보통 어디서 import 해야할지 헷갈릴 경우 common에서 가져오면 된다.

지금까지 우리는 하나의 파일 내에서 컨트롤러와 module을 작성하였다.

만약, 여기에서 우리가 확장 공사를 한다고 가정하면 이러한 형식으로 코드를 작성하는 것은 비효율적일것이다.

하나의 파일에 모든 것이 다 들어있다니. 유지보수가 정말 어려울 것이다.

그래서 우리는 controller 와 module을 파일로 나눌 것이다.

nest에서 사용하는 네이밍 컨벤션은 다음과 같다.

app.controller.ts

app.module.ts

우리는 이 두가지 파일로 하나에 담겨있던 내용들을 옮겨 볼 것이다.

app.controller.ts

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

@Controller() // 애플리케이션 내부에서 컨트롤러 역할을 할 클래스를 생성
export class AppController {
    //한 종류의 들어오는 요청을 처리함
    //내부에 또 다른 경로를 원할 때 마다 추가 방법을 추가 할 예정
    @Get()
    getRootRoute() {
        return 'hi there!';
    } //누가 요청을 할 때마다 해당 요청을 이 메서드로 라우팅 할 때 사용
}

app.module.ts

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

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

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);
    //app instatnce
    //특정포트에서 들어오는 트래픽을 수신하게끔 지시하기
}

bootstrap();
//애플리케이션의 인스턴트를 생성한 다음 들어오는 트래픽을 수신하도록 지시하는 것

앞서 main.ts 에 담겨있던 불필요한 import 문을 정리하고 AppModule만 import 하여 코드를 정리하였다.

다음으로는 컨트롤러와 데코레이터에 대한 내용을 살펴보자.

우리가 설정했던 @Controller 데코레이터와 @Get 데코레이터를 이용하여 라우팅을 할 수 있다.

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

@Controller('/app') // 애플리케이션 내부에서 컨트롤러 역할을 할 클래스를 생성
export class AppController {
    //한 종류의 들어오는 요청을 처리함
    //내부에 또 다른 경로를 원할 때 마다 추가 방법을 추가 할 예정
    @Get('/hello_world')
    getRootRoute() {
        return 'hi there!';
    } //누가 요청을 할 때마다 해당 요청을 이 메서드로 라우팅 할 때 사용

    @Get('/bye')
    getByeThere() {
        return 'bye there';
    }
}

Controller 에 /app 은 상위 라우팅 경로이다. Get 에 설정해놓은 /hello_world/bye 의 경우 하위 라우팅 경로이다.

즉, 연결하려면 /app/hello_world app/bye 로 연결해야한다는 뜻이다.

참조

해당 내용은 Udemy 강좌
https://www.udemy.com/course/nestjs-the-complete-developers-guide/
를 수강하며 작성한 내용입니다 :)

profile
반갑습니다 ! 백엔드 개발 공부를 하고있습니다.

0개의 댓글