나오게 된 배경
- 자바스크립트가 발전하면서 다양한 framework가 나오게되면서 front-end/back-end 둘 다 다양하게 활용할 수 있게 되었다. front-end는 Angular,React,Vue 등 다양한 프레임워크들이 빠르고 유연하고 확장성있게 개발할 수 있도록 지원한다. back-end도 express.js,node.js 프레임워크, 또한 다양한 라이브러리들을 지원한다. 하지만, 유연성있는 아키텍쳐를 설계할 수 있는것은 없다. 그런 문제점을 해결하기 위해 나온게 NestJS이다. NestJS의 가장 큰 장점은 아키텍쳐를 유연성있고, 유지보수가 쉬우며, 테스트하기 쉽고 결합도 루즈하게 개발할 수 있게 만들어준다. 간단한 게시판 API를 만들면서 느껴보자.
NestJS 설치하기
- 밑에 명령어 터미널에 입력
npm i -g @nestjs/cli
- 설치 후 밑 명령어로 버전확인
nest --version
- nest 프로젝트 생성
nest new {프로젝트명}
생성된 프로젝트 초기 구조
- controller: 프레젠테이션 계층입니다. request(요청) & response(응답) 을 처리합니다.
- service: 서비스 계층입니다. 비즈니스 로직을 처리하는 곳입니다.
- module: 모듈 메타 데이터입니다. java의 main method 처럼 nest가 어플리케이션을 시작할 때 사용되는 메인 모듈입니다. 안에있는 모듈들이 실행됩니다.
모듈 생성
nest g res {모듈명}
- test라는 모듈을 생성했다. 놀랍게도!!! 위에 명령어만 쳤는데 밑처럼 자동으로 해당 모듈 관련 dto,entities,controller,service,module 이 생성된다.
npm run start
- 위 명령어를 사용하여 서버를 킨 후 http://localhost:3000/boards 를 입력한 후 접속하면 밑에 이미지 처럼 표시된다. (이 말은 기본적인 라우팅은 되있고 데이터베이스와 연동 후 응답값만 처리해주면 된다.)
데이터베이스 연동(MYSQL)
- 밑에 명령어로 DB 연결할 라이브러리(객체 관계 ORM 매퍼) 설치
yarn add mysql typeorm @nestjs/typeorm
- /entities 안에 board.entity.ts 파일안에 엔티티 내용 작성
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity("board")
export class Board {
@PrimaryGeneratedColumn()
private id: number;
@Column({ length: 1000})
private title: string;
@Column({length: 5000})
private content: string;
constructor(
title: string,
content: string
){
this.title = title;
this.content = content;
return this;
}
}
- app.module.ts 안에 import { TypeOrmModule } from '@nestjs/typeorm'; 와 @module 안에 TypeOrmModule.forRoot() 안에 DB 접속정보를 입력한후 엔티티 생성한 모듈을 넣어주면된다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { BoardsModule } from './boards/boards.module';
@Module({
imports: [TypeOrmModule.forRoot({
"type": "mysql",
"host": "hostname",
"port": 3306,
"username": "username",
"password": "password",
"database": "database",
"entities": ["dist/**/**.entity{.ts,.js}"],
"synchronize": true
}), BoardsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
- npm run start 로 서버를 구동시킨후 DB 툴을 보면 테이블/컬럼이 생성되었다.
Repository 연동
- /boards - boards.repository.ts 파일을 생성 후 작성
- typeorm 0.3x 부터는 @EntityRepository는 deprecated이 되서 @CustomRepository를 만들어줘야한다.(참조: https://velog.io/@wonjun1995/NestJS9.x.x-TypeORM0.3.x%EC%97%90%EC%84%9C-customRepository-%EC%89%BD%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
- /db 폴더 생성
- /db 경로에 typeorm-custom.module.ts 파일 생성 후 밑 코드 작성
import { DynamicModule, Provider } from '@nestjs/common';
import { getDataSourceToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { TYPEORM_CUSTOM_REPOSITORY } from './typeorm.decorator';
export class TypeOrmCustomModule {
public static forCustomRepository<T extends new (...args: any[]) => any>(
repositories: T[],
): DynamicModule {
const providers: Provider[] = [];
for (const repository of repositories) {
const entity = Reflect.getMetadata(TYPEORM_CUSTOM_REPOSITORY, repository);
if (!entity) {
continue;
}
providers.push({
inject: [getDataSourceToken()],
provide: repository,
useFactory: (dataSource: DataSource): typeof repository => {
const baseRepository = dataSource.getRepository<any>(entity);
return new repository(
baseRepository.target,
baseRepository.manager,
baseRepository.queryRunner,
);
},
});
}
return {
exports: providers,
module: TypeOrmCustomModule,
providers,
};
}
}
- /db - typeorm.decorator.ts 파일 생성 후 밑 코드 작성
import { SetMetadata } from '@nestjs/common';
export const TYPEORM_CUSTOM_REPOSITORY = 'TYPEORM_CUSTOM_REPOSITORY';
// eslint-disable-next-line @typescript-eslint/ban-types
export function CustomRepository(entity: Function): ClassDecorator {
return SetMetadata(TYPEORM_CUSTOM_REPOSITORY, entity);
}
- /src/boards - boards.repository.ts 파일 생성 후 위에 만든 @CustomRepository 사용
import { CustomRepository } from "src/db/typeorm.decorator";
import { Repository } from "typeorm";
import { Board } from "./entities/board.entity";
@CustomRepository(Board)
export class BoardRepository extends Repository<Board>{};
- /boards - boards.module.ts
import { Module } from '@nestjs/common';
import { BoardsService } from './boards.service';
import { BoardsController } from './boards.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from './entities/board.entity';
import { BoardRepository } from './boards.repository';
import { TypeOrmCustomModule } from 'src/db/typeorm-custom.module';
@Module({
imports:[TypeOrmCustomModule.forCustomRepository([BoardRepository])],
controllers: [BoardsController],
providers: [BoardsService]
})
export class BoardsModule {}
게시판 생성 API
- /boards - boards.service.ts
import { Injectable } from '@nestjs/common';
import { BoardRepository } from './boards.repository';
import { CreateBoardDto } from './dto/create-board.dto';
import { UpdateBoardDto } from './dto/update-board.dto';
import { Board } from './entities/board.entity';
@Injectable()
export class BoardsService {
// repository 주입
constructor(private readonly boardRepository: BoardRepository) {}
// 생성
async create(createBoardDto: CreateBoardDto) {
const board = new Board(createBoardDto.title,createBoardDto.content);
return this.boardRepository.save(board);
}
// 리스트 조회
findAll() {
return this.boardRepository.find();
}
// 단일 조회
findOne(id: number) {
return this.boardRepository.findOneBy({ id: id});
}
update(id: number, updateBoardDto: UpdateBoardDto) {
return `This action updates a #${id} board`;
}
remove(id: number) {
return `This action removes a #${id} board`;
}
}
- /boards/dto 에 create-board.dto.ts 에 밑처럼 필드값 작성
export class CreateBoardDto {
title:string;
content:string;
}
위처럼 하면 게시글 리스트 조회/단일 조회/생성 API가 생성되었다.
테스트하기
- POST http://localhost:3000/boards - 생성 API (성공)
- GET http://localhost:3000/boards - 리스트 조회 API (성공)
- GET http://localhost:3000/boards/2 - 아이디 값으로 조회 API (성공)
결론
- 회사에서 새 프로젝트로 NestJS를 사용하기 위해간단한 CRUD를 구현해봤다. 필자는 Spring + JPA 프레임워크를 쓴 경험이 있는데 NestJS + TypeORM도 유사한거 같다. 개인적으로 왜 핫한지 알꺼 같다! 모듈식으로 구조가 정해져 있어서 유지보수 측면에서 좋을꺼 같다. 또한, 간단하고 빠르게 설정부터 개발까지 할 수 있다. 모듈만 생성하면 알아서 관련 controller/service/dto/entity 까지 만들어진다.하지만 아직까진 Spring + JPA 와 차별점과 장점은 잘 모르겠다. 계속 해보면서 알아가야겠다.
'Typescript' 카테고리의 다른 글
[NestJS] Providers, Service, PIPE 등 기본 개념에 대해 알아보자 (0) | 2023.04.03 |
---|