포시코딩

1월19일 - Sequelize Transaction 본문

TIL

1월19일 - Sequelize Transaction

포시 2023. 1. 19. 18:36
728x90

Sequelize Transaction 다루기

팀스터디 중에 트랜잭션 관련해서 추가로 나한테 주어진 과제가 있었다.

  • repository에서 구현한 transaction 로직을 각각의 repo method를 구분지어 service에서 구현할 수 없을까
  • Error message의 성격별로 status code를 구분지어 리턴하는 방법
  • 트랜잭션 level? 전파?에 대해 알아보고 적용하는 방법

 

repo가 아닌 service에서 transaction 구현

일단 기존에 repo에서 성공적으로 transactiond을 구현했기 때문에 

바로 떠오르는 아이디어가 있었다.

repo에서 했던대로 service에서 transaction 객체를 호출한 뒤 repo method를 호출할 때 

파라미터로 넘겨 transaction 안에 포함시켜 버리는 것이었다.

 

변경 후 코드

orders.repository.js

createOrder = async (transaction, { ownerId, kinds, details, pickup, imageUrl }) => {
    await this.ordersModel.create(
      { 
        ownerId, kinds, details, pickup, imageUrl 
      }, 
      { transaction }
    );
  };

  deductPoint = async (transaction, ownerId, transferPoint) => {
    const userInfo = await this.usersModel.findOne(
      {
        attributes: ['id', 'point'],
        where: { id: ownerId },
      },
      { transaction }
    );

    if (!userInfo) {
      const err = new Error('유저가 존재하지 않습니다.');
      throw err;
    }

    userInfo.point -= transferPoint;
    if (userInfo.point < 0) {
      throw new Error('유저의 포인트가 부족합니다.');
    }
    await userInfo.save({ transaction });
  };

orders.service.js

const { sequelize } = require('../sequelize/models/index');

// ...생략
createOrder = async (ownerId, kinds, details, pickup, imageUrl) => {
    const transaction = await sequelize.transaction();
    try {
      // ...생략

      await this.ordersRepository.createOrder(transaction, { ownerId, kinds, details, pickup, imageUrl });
      const transferPoint = parseInt(process.env.ORDER_PRICE);
      await this.ordersRepository.deductPoint(transaction, ownerId, transferPoint);

      await transaction.commit();

      SocketManager.alertNewOrder();
      return { code: 201, message: '주문에 성공하였습니다.' };
      
    } catch (err) {
      await transaction.rollback();
      return { code: 403, message: err.message };
    }
  };

예상했던대로 잘 작동하는걸 볼 수 있었다. 해결

 

상황별 Error에 따른 Status code 구분짓기

현재 코드에서 전달하는 Error.message를 어떤식으로 구분지어야할지 감이 안잡히고 있었는데

다른 팀의 프로젝트 코드를 리뷰하다가 아이디어를 얻었다.

new Error(); 로 생성한 객체의 멤버 변수 message처럼 name에도 똑같이 string을 부여할 수 있었다.

 

즉시 테스트 

 

orders.repository.js

// ...생략
if (userInfo.point < 0) {
  const err = new Error('유저의 포인트가 부족합니다.');
  
  console.log('repo:: ');
  console.log(err.name);
  
  err.name = 'notEnoughPointError'
  console.log(err.name);
  
  throw err
}
// ...생략

orders.service.js

// ...생략
} catch (err) {
  await transaction.rollback();
  
  console.log('service:: ');
  console.log(err.name);
  
  return { code: 403, message: err.message };
}

console

 

repo에서 직접 부여한 Error.name을 service에서 전달받을 수 있다는게 확인되었음으로

Error.name에 대해 if문으로 status code를 구분 지을 수 있음을 알 수 있다. 이것도 해결

 

Propagation(전파 속성), Isolation Level(격리 수준 레벨)

Transaction level에 대해 한국말로는 전파라고 명칭하는줄 알았는데 

좀 찾아보니 아예 다른 개념이었다.

 

Isolation(격리 수준 레벨)

Sequelize의 Isolation은 일반적인 SQL의 Isolation과 동일하게 동작한다.

SQL의 Isolation 격리 수준 속성은 다음과 같다.

= Transaction에서 일관성 없는 데이털르 허용하는 수준

 

Isolation Level은 네종류가 있다.

  • Read Uncommitted
  • Read Commited
  • Repeatable Read
  • Serializable

더 자세한건 아래 링크 참고

https://developyo.tistory.com/236?category=688590 

 

데이터베이스 격리 수준 : Database Isolation level

1. 데이터베이스 Isolation Level 이란 트랜잭션에서 일관성 없는 데이터를 허용하는 수준 2. Isolation Level 의 종류 - Read Uncommitted - Read Commited - Repeatable Read - Serializable Read Uncommitted -> Serializable 로 갈 수

developyo.tistory.com

 

Propagation(전파 속성)

여러 트랜잭션의 적용 범위를 묶어 커다란 하나의 트랜잭션 경계를 만들거나 할 수 있는데, 

해당 트랜잭션에 대해 어떻게 진행시킬지를 전파 속성을 통해 결정한다.

 

transaction propagation 관련 검색을 하면 죄다 Spring에 대해서만 나오고

정작 Sequelize 공식 문서에도 Isolation Level 말고는 나오질 않아 

일단 이정도 알게 되는 선에서 그치고

마침 따로 DB 관련해서 튜터님이 알려주시는게 있기 때문에 나중에 물어보기로 했다.

 

마치며

그냥 trasaction을 적용하기 까지는 너무 쉽다 생각했는데

역시 그에 관련해서 더 여러가지 고려해야될게 많다는걸 알게 되었고

심화 개념으로 갈수록 정보를 얻기도, 그 옵션 중 어떤걸 적용할지 어떻게 적용할지 모두 어려워지는걸 느꼈다.

그래도 DB 종류가 다름에도 전파 속성과 격리 수준 레벨에 대한 개념은 모두 비슷비슷하게 가지고 있기 때문에

검색해보면 관련 정보들을 얻기 수월해서 어느정도의 기본 개념은 잡게 된 것 같다.

 

 

 

 

참고한 곳

https://developyo.tistory.com/250

 

@Transactional Propagation (전파속성), Isolation (격리수준레벨) 그리고 synchronized

1. Transaction 설정방법 트랜잭션 설정 방법은 이곳을 참고. 2. Isolation SQL 의 Isolation level 과 동일하게 동작 (SQL 격리수준 속성에 대한 자세한 내용은 이곳을 참고) - READ_UNCOMMITED : commit 되지 않은 데

developyo.tistory.com

https://velog.io/@denhur62/트랜잭션-격리-수준과-전파-수준-그리고-읽기-수준

 

트랜잭션 격리 수준과 전파 수준

오늘은 트랜잭션의 전파 수준과 격리 수준에 대해서 이야기해보고자 한다. 트랜잭션에서의 격리는 한 트랜잭션에서 데이터가 수정되는 과정이 다른 트랜잭션과는 독립적으로 진행되어야 한다

velog.io

https://velog.io/@myspy/Transaction-전파가-뭡니까

 

Transaction 전파가 뭡니까?

이전 포스팅에서 Transactional을 학습한 이후에 Transactional 전파에 대한 학습을 약속했기 때문에 Transactional 전파라는 주제로 추가학습을 진행하게 됐다. 어떤 트갠잭션이 동작중인 과정에서 다른

velog.io

https://nesoy.github.io/articles/2019-05/Database-Transaction-isolation

 

트랜잭션의 격리 수준(isolation Level)이란?

 

nesoy.github.io

728x90

'TIL' 카테고리의 다른 글

1월21일 - TypeScript 기본 세팅  (0) 2023.01.21
1월20일 - 오버로딩(overloading)  (0) 2023.01.20
1월18일 - reversed, map  (0) 2023.01.18
1월17일  (0) 2023.01.18
1월16일 - session, jwt, OAuth  (0) 2023.01.16