Hyen Dev
Nest.js + typeORM + MySQL 연동하기 본문
Nest.js 선정 과정
최근에 GDSC에서 시작한 mini project는 프론트, 서버, ai 파트가 적어도 한 명 이상씩 참여해야했기 때문에 처음 기획단계에서는 ai가 대부분 파이썬을 이용하니까 백엔드는 장고로 진행하자!라고 결정했었다.
하지만, 막상 개발을 하려고보니 그건 장고 안에서 ai 모델을 직접적으로 사용할때나 의미있는 기능들이지, 우리는 백엔드와 AI 담당이 명확하게 나뉘어져있었기 때문에 ai파트에 데이터를 넘겨주거나 받아오기만 하면 되는 상황이다. 그래서 서버 스터디에서 사용했던 node.js를 쓰는걸로 변경했다.
거의 반년만에 다시 본 node.js는 나에게는 생각보다 불편한 점들이 많았다.
현재 내게 제일 익숙하고 편한 스프링부트와 계층 구조가 많이 달랐으며, 결정적으로 스프링부트에선 JPA를 쓰며 쿼리문을 작성하지 않아도 됐었는데, 노드는 코드를 짜는데 자유도가 너무 높아서 쿼리문도 직접 작성하고 내가 일일이 수동으로 적용해야하는게 너무 많았다..
그래서 좀 더 찾아본 결과, node.js 기반의 프레임워크인 nest.js를 typeORM과 함께 사용하면 나에게 익숙한 스프링부트와 유사한 구조로 쓸 수 있다는 것을 알게되었다. 또한, Swagger 역시 기본으로 지원하고있는 것 역시 마음에 들었다.
(사실 이 밖에도 나랑 친한 창업한 선배들도 대부분 nest쓰고, 스타트업들도 nest를 쓰는 곳들이 많아져서 대체 왜들 그렇게 사용하는걸까 이참에 알아보고싶었던 것도 있다 ㅎㅎ)
그렇게 이번 프로젝트 최종 백엔드 프레임워크는 Nest.js가 되었다!
Nest.js란?

Nest.js 는 Node.js 환경에서 동작하는 프레임워크로, Node.js의 Express 위에서 구축된 프레임워크이다.
내가 생각한 Nest.js의 장점들은 다음과 같다.
- Express의 기능을 확장하여 보다 체계적이고 모듈화된 구조 제공 -> 서비스 확장 및 유지 보수에 유리!
- 기존의 Node.js에서도 Express를 설치하여 함께 사용하곤 했었는데 Express는 모듈 구조를 직접 정의하여 개발해야했다. 반면에 NestJS는 Decorator 기반으로 사전에 모듈구조가 정의되어 있어 모듈성 측면에서 뛰어나다고 할 수 있다.
- 정형화된 아키텍처
- Nest.js에서는 기존 Express와는 달리 정형화된 아키텍처를 지니고 있다. Controller, Service, DTO 등의 개념과 Decorator(스프링에서의 Annotation과 같은 역할)가 사용된다는 점에서 나처럼 스프링 구조에 익숙한 개발자들은 이것 역시 장점이 될 것이라 생각한다.
- 기본적으로 지원되는 기능들
- Express는 필요한 기능을 위해 라이브러리를 따로 설치해서 사용했어야했는데, NestJS는 필수적인 라이브러리 및 편의 기능을 기본으로 포함하고 있다. HTTP, 웹 소켓, 미들웨어 구축, 인증 및 보안, 예외 필터, 로깅 등 서버 동작에 필수적인 기능을 사전에 포함하고 있다. 이번에 Nest.js를 선정하게 된 이유인 swagger문서를 자동으로 작성해주는 기능 제공도 있다.
- Express 프레임워크(기본 설정)와 Fastify 프레임워크를 선택해서 사용 가능
- 자동으로 생성되는 테스트 파일
- Jest라고 하는 테스트 환경이 자동으로 제공되는데, Nest.js에서 제공하는 CLI를 통해 파일을 생성하면 테스트를 위한 spec 파일이 자동으로 생성된다는 점 역시 신선했다.
Nest.js 기본 세팅
npm i -g @nestjs/cli
제일 먼저 이 명령어를 실행하면 NestJS CLI를 컴퓨터 시스템에 전역적으로 설치한다. NestJS CLI는 NestJS 애플리케이션을 생성하고 관리하는데 사용할 수 있는 명령어 세트를 제공한다.
nest new {프로젝트 폴더명}
# 만약 이미 프로젝트 디렉토리가 만들어져있는 상태라면, 해당 폴더로 이동 후 아래의 명령어를 입력하면 된다.
nest new ./
이 명령어는 새로운 NestJS 프로젝트를 현재 위치의 {프로젝트 폴더명} 이름의 폴더 안에 생성한다.
이후, npm run start 로 실행하고, localhost:3000으로 접속해보면 Hello World가 뜰 것이다.
TypeORM이란?
typeORM을 알아보기 전에 ORM에 대해 다시 한 번 짚어보고 넘어가자.
Object-Relational Mapping(ORM)은 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑해주는 도구이다.
< 장점 >
📍 SQL문을 작성할 필요가 없어진다
: 직접 쿼리를 날리는것이 아닌 개발자들이 선호하는 프로그래밍언어를 사용하여 데이터베이스와 소통할 수 있다
: 코딩하는것 처럼 자연스러운 방식으로 데이터베이스에 질의할 수 있다.
📍 data model을 한 군데에만 작성해도 된다.
: 유지보수하기 쉽고 반복되는 코드가 적어진다.

TypeORM은 NodeJS 를 비롯한 Javascript, Typescript 플랫폼에서 활용 가능한 ORM을 말한다.
TypeORM 설정
npm install --save @nestjs/typeorm typeorm mysql2
NestJS 프로젝트에서 TypeORM과 MySQL을 사용하기 위해 필요한 패키지들을 설치한다.
typeorm.config.service.ts
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from "@nestjs/typeorm";
import { ConfigService } from '@nestjs/config';
import { Injectable } from "@nestjs/common";
@Injectable()
export class TypeOrmConfigService implements TypeOrmOptionsFactory {
constructor(private readonly configService: ConfigService) {}
createTypeOrmOptions(): TypeOrmModuleOptions {
return {
type: 'mysql',
host: this.configService.get<string>('DB_HOST'),
port: this.configService.get<number>('DB_PORT'),
username: this.configService.get<string>('DB_USER'),
password: this.configService.get<string>('DB_PASSWORD'),
database: this.configService.get<string>('DB_NAME'),
entities: [__dirname + '/../**/*.entity{.js,.ts}'],
synchronize: true,
autoLoadEntities: true,
logging: true,
};
}
}
루트 모듈인 app.module.ts 에서 import 해주기
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
TypeOrmModule.forRootAsync({
useClass: TypeOrmConfigService,
}),
UserModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
📍Module 이란?

Nest.js에서 모듈은 @Module() 데코레이터가 붙어있는 클래스를 의미한다.
Nest.js 의 프로젝트에는 최소 한 개 이상의 모듈이 존재하며, 최초 프로젝트 생성 시에 만들어지는 최초 모듈은 Root Module(AppModule) 이다.
각각의 모듈은 밀접하게 관련있는 기능(인증, 게시판 등)들을 캡슐화하는 형태로, 쉽게 말하자면 비슷한 기능을 하는 도메인 별로 묶어서 관리하는 느낌이다. 덕분에 명시적으로 알 수 있어 편리하긴 하지만 순환참조와 같은 문제가 일어나지 않도록 모듈간의 의존성 관리를 잘 해줘야한다.
user 리소스 생성
nest g resource user 명령어를 실행하여 user라는 이름을 가진 리소스를 생성한다.
리소스는 일반적으로 CRUD(Create, Read, Update, Delete) 기능을 갖춘 기본적인 API 모듈을 의미한다.
즉, 해당 명령어를 실행하면 기본적으로 crud 기능에 필요한 파일들이 모두 생성된다.
// user.entity.ts
@Entity({ name: 'user' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
test: string;
}
테스트 용으로 위처럼 entity를 하나 만들어준다.
// user.service.ts
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
데이터베이스 작업을 수행하기 위해 user.service.ts에 TypeORM을 사용하여 User 엔티티에 대한 리포지토리를 주입(Injection)하는 코드도 넣어준다.
// user.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
forFeature 메서드로 특정 기능 모듈에서 사용할 엔티티를 등록한다. 여기서는 테스트용으로 만들어뒀던 User 엔티티를 등록하여, 이 모듈 내의 서비스에서 User 엔티티와 관련된 리포지토리를 사용할 수 있게 한다.
여기까지 설정을 하고 npm run start 로 실행한 뒤, DataGrip으로 확인해봤을때 아래와 같이 Nest.js에서 작성한 엔티티가 그대로 잘 반영되면 DB 연결이 제대로 되었음을 확인할 수 있다.

3주 안에 프로젝트 하나 개발을 끝내야하기 때문에 오늘 하루 만에 nest 파악하고 적응하는게 목표였는데,
스프링과 유사한 구조를 가지니까 바로바로 이해가 돼서 오늘 하루만에 모든 환경 세팅과 DB ERD 구상 및 적용까지 끝낼 수 있었기에 더욱 더 프레임워크 바꾸길 잘했다는 생각이 들었다..ㅎㅎ
앞으로 공부해야할게 아직도 산더미처럼 많지만 이번 달은 인턴부터 프로젝트, GDSC 까지 그동안 해오던 많은 일들이 마무리되는 달인만큼 전부 마지막까지 잘 해내어 올해 상반기를 성공적으로 마무리하고싶다☺️