plainToInstance()를 MongoDB와 사용할 때 _id 불일치 문제

윤학·2023년 8월 11일
0

MongoDB

목록 보기
3/4
post-thumbnail

문제상황

현재 프로젝트를 진행하면서 MongoDB를 이용하여 프로필에 관한 데이터를 아래와 같이 저장해두었다.

그리고 프론트엔드에서는 메인 페이지를 로딩할 때 아래와 같이 무한스크롤을 구현하였는데

무한스크롤을 구현할 때 No Offset방식에 대한 글을 읽게 되어 MongoDB도 B-tree구조를 사용하는 것으로 알고 있어 해당 방법을 적용해보았다.

테스트 데이터를 몇개 넣어두고 실험을 해보았었는데 첫 요청이후로 불러오질 못했다.

이유를 확인해보니 항상 반환된 document에 대해서 class-transformer의 plainToInstance()를 통해 인스턴스를 생성하는데 이때마다 해당 시점에서 새로운 ObjectId가 할당되고 있었기 때문이였다.

	/* 마지막 프로필 아이디보다 오래된 프로필들 받아오기 위해 */
    private ltProfileId(profileId: string): null | { _id: { $lt: ObjectId }} {
        if( !profileId ) return null;

        return { _id: { $lt: new ObjectId(profileId) } };
    }

위와 같이 이전 요청보다 더 오래된 프로필을 불러와 최상단에는 가장 최신의 프로필이 존재하도록 하려 했지만 계속해서 최신으로 카운터가 증가한 ObjectId가 반환되고 있어 이후 요청에서 받아올 수가 없었다.

실제로 _id 값이 불일치하는지 확인해 보자.

        it('plainToInstance()로 변환한 프로필 id가 기존 id와 같은지 Test', async () => {
            /* 새로운 id를 생성하면서 기본 값들과 함께 */
            const oldProfile = TestCaregiverProfile.default().build();
            const oldProfileId = oldProfile.getId();

            await caregiverProfileRepository.save(oldProfile);

            const afterProfile = await caregiverProfileRepository.findById(oldProfileId);
            const afterProfileId = afterProfile.getId();

            expect(oldProfileId).toBe(afterProfileId);

            await caregiverProfileRepository.delete(oldProfileId)
        })

caregiverProfileRepository의 findById() 메서드는 내부적으로 plainToInstance()를 수행하도록 되어있다.

	async findById(id: string): Promise<CaregiverProfile> {
        const findProfile = await this.mongodb
                                .collection<CaregiverProfile>(this.collectionName)
                                .findOne({ _id: new ObjectId(id) });
        
        return this.documentToInstance(findProfile);
    }
    
    /* 조회된 Document를 인스턴스로 변경 */
    private documentToInstance(document: WithId<CaregiverProfile> | Document): CaregiverProfile {
        return plainToInstance(CaregiverProfile, document);
    }

테스트를 돌리면 위와 같이 실제로 불일치하다는 것을 확인할 수 있는데 해당 문제는 찾아본 결과 이미 class-transformer의 github issue에 등록되어 있었다.

해결방법

해결법도 함께 나와있어 코드를 보면 변환을 할 때 해당 데코레이터가 달린 속성은 변환하지 않고 그대로의 값을 가져오는 것 같다.

위에 코드를 참고해서 _id 필드에 만든 데코레이터를 적용하고 난 이후에 똑같은 테스트코드를 실행해보면

기존의 _id값 그대로 유지되는 것을 확인할 수 있다.

참고

fix: _id changes when {_id: new ObjectId(id) } is passed

profile
해결한 문제는 그때 기록하자

1개의 댓글

comment-user-thumbnail
2023년 8월 11일

잘 읽었습니다. 좋은 정보 감사드립니다.

답글 달기