게시글, 리뷰에 좋아요 기능을 구현하다가 mongoose transaction이라는 것을 알게되어 사용했다.
기억하고 다음에도 쓰기 위해 기록해본다.
참고 :
https://velog.io/@moongq/mongoose-transaction
1. Mongoose Transaction
transaction은 복수개의 작업들이 모두 성공했을 경우 성공하게 되고, 하나라도 실패했을 경우 실패 처리한다.
MongoDB에서는 mongoose session으로 트랜잭션 처리가 가능하다. 이때 몽고의 버전은 5.2.0 이상이어야한다.
2. Express.js 코드
좋아요 기능을 구현하기 위해 mongoose transaction을 사용해보자.
LikeSchema.index({ userId: 1, postId: 1 }, { unique: true })
일단 중복을 방지하기 위해 좋아요 스키마에서 unique를 설정해준다.
const session = await mongoose.startSession()
session.startTransaction()
session을 startSession()으로 생성하고 startTransaction()으로 트랜잭션을 활성화한다.
try {
await this.Likes.create([{ userId, boardId }]).session(session)
await this.Board.findByIdAndUpdate(
boardId,
{ $inc: { likesCnt: 1 } },
).session(session)
await session.commitTransaction()
}
배열 형태로 userId, boardId를 저장해주고 .session(session)으로 세션을 설정해준다.
.session(session)가 붙지 않은 (세션 객체가 명시되지 않은) 쿼리는 트랜잭션과 무관하게 작동하기 때문에 반드시 설정해준다.
boardId를 가진 게시글에 좋아요 수를 1 증가 또는 감소 시켜준다. $inc가 그 역할을 한다.
.commitTransaction()으로 모든 작업을 커밋한다.
이제 이 코드를 리팩터링 해볼 것이다.
try {
// await this.Likes.create([{ userId, boardId }]).session(session)
// await this.Board.findByIdAndUpdate(boardId, {
// $inc: { likesCnt: 1 },
// }).session(session)
// await session.commitTransaction()
await session.withTransaction(async () => {
await this.Likes.create({ userId, boardId }).session(session)
await this.Board.updateOne(
{ _id: boardId },
{ $inc: { likesCnt: 1 } }
).session(session)
})
session.endSession()
return { success: true }
}
세션을 좀 더 편하게 쓰기 위해 .withTransaction()을 사용했다. withTransaction()은 startTransaction()과 commitTransaction()을 한 줄로 줄여준다. 따라서 위의 startTransaction() 부분도 주석처리 해준다.
추가로 findByIdandUpdate 대신 updateOne을 사용했다.
catch (err) {
await session.abortTransaction()
session.endSession()
if (err.code == 11000) {
throw { status: 400, message: "이미 처리된 좋아요" }
}
throw err
}
예외 처리도 마저 해준다. err.code == 11000 는 몽고에서 중복 키 에러가 발생했을 경우 나타나는 에러다.
전체 코드
try {
// await this.Likes.create([{ userId, boardId }]).session(session)
// await this.Board.findByIdAndUpdate(boardId, {
// $inc: { likesCnt: 1 },
// }).session(session)
// await session.commitTransaction()
await session.withTransaction(async () => {
await this.Likes.create({ userId, boardId }).session(session)
await this.Board.updateOne(
{ _id: boardId },
{ $inc: { likesCnt: 1 } }
).session(session)
})
session.endSession()
return { success: true }
} catch (err) {
await session.abortTransaction()
session.endSession()
if (err.code == 11000) {
throw { status: 400, message: "이미 처리된 좋아요" }
}
throw err
}
}
업그레이드 된 좋아요 기능 포스트!!
https://knhye.tistory.com/31

감사합니다 ヾ(⌐■_■)ノ
'Development > DB' 카테고리의 다른 글
| [JPA] 카테시안 곱 발견 후 해결 과정 (1) (0) | 2025.10.28 |
|---|---|
| [Express+MongoDB] 검색 기능 구현 (MongoDB Atlas Search Index) (0) | 2025.04.08 |