2023. 7. 16. 23:30ㆍ개발 공부/NestJS
✍🏻 들어가기 전
-----------------------------------------------------------------------------------------------
해당 글은 NestJS 초보자가 study를 하기 위해 틈틈이 작성 중인 게시글입니다.
부족한 점이 많기 때문에 NestJS 고수 분들께서 피드백 주시면 너무 감사드립니다 :)
✅ NestJS
NestJS를 학습하게 된 계기는
회사 내 담당하고 있던 프로젝트의 프레임워크인 ExpressJS를 조금 더 나은 구조로 개선할 수 있을까라는 고민에서 시작되었다.
현재 약 하루 500명 정도의 방문자가 있고, 앞으로 회사 측에서 더 투자할 프로젝트이기 때문에
대량의 트래픽이 발생하기 전에 미리 안정적인 서버를 구축해놓고 싶은 생각이 문득 들었다.
지금의 프레임워크는 node.js 프레임워크 중 ExpressJS를 사용하고 있는데, ExpressJS도 물론 충분히 좋고 많은 곳에서 사용하고 있는 프레임워크지만, 엄격한 규칙이나 특별한 디자인 패턴이 없다는 것이 나 같은 주니어 개발자에게는 더 두려운 프레임워크 같다.
만약에 NestJS가 당장 도입하기 힘든 안타까운 상황이 온다면, TypeScript라도 적용하고 싶다 ㅜㅜ..
모든 데이터가 interface가 없고, 하물며 Response에 어떤 데이터가 넘어오는지 인터페이스조차 없는 상황이다.
타입스크립트를 적용하면 타입 체크는 당연하고, 다른 개발자가 개발해 놓은 코드 히스토리를 타는 등의 불필요한 시간들이 훨씬 줄어들 것 같다..
처음엔 유연한 게 좋았는데, 점점 연차가 쌓이니 자유로운 게 마냥 좋은 게 아니더라....😅
1. 아키텍처
NestJS는 NodeJS 프레임워크 중 하나로, ExpressJS (Fastify까지 포함하여) 위에 Typescript까지 겸비한 모듈화 된 아키텍처이다.
이전 ExpressJS나 Fastify와 달리 프레임워크 생성 시 이미 아키텍처가 잡혀있다는 점에서 차별화된다.
프레임워크 자체 내에 규칙이 정해져 있고, 디자인 패턴 또한 존재한다.
즉, 개발자는 프레임워크에서 세팅한 구조대로 개발을 진행해야 한다.
코드 샘플을 보면 아마 이해에 도움이 될 것 같다.
NestJs 프로젝트 구조는 아래와 같다.
main.ts
NestJS가 실제로 애플리케이션을 생성하는 메인 파일이라고 볼 수 있다.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
app.module.ts
📍Nest-Module은 애플리케이션 구조를 구성하는 데 사용되고, 개발자가 모듈의 종속성을 효율적으로 관리할 수 있게 해 준다.
NestJS는 3가지의 계층으로 분리할 수 있다.
- 컨트롤러 계층: 서비스의 계층을 호출
- 서비스 계층: 모든 비즈니스 로직 담당
- 데이터 저장소 계층: 데이터의 액세스를 담당
-> 이렇게 할 경우, 가장 좋은 점은 소프트웨어의 재사용성과 유지관리가 가능해진다.
NestJS는 이렇게 애플리케이션을 쉽게 테스트할 수 있도록 해주고, 의존성 주입이라는 디자인 패턴 또한 사용할 수 있다.
(제어의 역전의 한 형태인 DI는 객체들의 조립형태로 만들어지기 때문에 느슨한 결합으로 효율적으로 개발할 수 있는 디자인패턴이다.)
import { Module } from '@nestjs/common';
import { MoviesController } from './movie/movie.controller';
import { MoviesService } from './movie/movie.service';
@Module({
imports: [],
controllers: [MoviesController],
providers: [MoviesService],
})
export class AppModule {}
그럼 NestJS는 app.module.ts파일에 등록되어 있는 모듈들을 모아 아래와 같이 NestFactory라는 내장 함수를 통하여 애플리케이션을 생성한다.
애플리케이션을 생성할 때 모듈을 app.module.ts에 등록을 해줘야 NestJs는 이를 감지하여 생성한다.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
app.controller.ts
Controller는 클라이언트의 요청이 들어오는 파일이다.
생성자를 통해 service를 주입하여 사용하고, 다양한 데코레이터를 통해 사용 가능하다.
Controller에서 Service접근 시 수동으로 import 하지 않아도 해당 컨트롤러의 생성자에 서비스를 주입시켜 사용할 수 있다.
express.js에서는 수동으로 import 했다면, NestJs에 요청을 해서 import 한다고 생각하면 된다.
- readOnly(불변성 유지): 해당 컨트롤러에서 서비스 수정하게 될 가능성이 있기 때문에 해당 클래스 내부의 변수들을 바꾸지 못하게 하는 것
import {
Controller,
Get,
Param,
Post,
Delete,
Patch,
Body,
Query,
} from '@nestjs/common';
import { MoviesService } from './movies.service';
import { Movie } from './entities/movie.entity';
import { CreateMovieDto } from './dto/create-movie.dto';
@Controller('movies')
export class MoviesController {
constructor(private readonly moviesService: MoviesService) {}
@Get()
getAll(): Movie[] {
return this.moviesService.getAll();
}
}
app.service.ts
실제로 비즈니스 로직을 구현하는 곳이다.
controller에 주입시키기 위해서는 모듈의 상단에 Injectable()이라는 데코레이터를 명시하여 의존성 주입을 할 수 있다.
import { Injectable, NotFoundException } from '@nestjs/common';
import { Movie } from './entities/movie.entity';
import { CreateMovieDto } from './dto/create-movie.dto';
@Injectable()
export class MoviesService {
private movies: Movie[] = [];
getAll(): Movie[] {
return this.movies;
}
}
일단 NestJS의 기본 구조를 알아봤다.
사실 지금까지는 정말 기본 구조인 것 같고, 내가 궁금했던 그래서 Express보다 좋은 게 뭔데?라는 부분은 아직 해소되지 않았다.
Express에서는 미들웨어 내에서 필요한 부분들은 모두 일일이 세팅해줘야 했다.
그리고 기본으로 제공해 주는 라이브러리가 없기 때문에 개발자가 손수 설치하고 개발해야 하는 부분들이 대부분이었는데,
이런 부족한 부분들도 채워줄 정도로 좋은가?라는 의문이 들었고, 내가 제일 학습하고 싶던 부분이었다.
다음 포스팅에서는 NestJS의 정말 핵심 구조들을 살펴볼 예정이다.
- middleware, Guards, Interceptors 등을 학습하고, 얼마나 좋은지 한번 체감하고 싶다!