데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      mongoDB Session 사용하기

      너구리킹 24.07.10
      1,352 4 1
      DEVOTEE 요약
      MongoDB에서는 RDB와 달리 atomic성을 보장하기 위해 session을 사용해야 하며, session 기능은 replica set 환경에서만 동작한다. Docker를 이용해 replica set을 구성한 후, nestjs를 사용하여 session을 시작하고 DB 작업을 수행한 다음 commit 또는 abort하여 모든 작업을 관리할 수 있다. 이를 통해 여러 컬렉션을 atomic하게 다룰 수 있으며, 동일한 DB 내에서만 session 사용이 가능하다.

      mongoDB Session

      RDB같은 경우에는 atomic성이 항상 보존되기 때문에 이에 대한 생각을 할 필요가 없지만,

      mongoDB는 그렇지 않기 때문에 일련의 과정으로 처리해야할 경우 session을 사용해서 이를 보장해야 한다. (mongodb 4.0 version 부터 사용 가능하다.)

      session 기능은 standalone에서는 동작하지 않기 때문에 replica set 구성을 해 주어야 한다.


      예시를 작성할 때 nestjs를 사용하여 작성하였고 동작 방법에 대해 간단히 요약해서 말하자면 아래와 같은 순서로 동작한다고 볼 수 있다.

      1. session을 시작한다.

      2. DB에 접근할 때 session 정보를 같이 전달한다.

      3. 모든 session이 정상적으로 완료되었으면 commit을 한다.

      4. 만약 중간에 에러가 발생하면 commit을 하지 않고 session을 abort 시킨다.

      5. session을 종료한다.


      local 환경해서 테스트 해보기

      docker로 mongoDB replica Set 띄우기

      우선 session기능을 사용하기 위해서는 replica set 구조가 되어있어야 하기 때문에 이를 먼저 적용한다.

      docker는 이미 사용하고 있다고 가정하고 docker-compose를 이용해서 replica set을 구성하도록 하겠다.

      본인의 경우 27017,27018,27019 port로 3개의 db를 띄워서 사용하였다.

      version: '3'
      services:
        mongo1:
          image: mongo:5.0
          command: --replSet replDb --bind_ip_all --port 27017
          volumes:
            - /Users/ji-hyeonyu/dev/test_db:/data/db 
            # 본인이 사용할 경로로 지정
          ports:
            - 27017:27017
          healthcheck:
            test: test $$(mongosh --port 27017 --quiet --eval "rs.initiate({_id:\"replDb\",members:[{_id:0,host:\"mongo1:27017\"},{_id:1,host:\"mongo2:27018\"},{_id:2,host:\"mongo3:27019\"}]}).ok || rs.status().ok") -eq 1
            interval: 20s
            timeout: 10s
            retries: 6
      
        mongo2:
          image: mongo:5.0
          command: --replSet replDb --bind_ip_all --port 27018
          volumes:
            - /Users/ji-hyeonyu/dev/test_db1:/data/db
            # 본인이 사용할 경로로 지정
          ports:
            - 27018:27018
      
        mongo3:
          image: mongo:5.0
          command: --replSet replDb --bind_ip_all --port 27019
          volumes:
            - /Users/ji-hyeonyu/dev/test_db2:/data/db
            # 본인이 사용할 경로로 지정
          ports:
            - 27019:27019

      이제 docker-compose up -d를 하면 mongodb 5.0 version을 다운로드하고 실행한 모습을 볼 수 있다.

      image.png

      이처럼 docker app에서 잘 보인다면 cmd 창에서 제대로 동작하는지를 확인해보자

      image.png

      docker ps 명령어를 이용한다.

      이제 replica set으로 잘 동작하고 있는지 확인해 보자.


      docker exec -it docker_mongo1_1 /bin/bash

      위 명령어를 통해서 container 안으로 접근한다.

      image.png


      mongo --port 27017을 통해서 mongo app을 접근한다. PRIMARY> 라고 보이면 정상적으로 실행 중임을 확인할 수 있다.


      rs.status() 명령어를 통해서도 확인할 수 있다.


      이제 mongoDB는 켜져있으나 실제로 접근은 알 될 것이다.

      왜냐하면 아래의 members 정보로 initialize 가 되어 있는데 mongo1, mongo2, mongo3의 host 정보를 모르기 때문이다.

      (dns mapping 과정에 따라서 /etc/hosts file에 정의를 하면 된다. dns lookup 과정을 참조하면 된다.)

      [{_id:0,host:\"mongo1:27017\"},{_id:1,host:\"mongo2:27018\"}ㅣ,{_id:2,host:\"mongo3:27019\"}]

      이를 해결하기 위해서 /etc/hosts 파일을 수정해주자.


      sudo vi /etc/hosts

      ##
      # Host Database
      #
      # localhost is used to configure the loopback interface
      # when the system is booting.  Do not change this entry.
      ##
      127.0.0.1       localhost
      255.255.255.255 broadcasthost
      ::1             localhost
      
      127.0.0.1 mongo1
      127.0.0.1 mongo2
      127.0.0.1 mongo3
      
      # Added by Docker Desktop
      # To allow the same kube context to work on the host and the container:
      127.0.0.1 kubernetes.docker.internal
      # End of section

      이렇게 mongo1, mongo2, mongo3에 대한 host를 정의해준다. 이제 제대로 연결이 된다.


      Session 사용해서 여러 collection atomic하게 다루기 (nest js)

      nestjs를 사용해서 작성했고 아마 다른 언어를 쓴다고 해도 크게 차이가 없을 거라고 생각한다.

      예시 코드는 예시 git 에 올려놓았다. stock인 이유는 복기하기 위한 저장소를 만드려고 했는데 그냥 예시로 쓰려고 한다.

      #src/post/post.service.ts
      import { Injectable } from '@nestjs/common';
      import { InjectModel } from '@nestjs/mongoose';
      import { Model } from 'mongoose';
      import { PostRepository } from 'src/post/post.repository';
      import { Posts } from 'src/post/post.schema';
      import { UserRepository } from 'src/user/user.repository';
      import { User } from 'src/user/user.schema';
      import { CreatePostDto } from './dto/create-post.dto';
      import { UpdatePostDto } from './dto/update-post.dto';
      
      @Injectable()
      export class PostsService {
        constructor(
          private readonly userRepo: UserRepository<User>,
          private readonly postRepo: PostRepository<Posts>,
        ) { }
      
        async create(input: CreatePostDto) {
          //session 을 연다.
          const session = await this.userRepo.getSession();
          session.startTransaction();
          try {
            // post를 create하면 user의 total post갯수를 증가시킨다.
            const user = await this.userRepo.update(input.author, { $inc: { totalPosts: 1 } }, session);
            // post를 생성한다.
            const post = await this.postRepo.create({ title: input.title, content: input.content, author: user._id, date: new Date(), index: user.totalPosts }, session);
            // 정상적으로 끝나면 session을 commit하여 정상적임을 알린다.
            await session.commitTransaction();
            return post;
          } catch (error) {
            //중간에 error 발생 시 session을 abort한다.
            await session.abortTransaction();
            throw error;
          } finally {
            // 종료 시 session을 닫는다.
            await session.endSession();
          }
        }
      
        async update(id: string, input: UpdatePostDto) {
          return this.postRepo.update(id, input);
        }
      
        async delete(id: string) {
          return this.postRepo.delete(id);
        }
      }

      위와 같이 사용하였다. 이제 create는 두개의 collection을 atomic하게 사용할 수 있다. 실제로 error를 주입하여 사용해 보면 abort 발생 시 다 롤백되는 것을 확인할 수 있다.

      이제 mongoDB 사용 시 유지 보수를 훨씬 더 간편하게 해주고 DB 정합성을 신경쓰지 않아도 된다.


      주의할 점

      • 같은 DB를 사용해야 session 사용이 가능하다. replica set으로 묶여있는 DB는 괜찮지만,

        아예 다른 mongoDB 두개를 상용하려고 하면 DB가 관리하는 session 정보가 맞지 않기 때문에 에러가 발생한다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      너구리킹 님의 최신 블로그

      더보기
      동영상 기고하기