MongoDB with NestJs
MongoDB Atlas
데이터베이스를 직접 컴퓨터에 설치할 수도 있지만, 클라우드화 된 외부 서비스에 이를 위임할 수도 있다. MongoDB의 경우 Atlas라는 서비스가 대표적이다.
새로운 Atlas 프로젝트를 생성하면 클러스터를 생성하라는 안내 문구가 나오게 된다. 학습 용도라면 무료 버전인 M0를 선택하고, 클러스터 이름을 정해주면 된다. 클러스터가 생성되면 그 즉시 보안 설정 팝업이 뜨게 되는데, 데이터베이스에 접속할 때 필요한 유저 네임과 비밀번호를 설정하고 이를 안전한 곳에 기록해두어야 한다.
Database 탭에 가서 Browse Collections에 add my own data 클릭하고 데이터베이스 이름과 컬렉션 이름을 입력하고 create 버튼을 누르면 된다. 여기에 연결하려면 다시 Database 탭에 가서 connect 후 drivers 옵션을 클릭해 아래의 코드를 복사해서 환경변수에 넣어주면 된다.
@nestjs/Mongoose
MySQL이 Sequelize라는 ORM을 사용하고 PostgreSQL이 TypeORM을 사용하는 것처럼, MongoDB는 Mongoose라는 이름의 ODM을 사용한다. 나는 백엔드 프레임워크로 nestjs를 사용하기 때문에 nestjs에서 말아주는 Mongoose를 사용하고 있다.
환경 변수를 사용하여 데이터베이스 주소를 참조하게 하려면 추가적인 @nestjs/config 라이브러리를 설치해야 한다. 여러 dotenv를 사용하는 방법도 나와있으니 궁금하다면 관련 내용은 공식 문서 중 Configuration을 참고하면 좋겠다.
MongooseModule과 configModule에서 제공하는 forRoot 메소드를 사용해 dotenv 파일과 데이터베이스를 연결해준다. 만약 어떤 상황이 생겨서 데이터베이스 여러 개를 연결해야 하는 상황이 된다면 역시 nestjs 공식 문서 중 Multiple databases에 나와있는 설명대로 진행해야 한다.
// CLI
npm i @nestjs/mongoose mongoose
npm i --save @nestjs/config
// .env
DATABASE_URL = 'mongodb+srv://';
// app.module.ts
@Module({
imports: [
ConfigModule.forRoot(),
MongooseModule.forRoot(process.env.DATABASE_URL)
],
})
export class AppModule {}
Schema
스키마는 다른 데이터베이스에서도 사용하지만, mongoDB의 스키마는 살짝 다른 것 같다. 모델의 구조와 프로퍼티를 지정하고 데이터베이스와의 연결을 지원하는 일종의 징검다리같은 역할처럼 느껴진다.
깡 mongoose에서는 스키마 만드는 게 좀 번거로웠던 거로 기억하는데, nestjs에서는 간단하게 Schema를 구현할 수 있다. 특히 @Prop 데코레이터를 사용해 각 프로퍼티에 대한 필수값이나 기본값 내용 등의 설정을 넣어줄 수도 있다.
여기서 사용한 required, unique, default 등의 설정에 대해서는 mongoose 공식 문서에서 더 자세히 확인할 수 있다.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
export enum BoardStatus {
PUBLIC = 'PUBLIC',
PRIVATE = 'PRIVATE',
}
@Schema()
export class Board {
@Prop({ required: true, unique : true})
title: string;
@Prop({ required: true, })
description: string;
@Prop({ default: 'PUBLIC' })
status: BoardStatus;
@Prop({ default: [] })
tag: Array<string>;
}
export const BoardSchema = SchemaFactory.createForClass(Board);
MongooseModule.forFeature를 사용해 스키마를 모듈에 연결해주면 해당 모듈의 프로바이더에서 데이터베이스를 사용할 수 있게 된다.
@Module({
controllers: [BoardsController],
providers: [BoardsService],
imports: [
MongooseModule.forFeature([{ name: Board.name, schema: BoardSchema }]),
],
})
export class BoardsModule {}
의존성 주입 및 CRUD
@InjectModel 데코레이터를 사용해 프로바이더에 데이터베이스 모델을 주입하고, 이 프로바이더를 컨트롤러에 주입하는 방식으로 사용된다. Model에서 가져다 쓸 수 있는 메소드는 역시 공식 문서를 참고하면 좋겠다.
// boards.controller.ts
@Controller('boards')
export class BoardsController {
constructor(private boardService: BoardsService) {}
}
// boards.service.ts
@Injectable()
export class BoardsService {
constructor(@InjectModel(Board.name) private BoardModel: Model<Board>) {}
}
@Injectable()
export class BoardsService {
constructor(@InjectModel(Board.name) private BoardModel: Model<Board>) {}
createBoard(createBoardDTO: CreateBoardDTO) {
return new this.BoardModel({ ...createBoardDTO }).save();
}
getBoards() {
return this.BoardModel.find();
}
getBoardsByUserName(userID) {
return this.BoardModel.find({ userID });
}
getBoardById(id: string) {
return this.BoardModel.findById(id);
}
updateBoard(id: string, createBoardDTO: CreateBoardDTO) {
return this.BoardModel.findByIdAndUpdate(id, createBoardDTO, { new: true });
}
deleteBoard(id: string) {
return this.BoardModel.findByIdAndDelete(id);
}
}
블로그의 정보
Ayden's journal
Beard Weard Ayden