mongoose에서는 기본적으로 schema를 생성할 때 id와 _id 필드가 생성된다.
이 필드들에 대해 알아보자.
이때 _id 필드의 타입은 mongodb의 ObejctId(처럼 행동하는 것)이다.
model에서 document를 가져와 console log를 찍어보면 다음과 같다.
console.log(await model.findOne().exec());
/*
{
_id: new ObjectId("asdf");
...
}
*/
이 ObjectId 인스턴스는 .toString() 메서드를 통해 24자리 16진수 문자열을 얻을 수 있다.(.toHexString()도 있으나 toString에서 toHexString을 리턴하기 때문에 사실상 같은 값이며, 공식문서에서 toString을 권장함)
그런데 위 console.log 결과에서는 _id만 존재하고 id는 존재하지 않는다.
하지만 console.log((await model.findOne().exec().id);를 해보면 id가 string으로 id 값이 나오는데, 이는 id가 document 클래스의 필드가 아닌 Document.prototype.id로 정의되어 있기 때문에 클래스를 console.log 했을때는 표기되지 않았던 것이다. (prototype에 대한 것은 js 문서 참고)
typescript를 사용 중이라면, id의 타입을 확인해보면 id?: any로 뜬다. 경험적으로 항상 id는 string인데 진짜 항상 string일까? 그렇다면 왜 any로 떠서 불편하게 만드는 걸까?
일단 https://mongoosejs.com/docs/guide.html#id 에 따르면 id는 항상 string이 맞다.
any인 이유는 mongoose 깃허브 이슈에서 이유를 알 수 있다.
https://github.com/Automattic/mongoose/pull/9773
https://github.com/Automattic/mongoose/pull/10248
https://github.com/Automattic/mongoose/issues/13079
요약하자면, mongoose에서 자동으로 넣어주는 id는 string이지만, 그렇게 사용하지 않고, 임의로 다른 타입의 id를 지정하는 것도 허용되기 때문에 id가 number 일수도 있다.
또한 mongoose에서 document의 타입은 보통 HydratedDocument로 가져오는데 이 타입이 & 로 스키마 클래스와 document 타입을 합치고 있기 때문에, 그냥 any로 둘 수 밖에 없다고 한다. (document에서 {id?:string} 으로 해놨는데 schema에서 {id:number}를 쓰게 되면, &로 합쳐지면서 {id:never}가 되어버림)
우리가 직접 schema에서 다른 id를 선언하지 않고, 일반적으로 mongoose에서 넣어주는 id를 쓰는 경우에는 무조건 해당 schame로 생성된 model로부터 가져온 document의 id는 _id를 .toString() 한 string이 맞다. 그러나 여러 가능성 때문에 any로 선언될 수 밖에 없으니 우리가 id를 사용할때, as string을 붙여서 쓰면 된다.
async create(userId: Types.ObjectId) {
const response = await this.postModel.create({
author: userId,
});
return response.id as string;
}
https://mongoosejs.com/docs/guide.html#id
https://mongoosejs.com/docs/guide.html#_id
저번에 언급했던 이슈를 정리했네요!
이거 보니까 이해가 됩니다.
잘 읽고 배우고 가요~