NestJS로 백엔드 프로젝트 시작하기 (TypeORM, PostgreSQL)
Nest.js 란?
Java Spring
의 구조를 차용한 Node.js
환경의 새로운 프레임워크입니다. 이는 MVC 디자인 패턴(Controller, Service, Entity, Repository 등)을 기본 패턴으로 하므로 역할과 구현(또는 책임)이 명확하게 구분된다는 장점이 있습니다. 따라서, 협업 시 매우 효율적으로 작업할 수 있으며, Typescript
를 기본으로 적용하므로 잠재적인 오류 발생 확률을 줄일 수 있습니다.
실제로 저는 Nest 는 이번 기회에 처음 써봤는데, Spring 개발 경험과 Typescript 로 프론트엔드 개발했던 경험이 있어서 그런지 금방 적응 할 수 있었습니다.
프로젝트 생성
$ nest new project1
$ cd project1
$ npm run start
- Node 가 설치되어 있는 환경에서 위 명령어를 통해 프로젝트를 생성해주었습니다.
- vscode 터미널에서 간단히 프로젝트를 생성할 수 있습니다.
프로젝트가 정상적으로 생성되었을 경우에는 브라우저에서 localhost:3000 에 접속했을 때 위와같이 Hello world! 라는 글자가 보이게 됩니다.
nest start
명령어로도 로컬 서버를 실행시킬 수 있습니다.
TypeORM 의존성 설치
$ npm install @nestjs/typeorm typeorm pg
@nestjs/typeorm
: NestJS에서 TypeORM을 사용하기 위한 모듈typeorm
: Node.js ORM(Object Relational Mapper) 라이브러리. 엔티티를 통해 DB 테이블을 정의하고, 쿼리 작성 없이도 조작이 가능함.pg
: PostgreSQL을 TypeORM과 함께 사용하기 위한 드라이버
사용 예시
// app.module
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.PG_HOST,
port: parseInt(process.env.PG_PORT ?? '5432'), // 기본값 5432
username: process.env.PG_USER,
password: process.env.PG_PASSWORD,
database: process.env.PG_DBNAME,
entities: [__dirname + '/**/*.entity.{ts,js}'],
synchronize: true,
}),
app.module
파일에서 orm 설정을 할 수 있습니다.
저는 .env 파일을 통해 민감정보를 처리하는 방식을 활용했습니다.
// src/data-source.ts
import { DataSource } from 'typeorm';
export const AppDataSource = new DataSource({
type: 'postgres',
host: process.env.DATABASE_HOST || 'localhost',
port: Number(process.env.DATABASE_PORT) || 5432,
username: process.env.DATABASE_USER || 'postgres',
password: process.env.DATABASE_PASSWORD || 'postgres',
database: process.env.DATABASE_NAME || 'board_db',
entities: [__dirname + '/../**/*.entity.{ts,js}'],
synchronize: true,
});
data-source.ts 파일을 src 안에 생성해주고 위 설정을 추가해줍니다.
환경변수 관리 (.env)
$ npm install @nestjs/config
.env
파일을 사용해서 환경별 설정을 분리할 수 있게 해주는 모듈입니다.- DB 정보, JWT 시크릿 키, 포트 번호 등 민감한 정보를 코드에서 분리할 수 있습니다.
저는 개발환경과 서비스 환경의 DB를 분리하기 위해 두개의 .env파일을 만들었습니다. (.env.production
, .env.development
)
// .env.development
NODE_ENV=development
PG_HOST=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASSWORD=password
PG_DBNAME=boarddev
JWT_SECRET=access-secret
JWT_REFRESH_SECRET=refresh-secret
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_DB=boarddev
이런식으로 민감정보를 따로 관리하여 노출되지 않게 할 수 있습니다.
//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import { config } from 'dotenv';
// NODE_ENV에 따라 환경변수 파일 선택
const envFile =
process.env.NODE_ENV === 'production'
? '.env.production'
: '.env.development';
config({ path: envFile });
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
// 개발 환경일 경우 요청 로그 기록
if (process.env.NODE_ENV === 'development') {
app.use((req, res, next) => {
const { method, originalUrl } = req;
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const log = `${method} ${originalUrl} ${res.statusCode} ${res.statusMessage} - ${duration}ms`;
console.log(log);
});
next();
});
}
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
그리고, 개발환경일 경우 api 호출이 될때 로그를 찍고 싶어서 위 코드를 추가해주었습니다.
데이터 검증
$ npm install class-validator class-transformer
위 라이브러리를 설치하면, 들어오는 데이터를 자동으로 검증하고 변환할 수 있습니다.
import { IsString, IsEmail } from 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
}
예를 들어, 이메일 형식인지 string 형식인지 자동으로 검증해줍니다.
위 사진은 postman 으로 테스트한 결과입니다. 올바르지 않은 형식으로 요청했을 때, 자동으로 에러메세지를 반환해주는 모습을 볼 수 있습니다.
설정 방법
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
main.ts
에app.useGlobalPipes(new ValidationPipe());
이 부분을 넣어줍니다. listen 코드 이전에 넣어야합니다.
마무리
이렇게 기본적인 NestJS
프로젝트를 구성하면서 필수적인 라이브러리들을 설치하고 활용하는 방법을 살펴봤습니다. 다음 포스팅에서는 swagger
설정부터 docker
활용까지 확장해보겠습니다.