이번 포스팅에서는 NestJS 프로젝트에 MongoDB를 연결하고 환경변수를 설정하는 방법에 대해 정리해보고자 한다.
데이터베이스는 MongoDB Atlas를 사용하였고 Mongoose 패키지를 사용하였다.
< 목차 >
- MongoDB 연결 & 환경 변수 설정
- Schema 설계
- Virtual field 설정
1. MongoDB 연결 & 환경 변수 설정
우선 다음과 같이 mongoose 패키지를 설치해주도록 하자.
$ npm i --save @nestjs/mongoose mongoose
설치가 완료되었다면 AppModule에서 다음과 같이 MongooseModule을 import 해준다.
// app.module.ts 파일
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [MongooseModule.forRoot('MongoDB_URI')],
})
export class AppModule {}
'MongoDB_URI'는 노출이 되어서는 안되므로 환경 변수를 설정해서 따로 관리해 주는 것이 좋다. 환경 변수를 설정하기 위해서는 다음과 같이 @nestjs/config 를 설치해주면 되는데 공식문서에 따르면 @nestjs/config 패키지는 내부적으로 'dotenv'를 사용한다고 한다.
$ npm i --save @nestjs/config
.env를 사용하기 위해서는 마찬가지로 AppModule에서 다음과 같이 ConfigModule을 import 해주면 된다.
// app.module.ts 파일
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule.forRoot(), MongooseModule.forRoot(process.env.MONGODB_URI)],
})
export class AppModule {}
참고로 모듈 안에서 환경 변수를 사용하기 위해서는 위와 같이 import를 통해 ConfigModule을 가져와야만 한다. 하지만 다음과 같이 options 객체의 isGlobal 속성을 true로 설정해 놓으면 ConfigModule을 global로 사용할 수 있다. 이렇게 { isGlobal: true } 로 설정할 경우 루트 모듈(ex. AppModule)에서 로드된 후에는 다른 모듈에서 ConfigModule을 가져올 필요가 없게 된다.
ConfigModule.forRoot({
isGlobal: true,
});
마지막으로, 개발시 mogoose의 쿼리를 로그로 찍어주는 세팅을 추가해보도록 하자.
// app.module.ts 파일
import { Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
import { ConfigModule } from '@nestjs/config';
import * as mongoose from 'mongoose';
@Module({
imports: [ConfigModule.forRoot(), MongooseModule.forRoot(process.env.MONGODB_URI)],
controllers: [AppController],
providers: [AppService]
})
export class AppModule implements NestModule {
private readonly isDev: boolean = process.env.NODE_ENV === 'dev' ? true : false;
configure() {
mongoose.set('debug', this.isDev); // mongoose 쿼리 logger
}
}
AppModule의 configure( ) 함수 안에서 mongoose.set( 'debug' , true ) / mongoose.set( 'debug' , false ) 를 통해 mongoose 쿼리를 logging 할 수 있도록 위와 같이 세팅해주면 된다.
2. Schema 설계
MongoDB 연결이 완료되었다면 이제 스키마를 설계해주도록 하자.
// users.schema.ts 파일
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
import { Document, SchemaOptions } from 'mongoose';
// 스키마 옵션
const options: SchemaOptions = {
timestamps: true,
};
// @Schema() 데코레이터을 사용해서 스키마 정의
@Schema(options)
export class User extends Document {
@Prop({
required: true,
unique: true,
})
@IsEmail()
@IsNotEmpty()
email: string;
@Prop({
required: true,
})
@IsString()
@IsNotEmpty()
name: string;
@Prop({
required: true,
})
@IsString()
@IsNotEmpty()
password: string;
@Prop()
@IsString()
imgUrl: string;
}
// User 클래스를 스키마로 만들어준다.
export const UserSchema = SchemaFactory.createForClass(User);
우선 'mongoose'의 Document를 상속 받은 다음, @Schema( ) 데코레이터를 사용해서 스키마를 정의해준다. 그리고 SchemaFactory.createForClass( )를 통해 User 클래스를 스키마로 만들어주면 된다. 추가로 SchemaOptions는 스키마에 대한 옵션값이며 @Schema( ) 데코레이터의 인자값으로 전달해 줄 수 있다.
@Prop( ) 데코레이터를 사용해서 document 안의 속성을 정의해 줄 수 있다. @Prop( ) 데코레이터의 인자값으로 options 객체를 전달하여 required 여부 , unique 여부 , default value 등의 옵션 지정이 가능하다.
더 나아가 'class-validator'를 사용해 document 안의 속성들에 대한 유효성 검사(validation)를 진행할 수 있다. class-validator에서 제공해주는 @IsEmail( ) , @IsString( ) , @IsNotEmpty( ) 등과 같은 데코레이터를 사용해 validation 역시 추가해 줄 수 있다. class validation을 추가했다면 다음과 같이 main.ts에서 useGlobalPipes( )를 통해 ValidationPipe( )를 등록해주기만 하면 된다.
// main.ts 파일
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe()); // class validation 등록
await app.listen(4000);
}
bootstrap();
3. Virtual field 설정
'mongoose'에서는 스키마 안에 virtual field를 만들어서 보여주고 싶은 속성 값만을 return 해주도록 만들 수 있다. Virtual field는 실제 DB에 저장되는 물리적인 필드는 아니지만, 비즈니스 로직에서 사용할 수 있도록 제공해주는 가상의 필드이다. 다음과 같이 스키마에 virtual( ) 메소드를 사용해서 virtual field를 만들 수 있다.
UserSchema.virtual('readOnlyData').get(function (this: User) {
return {
id: this.id,
email: this.email,
name: this.name,
};
});
Virtual field로 만들어준 readOnlyData 필드를 사용하면 이제 클라이언트에게 보여주고 싶은 데이터만을 가상으로 필터링해서 내보내줄 수 있게 된다. Virtual field를 추가적으로 만들었다면 다음과 같이 스키마에 해당 속성을 추가해 주는 것을 잊지 말자.
@Schema(options)
export class User extends Document {
readonly readOnlyData: { id: string; email: string; name: string };
}
'Node > NestJS' 카테고리의 다른 글
NestJS - Pipes & Interceptors (0) | 2023.03.26 |
---|---|
NestJS - Exception filter 설정 (0) | 2023.03.16 |
NestJS - LoggerMiddleware 설정 (0) | 2023.03.13 |
NestJS - 캡슐화(Encapsulation) & Modules (0) | 2023.03.10 |
NestJS - 의존성 주입(DI) & Providers (0) | 2023.03.10 |