NestJS과 Express의 개념 & 비교 (차이점, 특징 등)
● NestJS란?
Nestjs의 공식 사이트 Nest (NestJS)는 효율적이고 확장 가능한 Node.js서버측 애플리케이션을 구축하기 위한 프레임워크입니다. 프로그레시브 자바스크립트를 사용하고 TypeScript로 빌드되고 완벽하게 지원하며(하지만 여전히 개발자가 순수 자바스크립트로 코딩할 수 있음), OOP (객체 지향 프로그래밍 Object Oriented Programming), FP (함수형 프로그래밍 Functional Programming) 및 FRP (함수형 반응형 프로그래밍 Functional Reactive Programming) 요소를 결합합니다. express를 기본으로 채택하고 그 위에 여러 기능을 미리 구현해놓은 것이 nestjs입니다. |
※ NestJS 특징
- NestJS를 이용하면 확장 가능하며 유지 관리가 쉬운 서버 애플리케이션을 쉽게 개발할 수 있습니다.
- TypeScript 및 OOP (객체 지향 프로그래밍), FP (기능 프로그래밍), FRP (기능 반응성 프로그래밍) 요소를 결합합니다. (효율성 증가)
- Nestjs는 typescript를 적극적으로 도입함으로서 서버 어플리케이션 개발 시 발생할 수 있는 오류들을 사전에 방지할 수 있도록 했습니다. 또한 모듈로 감싸는 형태로 개발하기 때문에 모듈 별로 테스트 코드를 쉽게 작성할 수 있도록 구현되어 있습니다. (안정적)
- Nestjs는 module을 통해 확장이 용이하도록 설계되어 있습니다. 실제로 사용해보면 module을 통해 코드적으로, 논리적으로 구분한다는 장점을 크게 느끼실 수 있습니다. 또한 nestjs는 기본적으로 마이크로서비스 아키텍처 개발 스타일을 제공합니다. (참고문서 바로가기)
- Nest는 typescript를 사용하여 DI(Dependency Injection), IoC(Inversion of Control), 모듈을 통한 구조화 등의 기술을 통해 생산성이 높습니다.
- spring과 사용 경험이 유사하고 spring보다 간단합니다.
- 간편하게 Validation로직을 작성할 수 있습니다. (파이프 pip 사용)
NestJS프로젝트가 생성된 후 각각의 디렉토리를 살펴보면 아래와 같습니다.
이외에 각각 Cli로 생성할 수 있습니다. (nest global help를 통해 더 다양하게 확인 가능합니다.)
$ nest g mo users
$ nest g co users
$ nest g s users
├── src
├── app.controller.ts
├── app.service.ts
├── app.module.ts
├── main.ts
● Express란?
Express 공식 사이트 웹 및 모바일 애플리케이션을 위한 일련의 강력한 기능을 제공하는 간결하고 유연한 Node.js 웹 애플리케이션 프레임워크입니다. 사실상 Nodejs의 표준 웹서버 프레임워크로 불려질 만큼 많은 곳에서 사용하고 있습니다. Node.js는 Chrome의 V8엔진을 이용하여 javascript로 브라우저가 아니라 서버를 구축하고, 서버에서 JavaScript가 작동되도록 해주는 런타임 환경(플랫폼)입니다. Express는 이런 Nodejs의 원칙과 방법을 이용하여 웹애플리케이션을 만들기 위한 프레임워크입니다. |
※ Express 특징
- express는 전 세계 nodejs 프레임워크로 1위를 지키고 있으며 수많은 개발자들에게 개발 규칙을 강제하여 코드 및 구조의 통일성을 향상시킬 수 있습니다.
- 가장 많이 보편적으로 사용되기 때문에 구글링을 통해 충분한 레퍼런스를 검색할 수 있습니다.
- typescript를 express에서 사용할 수 있지만, tsconfig.json, lint.json 등등의 json파일을 만들고 세팅하는 과정이 복잡합니다.
- 웹서버를 빠르게 구현하기 위해 개발시에 구조에 대한 자유도가 높습니다.
NestJS와 Express와의 큰 차이점
예시 1)
NestJS를 사용하지 않고 순수 express를 사용하는 프로젝트에 여러명의 팀원들이 같이 개발을 하고 있을 때 한 팀원 A가 B에게 이렇게 말을 합니다.
A: “B, 이 코드는 언제 호출하는 코드에요?”
B: “그 코드는 제가 개발하지 않아서 C한테 물어보셔야 해요”
A는 다시 C에게 가서 이렇게 말해요.
B: “C, 이 코드는 언제 호출하는 코드에요?”
C: “아 그 코드는 사용자를 인증할 때 호출되는 코드에요. 저는 이런 코드는 미들웨어에 넣고 비즈니스 로직 관련 코드는 클래스를 따로 정의하고 컨트룰러에서 그걸 호출하고…”
A가 B에게 물어봤지만 B도 다른 사람이 작성한 코드가 쉽게 눈에 들어오지 않습니다.
그리고 C의 아키텍처 디자인 스타일을 한참을 설명 받고 나서야 C가 개발한 코드가 이해가 됩니다.
점점 기능개발이 되고 프로젝트가 커지면서 이런 현상이 더 많이 발생하게 됩니다.
사람마다 아키텍처 디자인 스타일이 다르거나 팀마다 스타일이 다르면 이처럼 커뮤니케이션 하는데 비용이 증가합니다.
NestJS는 이런 아키텍처의 정의도 프레임워크에서 제공하기 때문에 각 개발자들의 아키텍처가 통일(장점이자, 단점이 될 수 있음)되고 개발자들이 서로가 작성한 코드의 구조를 쉽게 파악할 수 있습니다.
예시 2)
라우팅의 차이
// Express
app.use('/user', require('./userRouter'))
app.use('/board', require('./boardRouter'))
// NestJS
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
//...
@Module({
imports: [
TypeOrmModule.forFeature([UsersRepository], DB_CONNECTION),
S3ImageModule,
],
providers: [UsersService, HttpService, ImageFileService],
controllers: [UsersController],
})
export class UsersModule {}
Express는 라우팅 할 때 app.use 처럼 등록해서 사용합니다.
하지만, Nestjs는 모듈별로 나누어서 라우팅을 하게 됩니다.
컨트롤러의 차이
// Express
router.route('/')
.get((req,res) => {
})
// NestJS
import { Controller, Get, Post, Delete, Patch } from "@nestjs/common";
//...
@Controller("users")
export class UsersController {
@ApiTags("function api") // swagger
@ApiOperation({ summary: "상세 조회" }) // swagger
@UseGuards(AuthGuard)
@Get()
getAll() {
return "users";
}
@Get("/:id")
getOne(@Param("id") users: string) {
return `one user id: ${users}`;
}
@Post()
create() {
return "This will create a user";
}
@Delete("/:id")
remove(@Param("id") userId: string) {
return `this will delete a user with the id : ${userId}`;
}
@Patch("/:id")
path(@Param("id") userId: string, @Body() updateData) {
return `this will patch a user with the id : ${userId}`;
}
}
Express는 간결해보이지만, Nestjs는 클래스별로 컨트롤러에서 메소드에 데코레이터를 사용합니다.
이외에 auth 데코레이터를 사용하고 swagger 데코레이터도 사용한 예시입니다.
서비스의 차이
// Express
router.route('/')
.get((req,res) => {
// 서비스 로직
})
// NestJS
import { Injectable } from '@nestjs/common';
import { DB_CONNECTION } from '../../configs/database/constants';
import { UserInput } from '../../common/dtos/userInput.dto';
import { CoreOutput } from '../../common/dtos/output.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(UsersRepository, DB_CONNECTION)
private readonly usersRepository: UsersRepository,
) {}
/**
* user 가입 생성
* @param userInput
* @returns
*/
async create(
userInput: UserInput,
): Promise<CoreOutput> {
try {
await this.usersRepository.saveUser(
userInput
);
return { ok: true, message: 'user create success' };
} catch (error) {
return { ok: false, message: 'error:' + error };
}
}
Nestjs의 경우 create 함수 인자로 인터페이스로 정한 값들이 들어오고 return 타입을 공통 coreOutput이라는 인터페이스로 정한 ok, message로 리턴합니다.
ref: [NestJS] NestJS 시작하기 (Express 와의 비교) — nGyu (tistory.com)
https://dosomthing.tistory.com/2
https://docs.nestjs.com/first-steps