MongoDB를 쓰면서 다시 고민한 순간들

chloe·2022년 11월 28일
0
post-thumbnail
post-custom-banner

이 포스팅은 MongoDB를 쓰면서 생각했던 일련의 과정들을 담고있습니다.

시작

Read 성능을 높혀보고자 MongoDB를 사용해서 DB Schema를 비정규화 하였다.
DB 선택 과정은 여기 포스팅을 작성했다.


1차 회고

3주차 화요일에 조원이 갑자기 이런 이야기를 한다. (프로젝트는 6주)

사용자나 채널에 대한 정보는 API server에서 MySQL로 관리하고, socket server는 채팅만 MongoDB를 연결해서 관리하면 어때요?

이날은 현업 DB 특강이 있는 날이었는데, 나는 7시 이후에 일정이 있어서 참여를 못했고 조원은 그 특강을 들은 상황이었다.
특강의 내용이 생각을 바꾼 것 같아 이유를 물었을 땐 다음과 같았다.

오늘 DB 특강 들었는데
NoSQL에서 Join에 대해 별로 안좋게 얘기하셔서
NoSQL 데이터베이스 모델링 공부해봤는데
하나의 NoSQL만으로 모든 데이터를 처리하는거 보다 RDBMS와 혼용하는게 좋을까 같아서생각해봤어요

MongoDB로 결정을 내리고 Document DB를 잘 활용할 수 있는 Schema를 1주일이상을 소모했고 monoogse 러닝커브랑 싸우던 그 뒤의 일주일이 다 없어질 수 있는 상황이었다.

일단 내가 특강을 듣지 못한 상태였기 때문에, Relation DB와 No SQL DB를 혼용해서 쓰는 사례가 있는지 찾아봤다.

혼용할 수 있긴한데 지금 프로젝트에서 더 좋은 방향인지는 와닿지 않았다.

내 의견을 요약하자면 다음과 같다.

DB 2개를 연결하고 이를 왔다갔다 하는 것이 과연 효율적인가?
특강에서 말씀해주신 No SQL JOIN의 단점이 우리가 작성한 스키마에도 적용되는 것인가? 우리는 비정규화로 인해 MongoDB 내부 Join 연산에 대한 로직이 없지 않는가?
지금 시점에서 모두 엎고 새로 ERD 작성 및 ORM을 익히기에는 시간이 부족하다.

사실 맨 처음 든 생각은 나에게 구체적으로 바꿔야하는 근거를 명확하게 제시하지 않고, 누군가 안좋다고하니 쓰지 말자로 해석되었다.

사실 둘 다 만들어보고 실험해보지 않는 이상, 뭐가 더 좋다를 판단하기 어려웠다.
어떤게 어떤 장점이 있나요? 라고 했을 때 이분법으로 딱 구분 지을 수 없었다.

RDB에서 indexing이 data 양이 적을 땐 비효율 적이다 라고 할 때 그 data가 어느정도인데? 를 대답할 수 없는 것 처럼..

내가 Risk assessment를 판단하긴 좀 그렇지만, 구현해야할 API나 Socket Event가 많아, ERD 다시 작성하고 MySQL에 맞는 ORM 찾아서 익히고 적용하면 한주가 다 지나 있을 것 같았다.
(화요일 저녁에 이야기했으니 수~목 Fully 투자하고 금요일은 데모 발표등의 일정으로 개발 시간이 부족했다.)

DB를 혼용하는 제안을 자신만의 생각을 가지고 알아본 다음 제안 한 것이었는데, 반대의견을 낸 것이 왠지 미얀해서 잠이 오지않았다.

나의 생각을 최대한 논리적으로 준비해서 다음날 설명을 하니 조원은 나의 의견에 동의를 해주었고, 이전 계획 대로 계속 진행하게 되었다.


2차 회고

User Schema는 다음과 같이 구성되어있다.

User {
  _id : ObjectId('asdfw2e23')
  followers : userId[]
  followings: userId[]
  communities : {  // 커뮤니티 object로
  	communityId : { // 커뮤니티 unique id를 key 값으로 하여
    	_id : string
    	channels : { // 내가 속한 커뮤니티에 참여하는 채널을 또 object 로 가지고
    		channelId : lastRead(Date) // 채널 unique id를 key, 마지막으로 읽은 시간을 value로 가진다.
    	}
    },
  }
}

발단

Object가 계속 중첩되어 있으니, Schema 구현하는데 시간이 걸렸다.
정규화를 깨면서 조원들과 이야기 했던게 있었다.

정합성은 Backend에서 다 챙기면되니까 괜찮아! 내가 꼼꼼하게 다 챙길게

전개

정합성 검사가 실제 비니지스 로직보다 더 길어졌다.. 에러가 발생했을 때 Rollback 하는 것도 다 챙겨줬다.
같이 Backend 담당하는 조원은 Rollback은 포기하고 에러 안생긴다는 믿음을 가지고 있었다. ㄱ..그거아니야
-> 나중에 조원이 Test를 하면서 정합성이 맞지 않게 Data를 넣어 에러가 몇 번 났었는데 발생원인 파악에 시간이 오래걸리는 것을 체감하고 결국 에러 핸들링의 중요성을 느낀 것 같았다.

위기

내가 이 포스팅을 써야겠다고 마음먹은 결정적 계기는 4주차 월요일에 발생했다.

  • 기능 : 사용자가 커뮤니티에 초대되면, Public으로 설정된 Channel에도 자동 참여되도록 해야한다.
  • 로직
    1. 커뮤니티 doc에 사용자를 추가한다.
    1. 커뮤니티가 가진 채널의 정보를 가져온다
    2. 채널 별로 private인지 public 인지 조사한다.
    3. public 채널은 사용자 doc에 추가한다.
    4. 사용자가 추가되면 채널 doc 에도 사용자를 채널 참여자로 추가한다.

이 비지니스 로직만 약 70 line이고 DB에 접근하는 부분은 최대한 줄여보고자 Promise.all과 reduce promise 조합에 아주 난리가 났다..

if (community && 'channels' in community) {
      // public channel 에 추가
      const getChannels = async () => {
        return await community.channels.reduce(async (acc, channelId) => {
          const result = await acc;
          const channel = await this.channelRepository.findOne({
            _id: channelId,
            isPrivate: false,
          });
          if (channel) {
            result[channelId] = now;
          }
          return Promise.resolve(result);
        }, Promise.resolve({}));
      };
      channels = await getChannels();
    }

    const newCommunity = makeCommunityObj(communityId, channels);
    await Promise.all(
      // 사용자 document 검증 (올바른 사용자인지, 해당 사용자가 이미 커뮤니티에 참여하고 있는건 아닌지)
      appendUsersToCommunityDto.users.map(async (user_id) => {
        const newParticipant = await this.userRepository.findById(user_id);
        if (!newParticipant) {
          throw new BadRequestException(
            `커뮤니티에 추가를 요청한 사용자 _id(${user_id})가 올바르지 않습니다.`,
          );
        } else if (IsUserInCommunity(newParticipant, communityId)) {
          throw new BadRequestException(`이미 커뮤니티에 추가된 사용자 입니다.`);
        }
      }),
    );

대단한 비지니스 로직이 있는 것도 아니고 그냥 Data를 가공하고 저장하는 것 뿐인데도 이렇게 많은 시간을 들여서 작성하는 것이 나에게 도움이 되는가에 대한 의문이 들었다.

느낀점

성능에만 초점을 두고 시작했기 때문에 MongoDB를 선택했지만, 실제로 현업에서는 훨씬 많은 Table과 각각의 관계 들을 고려한다면 정합성은 DBMS에 맡기고 우리는 기능 구현에 더 초점을 두는 것이 좋겠다는 생각이 와닿았다.

이론적으로 어떤 상황에서는 어떤 DB가 좋아요 을 외우기 보다, 실제로 DB를 사용하며 프로젝트 중간 중간 회고를 하면서 MongoDB는 Sub document를 사용해 빠른 탐색을 할 수 있는 장점이 좋고, MySQL은 Table 간의 관계를 명확히할 때 좋다는 장점을 확실하게 깨닫게되었다.

좋게 좋게 흘러가는 것 보다, 삐그덕 거리면서 굴러가는 것이 공부를 하는 입장에서 더 많이 배울 수 있었다.

참고
RDMBS와의 혼용 https://bcho.tistory.com/666

profile
삽질전문 아티스트
post-custom-banner

0개의 댓글