더 공부하고 정리한 추가 게시물이 있습니다.
node.js의 Express와 nestJs에서의 mongoDB find()메서드 차이점
mongoose의 find()메서드를 Express환경에서 사용할 경우 다음과 같이 출력된다.
[
{
"_id": "id항목입니다",
"title": "test2",
"content": "toDo Contents2",
"status": true,
"priority": 0,
"dueDate": "2024-03-21T00:00:00.000Z",
"category": "sports",
"completedDate": "2023-10-25T07:59:36.295Z"
},
]
반면nestJs에서 find()를 사용할 경우 다음과 같이 메타데이터들이 출력된다.
[
{
"$__": {
"activePaths": {
"paths": {
"category": "init",
"dueDate": "init",
"content": "init",
"title": "init",
"userId": "init",
"status": "init",
"priority": "init",
"isDeleted": "init",
"_id": "init",
"createdAt": "init",
"updatedAt": "init",
"__v": "init",
"completedDate": "init"
},
"states": {
"require": {},
"default": {},
"init": {
"_id": true,
"userId": true,
"title": true,
"content": true,
"status": true,
"priority": true,
"dueDate": true,
"category": true,
"isDeleted": true,
"createdAt": true,
"updatedAt": true,
"__v": true,
"completedDate": true
}
}
},
"skipId": true
},
"$isNew": false,
"_doc": {
"_id": {},
"userId": "",
"title": "test2",
"content": "toDo Contents2",
"status": true,
"priority": 0,
"dueDate": "2024-03-21T00:00:00.000Z",
"category": "sports",
"isDeleted": false,
"createdAt": "2023-10-25T07:58:51.436Z",
"updatedAt": "2023-10-25T07:59:36.295Z",
"__v": 0,
"completedDate": "2023-10-25T07:59:36.295Z"
}
},
둘의 출력이 이렇게 다른 이유는 Express환경에서 Mongoose의 find() 메서드를 사용하면 일반적으로 JSON 형식으로 결과가 반환된다. 이 결과는 기본적으로 JavaScript 객체로 표시된다.
하지만 NestJs에서 Mongoose를 사용할 때는 결과 객체가 Mongoose 문서(Document) 객체로 표시된다. 이것이 `$__`와 `_doc`와 같은 필드가 결과 객체에 추가되는 이유이다. Mongoose는 MongoDB문서를 JavaScript객체로 감싸며, 이러한 추가 필드는 Mongoose의 내부 속성을 나타내며, 결과 객체의 실제 데이터는 _doc필드 아래에 저장된다.
따라서, nestJs에서 Mongoose결과를 일반적은 JSON형태로 표시하려면 .lean() 메서드를 사용할 수 있다. .lean()은 Mongoose결과를 일반 JavaScript 객체로 변환한다.
const user = await user.find().lean();
하지만 이와 같은 방법을 사용하면 Mongoose가 제공하는 모델 메서드 (예: save() 또는 가상필드)를 사용할 수 없다는 단점이 있다.
그렇다면 데이터를 가공하거나 가상 필드를 사용하고자 한다면 어떻게 하면 될까?
nestJs에서 mongoDB document 가공하는 법
데이터 사후 가공 하기
프로젝트를 진행하며 가장 좋았던 방법은 가상필드를 정의 후 데이터를 사후 가공 하는 것이다. 우선 readOnlyData라는 가상필드를 정의한 후 다음과 같은 방법으로 map() 함수나 다른 함수를 사용하여 결과를 가공한다. 이렇게 필요한 필드만 선택하여 새로운 객체로 만들 수 있다.
const allUser = await this.userRepository.findAllUser(code);
const readOnlyData = allUser.map((user) => user.readOnlyData);
return readOnlyData;
virtual field를 만들 때 objectId가 출력되지 않는 이유
virtual field를 만들 때 _id와 같은 objectId가 출력되지 않을 때가 있다. 이 때는 id를 표기하는 방법에 의해서 그런 것인데. 아래 코드를 보자.
import ...
const options: SchemaOptions = {
timestamps: true,
};
@Schema(options)
export class Post extends Document {
...필드...
readonly readOnlyData: {
_id: string;
title: string;
content: string;
status: boolean;
priority: number;
dueDate: string;
category: string;
completedDate: string;
};
}
const _PostSchema = SchemaFactory.createForClass(Post);
_PostSchema.virtual('readOnlyData').get(function (this: Post) {
return {
_id: this.id, // 이 부분을 확인하자
title: this.title,
content: this.content,
status: this.status,
priority: this.priority,
dueDate: this.dueDate,
category: this.category,
completedDate: this.completedDate,
};
});
_PostSchema.set('toObject', { virtuals: true });
_PostSchema.set('toJSON', { virtuals: true });
export const PostSchema = _PostSchema;
주석 처리한 부분이 _id: this._id로 되어있을 가능성이 있다.
Mongoose에서는 기본적으로 _id필드는 MongoDB의 기본 식별자인 ObjectId와 연결되어 있다. _id 필드는 명시적으로 정의된 경우에만 사용자 지정 필드 값으로 덮어쓸 수 있다. 따라서 출력이 되지 않는 것이다. this.id는 Mongoose가 문서의 ObjectId를 나타내는 기본적인 방식인데, 따라서 가상필드에서 this.id를 사용하면 현재 문서의 ObjectId를 가져와 _id필드로 반환하며 이로인해 _id가 출력되게 된다.
'Web' 카테고리의 다른 글
[WebRTC] mediasoup을 사용한 SFU 영상 통화 미디어 서버 구현 (24.10.01 업데이트) (0) | 2024.03.21 |
---|---|
[NestJs/트러블슈팅] mongoDB document 반환 (0) | 2023.11.20 |
[SERVER] 웹 서버와 WAS 그리고 DB (0) | 2023.03.14 |
[좋은 웹 API 디자인] 사용하기 좋은 API 디자인 (0) | 2023.03.03 |
[좋은 웹 API 디자인] API 디자인 기초 (0) | 2023.03.01 |