📗 스터디#Web#nestJS
nestJS로 백엔드 구축하기
2024-12-03 | 🕒 읽는 데 0분 예상작성자
u
카테고리
📗 스터디
작성일
‣
태그
Web
nestJS
설명
상태
배포됨
최하위 정렬
최하위 정렬
forest_분류
forest_날짜
학교 수업에서 진행하는 팀 프로젝트에 백엔드 프레임워크로 nestJS를 사용해보기로 하였다. 원래는 익숙한 node.js와 Express의 조합으로 개발을 진행할 계획이었는데, 이번 기회에 새로운 프레임워크를 써보자라는 생각에 계획을 수정했다.
nestJS가 뭐죠?
NestJS는 효율적이고 확장 가능한 서버사이드 Node.js 프로그램을 구축하기 위한 플랫폼이다. 카밀 미슬리비에츠가 개발했다. Express.js 프레임워크를 기본으로 사용하며 Fasity와도 호환된다.
라고 위키백과에 나와있다. 이 설명에서 볼 수 있듯이 nestJS는 node.js와 Express 위에서 작동한다.
node.js와 Express의 조합은 Javascript 생태계의 서버 개발에서 많이 사용되고 있지만, 정해진 아키텍처가 없기에 협업이나 유지 보수 측면에서 단점이 있었다. nestJS는 이를 보완하기 위해 등장한 프레임워크로, 보다 체계적이고 모듈화된 구조를 제공한다. 또한 기본 언어로 Typescript를 채택하고 있어 강력한 타입 검사, 객체지향 등의 장점을 그대로 가져오고 있다. 요악하자면 nestJS는 Express의 확장판이다.
nestJS의 장점을 정리해보면 다음과 같다.
- 최신 기술 지원 - NestJS는 최신 ECMAScript 표준을 지원하여 최신 JavaScript 기능을 활용할 수 있다.
- 풍부한 내장 기능 - CORS(Cross-Origin Resource Sharing), HTTP 헤더 관리, 인터셉터, 미들웨어, 로깅, 테스팅, Swagger 문서화 등 다양한 기능을 자체적으로 제공한다.
- 데이터베이스 통합 - ORM(Object-Relational Mapping) 지원으로 데이터베이스 작업을 더욱 효율적으로 수행할 수 있다.
- 고급 기능 지원 - 스케줄링, 이벤트 핸들링, 마이크로서비스 아키텍처 등 고급 기능을 지원하여 복잡한 애플리케이션 개발에 적합하다.
- 아키텍처 유연성 - MVC, DDD 등 다양한 디자인 아키텍처를 유연하게 적용할 수 있어, 어느 정도의 유연성을 제공한다.
- TypeScript 사용 - TypeScript를 기본으로 사용하여 안정성과 유지보수성이 높다.
다만, node.js에 비해 인터넷 상에 자료가 상대적으로 적으며 정규화된 아키텍처나 Typescript 사용 등의 특징이 오히려 단순한 프로젝트에서는 개발 시간과 복잡성을 증가시킬 수도 있다.
설치 및 실행
위에서 언급했다시피 nestJS는 node.js를 기반으로 작동한다. 따라서 먼저 node.js를 필요한 버전으로 설치해주어야 한다. node.js 설치 과정은 본 글에서는 생략하겠다.
nodeJS를 설치하고 나면,
npm
을 통해 nestJS를 설치할 수 있다.$ npm i -g @nestjs/cli
설치가 완료되면 아래 커맨드를 입력해 보일러 플레이트 코드*가 포함된 프로젝트 폴더를 빠르게 생성할 수 있다.
*보일러 플레이트(Boilerplate code): 프로젝트를 생성할 때 꼭 필요한 기본 기능을 미리 준비해 놓은 것.
$ nest new project-name
이제 nestJS 서버를 구동시켜보자. 필자는 위의 프로젝트 생성 시에 패키지 매니저로 yarn을 선택했기 때문에 아래와 같이 실행 커맨드를 입력해주면 된다. npm의 경우에도 yarn 대신 npm으로 바꿔주기만 하면 된다.
$ yarn run start
그럼 아래와 같이 서버가 실행된다.
[Nest] 13383 - 2021-09-10 9:38:54 LOG [NestFactory] Starting Nest application... [Nest] 13383 - 2021-09-10 9:38:54 LOG [InstanceLoader] AppModule dependencies initialized +27ms [Nest] 13383 - 2021-09-10 9:38:54 LOG [RoutesResolver] AppController {/}: +6ms [Nest] 13383 - 2021-09-10 9:38:54 LOG [RouterExplorer] Mapped {/, GET} route +3ms [Nest] 13383 - 2021-09-10 9:38:54 LOG [NestApplication] Nest application successfully started +4ms
http://localhost:3000/
으로 GET
요청을 해보면, Hello World!
가 응답으로 반환되는 것을 확인할 수 있다.구조 살펴보기
서버가 제대로 작동하는 것을 확인했으니, 생성된 기본 프로젝트 구조를 살펴보자.
├── README.md ├── nest-cli.json ├── node_modules ├── package-lock.json ├── package.json ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ └── main.ts ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json ├── tsconfig.build.json └── tsconfig.json
main.ts
우선,
main.ts
파일은 NestJS 애플리케이션이 시작되는 진입 지점(entry point) 역할을 한다. 아래 코드를 보면 app.module
파일에서 AppModule을 불러와 NestFactory
객체를 생성하고, 3000번 포트로 요청을 받아오는 bootstrap()
함수가 구성되어 있다.import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();
Module
그럼
app.module
코드를 살펴보자. import { Module } from "@nestjs/common"; import { AppController } from "./app.controller"; import { AppService } from "./app.service"; @Module({ imports: [], controllers: [AppController], providers: [AppService], }) export class AppModule {}
AppModule
이라는 클래스 위에는 @Module
이라는 데코레이터(decorator)가 호출되고 있다. 데코레이터는 Java에서 어노테이션(Annotation)의 개념이라고 생각하면 된다. 클래스나 메서드에 어떤 정보를 추가해주는 역할을 수행한다.Module 데코레이터의 세 가지 속성을 살펴보자. 먼저
controllers
속성에는 컨트롤러 클래스, providers
속성에는 컨트롤러가 사용하는 다양한 일반 클래스(ex. 서비스 클래스)를 나열해줄 수 있다. imports
속성에는 해당 모듈이 의존하고 있는 다른 모듈을 나열해줄 수 있다. 모듈은 기능 단위로 쪼갠 단위라고 보면 되는데, 하나의 NestJS 애플리케이션은 보통 여러 개의 모듈로 구성된다.Controller
다음은 컨트롤러(Controller)이다.
컨트롤러는 HTTP 요청을 받아 처리하고 응답을 해주는 역할을 한다.
src/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()
데코레이터를 추가해주면 해당 클래스는 컨트롤러로 인식된다. 클래스 안에 선언된 각 메서드 위에는 @Get()
, @Post()
, @Put()
, @Delete()
등 HTTP Method를 지정해주면 된다.데코레이터의 괄호 안에는 URL 경로가 들어가게 된다. 예를 들어,
@Controller("a")
안의 @Get("b")
메서드가 존재한다면, http://localhost:3000/a/b
로 POST 요청을 날릴 수 있는 것이다.Service
마지막으로 서비스(Service) 클래스는 실제 로직을 처리하는 역할을 한다.
src/app.services.ts
파일을 보면 다음과 같다.import { Injectable } from "@nestjs/common"; @Injectable() export class AppService { getHello(): string { return "Hello World!"; } }
위와 같이 getHello라는 서비스 메서드가 구현되어 있는데, 위에서 살펴본 컨트롤러 코드에서 확인할 수 있듯이,
this.appService.getHello()
처럼 호출할 수 있다.여기에서는
@Injectable()
데코레이터가 붙어있는 것을 볼 수 있다. @Injectable()
데코레이터는 해당 클래스에 의존성 주입(Dependency Injection, DI)를 사용할 수 있게 해준다. 여기서 의존성 주입은 객체가 다른 객체의 인스턴스에 의존할 때, 직접 해당 인스턴스를 생성하지 않고 외부에서 주입하는 설계 패턴을 말한다.생성된 보일러 플레이트 코드로 이해해보자.
Service 클래스의
getHello()
메서드는 @Injectable()
데코레이터가 붙어있으므로 의존성 주입을 사용할 수 있다. 즉, 이제 AppService
를 다른 클래스에 주입할 수 있는 것이다. 따라서 아래와 같이 Controller 클래스에서 생성자(constructor)를 통해 AppService
를 주입받을 수 있는 것이다.constructor(private readonly appService: AppService) {}
Entity와 DTO
NestJS에서는 엔티티(entity) 클래스를 통해서 REST API에서 관리하는 데이터를 모델링할 수 있다. Entity는 실제 DB의 Relation과 1:1로 매핑되는 클래스이다. 관계형 데이터베이스를 예로 들면, entity 클래스의 인스턴스는 column들의 집합(=하나의 레코드)가 된다. DB에 User 테이블이 있다고 가정하면, 아래와 같이 Entity 클래스를 작성할 수 있을 것이다.
export class User { id: number; name: string; email: string; phone?: string; createdAt: Date; updatedAt?: Date; }
DTO(Data Transfer Object, 데이터 전송 객체)란 프로세스 간에 데이터를 전달하는 객체를 말한다. Entity를 Controller 같은 클라이언트(View)단과 정보를 교환하는 계층에 직접 전달하는 대신 DTO를 사용하는 것이다. 쉽게 말하면, 외부(클라이언트)로부터 들어오는 데이터를 모델링하는 역할을 수행한다.
DTO는 특별한 로직을 가지지 않는 데이터 객체여야 한다. 위의 User Entity에 이어서 예시를 작성해보면 다음과 같이 작성할 수 있다. 외부로부터 name, email, phone 데이터를 받겠다는 의미이다.
export class CreateUserDto { name: string; email: string; phone?: string; }