목차5개 항목
Mongoose는 MongoDB 문서를 JavaScript 객체로 다루게 해 주는 ODM(Object Document Mapper)이다. @nestjs/mongoose를 쓰면 스키마를 TypeScript 클래스와 데코레이터로 정의할 수 있다.
연결과 디버그 로그
npm i @nestjs/mongoose mongoose- 접속 문자열은
.env의MONGODB_URI에 두고MongooseModule.forRootAsync로 읽는다. 패턴은 설정과 환경 변수에 있다. - Atlas를 쓰면 클러스터의 Connect → Drivers에서 접속 문자열을 복사한다. Atlas 설정은 MongoDB 노트에 있다.
mongoose.set('debug', true)를 켜면 Mongoose가 실행하는 쿼리가 로그로 찍힌다. 개발 모드에서만 켠다.
export class AppModule implements NestModule {
private readonly isDev = process.env.MODE === 'dev';
configure() {
mongoose.set('debug', this.isDev); // dev일 때만 쿼리 로그
}
}
useNewUrlParser,useUnifiedTopology는 Mongoose 6부터 기본 동작이라 넘기지 않는다.useFindAndModify,useCreateIndex는 제거됐다. 오래된 예제에서 보이면 지운다.
스키마 정의 순서
- 클래스를 정의하고
@Schema(options)를 붙인다. - 각 필드에
@Prop()으로 컬럼 조건을 정의한다. SchemaFactory.createForClass()로 클래스를 실제 스키마로 바꾼다.
// cats/cats.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument, SchemaOptions } from 'mongoose';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
const options: SchemaOptions = {
timestamps: true, // createdAt, updatedAt 자동 기록
};
@Schema(options)
export class Cat {
@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;
readonly readOnlyData: { id: string; email: string; name: string };
}
export type CatDocument = HydratedDocument<Cat>; // _id, id, save() 등을 포함한 문서 타입
export const CatSchema = SchemaFactory.createForClass(Cat);@Prop()은 DB 차원의 제약(필수, 유일),@IsEmail()등은 요청 검증용이다. 스키마 클래스에 검증 데코레이터를 같이 달아 두면 DTO가PickType으로 상속해 재사용할 수 있다.- 스키마 클래스는 순수 클래스로 두고,
_id·save()같은 문서 기능이 붙은 타입은HydratedDocument<Cat>으로 따로 만든다. 과거에는class Cat extends Document로 상속하는 방식을 썼다.
모듈에 등록하고 주입받기
// cats.module.ts
@Module({
imports: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])],
providers: [CatsService],
})
export class CatsModule {}// cats.service.ts (또는 repository)
constructor(@InjectModel(Cat.name) private readonly catModel: Model<Cat>) {}실수 주의:
name: 'Cat.name'처럼 따옴표로 감싸면 문자열"Cat.name"이 모델 이름이 되어@InjectModel(Cat.name)(="Cat")과 맞지 않는다. 반드시Cat.name표현식을 쓴다.
virtual 필드로 응답 필터링
회원가입 후 생성된 문서를 그대로 반환하면 해시된 비밀번호까지 노출된다. virtual 필드는 DB에 저장되지 않지만 문서에서 계산해 꺼낼 수 있는 필드로, 응답용 데이터만 골라낼 때 유용하다.
CatSchema.virtual('readOnlyData').get(function (this: CatDocument) {
return {
id: this.id,
email: this.email,
name: this.name,
};
});const cat = await this.catModel.create({ email, name, password: hashedPassword });
return cat.readOnlyData; // password 제외조회 시점에 아예 빼고 싶다면 쿼리에서 .select('-password')를 쓴다.
핵심 정리
@Schema+@Prop+SchemaFactory.createForClass= Mongoose 스키마.MongooseModule.forFeature로 등록 →@InjectModel(Cat.name)으로Model<Cat>주입.- 응답에서 민감 필드는 virtual 필드나
select('-field')로 숨긴다.