이름도 어려운 Transaction

이예음·2022년 10월 21일
0
post-thumbnail

왜 트랜잭션을 사용하는거지?

결제가 완료됐으면, 프론트엔드에서 결제된 데이터를 요청 받은 경우, 백엔드는 당연히 이 데이터를 DB에 저장해야한다.
이때 결제정보를 저장할 뿐만 아니라, 동시에 사용자가 결제한 금액을 누적 결제금액 컬럼에 넣어 최신화 시켜줘야한다.
한마디로 결제는 한번으로 한 프로세스가 여러 가지 일을 하게 된다는 것이다.
그런데 결제정보는 저장되었는데 중간에 에러가 생겨서 로직이 끝나버렸다면, 결제정보만 저장되고 누적금액은 최신화가 되지 않아버린다. 이런걸 데이터 오염이 발생했다고 한다.
그래서 이런 문제를 해결하기 위해서 ACID 트랜잭션을 사용한다.

Transaction이란

: 처리되는 작업의 단위
데이터베이스에서의 Transaction 처리는 Business Logic 상 굉장히 중요한 기능이다.
따라서, 서로 다른 트랜잭션들을 처리하는 도중 하나의 단위 트랜잭션에서 에러가 발생한다면 이전에 성공했던 트랜잭션들을 다시 rollback 시켜 데이터의 Consistency가 깨지지 않도록 해주는 것입니다. 모두 성공했을 경우에는 commit을 통해 확정 지어주게 된다.

DB의 Transaction Flow

출처

TypeOrm Transaction Strategies

TypeOrm의 Transaction을 처리하기 위한 다양한 전략이 존재한다. 간단하게 @Transactional 데코레이터를 사용하여 해당 Method 위에서 간편하게 처리할 수도 있고, Callback Style로 처리할 수도 있다.

NestJS에서 강력하게 추천하는 방식은 바로 QueryRunner를 통해 Transaction을 수행하는 것이다.
QueryRunner를 사용하면 Transaction의 Commit과 Rollback을 수동으로 제어할 수 있다.
Unit Testing(단위 테스트)를 보다 쉽게 진행할 수 있다. 즉 Jest를 통한 Testing 시 Mocking을 좀 더 쉽게 할 수 있다.

Transaction의 속성들(ACID)

  • A(Atomicity) : 원자성 → 안전성 보장을 위해 가져야 할 성질 중의 하나로 트랜잭션과 관련된 작업들이 부분적으로 실행되다가 중단되지 않는 것을 보장하는 능력.
    • 모두 성공할 것 아니면 모두 실패하게 만드는 것(DB의 오염을 막기 위함).
  • C(Consistency) : 일관성 → 트랜잭션이 실행을 성공적으로 완료하면 언제나 일관성 있는 데이터베이스 상태로 유지하는 것.
    • 똑같은 쿼리를 조회할 때마다 동일한 결과값이 나타나야하는 것.
    • 수정과 삭제로 인해 결과값이 달라지는 것은 당연함.
  • I(Isolation) : 격리성 → 트랜잭션을 수행 시 다른 트랜잭션의 연산 작업이 끼어들지 못하도록 보장하는 것.
    • A 사람의 요청을 처리하는 동안 B사람의 요청은 잠시 기다리는 것
  • D(Durability) : 지속성 → 성공적으로 수행된 트랜잭션에 대한 로그가 남아야하는 성질로 런타임 오류나 시스템 오류가 발생하더라도, 해당 기록은 영구적이어야 하는 것.
    • 성공하여 commit이 되었으면 서버를 다시 켜도 그 데이터는 그대로 유지가 되어야 되는 것.

Transaction의 Commit, Rollback

  • Repository는 @InjectRepository 데코레이터로 의존성 주입을 하지만, Connection 객체는 이미 TypeOrm Module을 import한 것만으로도 의존성을 가져올 수 있다.
    이 의존성을 토대로 createQueryRunner() 함수를 통해 Transaction Manager를 수행할 수 있다.
  • createQueryRunner함수로 queryRunner를 선언하고, Transaction의 시작을 선언해준다.. Commit, Rollback을 수동으로 제어할 수 있듯이 Transaction의 시작과 끝 또한 제어할 수 있다.
  • 전체 로직을 try-catch-finally 로 감싸주고 Transaction을 처리하는 save method는 repository가 아니라 queryRunner.manager로 대체해 준다.
  • Error 없이 모든 로직을 수행하면 Transaction이 완료되어 commitTransactioin을 호출하고, finally에서 release함수를 호출해 Transaction을 종료한다. 만약 중간에 Error가 발생했을 경우 catch에서 잡아내서 rollback을 수행한다.
profile
응애

0개의 댓글