본문 바로가기
프로젝트 개발 기록/[개발] trouble shooting

[MongoDB] mongoose 트랜잭션

by HelloJudy 2022. 5. 28.

우선 트랜잭션에 대한 자세한 개념 공부 이후 실제 코드로 실습을 진행했다.

 

개념 포스팅 보러가기 👉[Database] 트랜잭션과 무결성·정합성, 면접 질문

트랜잭션 적용(좋아요 기능)한 프로젝트 보러가기 👉 GitHub Source

 


1. 사용 목적

'좋아요' 기능을 개발할 때 여러 유저가 동시에 좋아요를 누르는 경우가 발생한다.

이때 데이터 정합성 보장을 목적으로 모두 완벽하게 처리(Commit)하거나 또는 처리하지 못할 경우에는 원 상태로 복구(Rollback)해서 작업의 일부만 적용되는 현상이 발생하지 않게 구현하고자 했다.

 

 

2. 구현

 

index.js

const db = mongoose.connection;
export { db }

 

like.js - 좋아요 스키마

const likeSchema = new Schema({
  giveUserId: {
    type: Schema.Types.ObjectId,
    ref: "User",
  },
  getCocktailId: {
    type: Schema.Types.ObjectId,
    ref: "Cocktail",
  },
  name: {
    type: String,
  }
});

 

likeService.js - 서비스 계층

static addLike = async ({ giveUserId, getCocktailId }) => {
    const session = await db.startSession();

    try {
      session.startTransaction();

      const name = await CocktailModel.findById({ getCocktailId });

      const newLike = await LikeModel.addLike({
        name: name.name,
        giveUserId,
        getCocktailId,
      });

      await CocktailModel.likeCocktail({ getCocktailId });

      await session.commitTransaction();
      return newLike;
    } catch (e) {
      await session.abortTransaction();

      const errorMessage = "좋아요를 생성하는 데 실패했습니다.";
      return { errorMessage };
    } finally {
      session.endSession();
    }
  };

 

Transaction은 MongoDB sessions에 구성되어 있다고 한다.

그래서 다음과 같은 방식으로 구현하였다.

 

  1. db.startSession() 으로 호출한 다음
  2.  session.startTransaction() 으로 함수를 선언한다.
  3. 이때 try - catch 문을 사용하여
    1. '성공 = session.commitTransaction()'으로 모든 커밋을 저장
    2. '실패 = session.abortTransaction()'으로 트랜젝션내의 모든 작업을 rollback
    3. '최종 = session.endSession()'으로 세션 종료

 

초반에는 예외처리를 해주지 않아 불안정한 서비스였지만 리팩토링을 통해 예외처리를 추가해주었다.

 

 

사진 출처

 

 

 

JAVA에서의 트랜잭션 구현

@Service
@Transactional(readOnly = true)
public class MemberService {
    private final MemberRepository memberRepository;
    
    public MemberService(MemberRepository memberRepository) {
    	this.memberRepository = memberRepository;
    }

Spring 프레임워크에서는 @Transactional 애너테이션을 통해 트랜잭션 설정

 

 

 

3. 아쉬운 점

 

  • 현재 상태에서는 오버엔지니어링이라서 구현되어 있지 않지만
  • 차후 프로젝트 확장을 고려했을 때 파티셔닝이 필요하다고 생각했다.
  • 현재의 방식은 좋아요 하는 user_id와 좋아요를 받은 cocktail_id로 저장하고 있지만 인스타그램과 같은 서비스를 생각해보았을 때 이 방식은 확장성 있는 코드가 아니라고 판단된다.
  • 소규모 서비스를 만든다는 생각에서 벗어나서 좀 더 대규모 서비스를 초점으로 코드 구현이 필요할 것 같다.
  • 파티션 키를 유저로 잡고 구현이 필요하다!!

 

📌 Reference

 

반응형

댓글