- Transaction이란, 단어 그대로 보면 거래라는 뜻이다. 하지만, 개발에서도 비슷한 부분에서 쓰이긴 한다. 이것은 데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위를 뜻한다.
- 간단히 말해, 질의어(SQL)를 이용하여 데이터베이스를 접근 하는 것을 의미한다.
(SELECT, INSERT, DELETE, UPDATE)- 개발자가 하나의 트랜잭션 설계를 잘하는 것이 데이터를 다루는 것에 많은 이점이 있다.
- 서비스에서 가장 큰 문제는 데이터의 오염이라고 하는데, 에러가 나오는데 데이터가 계속 저장되면, 데이터베이스 안은 오염이 되어 어지럽기 일쑤다. 그래서 차라리 실패할거면 전체 다 실패하고, 다시 시도하는게 안전하다는게 트랜잭션의 취지이다.
- TypeOrm의 Transaction을 처리하기 위한 다양한 전략이 존재합니다. 간단하게
@Transactional
데코레이터를 사용하여 해당 Method 위에서 간편하게 처리할 수도 있고, Callback Style로 처리할 수도 있다.- NestJS에서 강력하게 추천하는 방식은 바로 QueryRunner를 통해 Transaction을 수행하는 것이다.
QueryRunner를 사용하면 Transaction의 Commit과 Rollback을 수동으로 제어할 수 있다.
Unit Testing(단위 테스트)를 보다 쉽게 진행할 수 있다. 즉 Jest를 통한 Testing 시 Mocking을 좀 더 쉽게 할 수 있다.
- 원자성(Atomicity): 모두 성공할거 아니면 다 실패하게 해줘!!(오염은 싫어!)
- 일관성(Consistency): 똑같은 쿼리는 조회할 때 마다 동일해야돼!
- 격리성(Isolation): 재형이꺼 처리하는 동안 짱구는 기다려줄래?!
- 지속성(Durability): 한번 성공했으면 장애가 발생했어도 살아있어야해!!
- 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
을 수행한다.
- READ UNCOMMITTED: 커밋되지 않은 것도 조회해주자.(더러운 읽기)
- READ COMMITTED: 커밋된 것만 조회하자
- REPEATABLE READ: Mysql에선 발생하지 않는다.
- SERIALIZABLE
- 서로 락이 걸려 오도가도 못하는 상태를 데드락이라고 한다. 저런식으로 개발 하면 안된다!! 그래서 순서있게 잘 정리해야한다.
- 업데이트 할때 누군가 또 업데이트를 하게 되면 꼬이게 된다. 그래서 락을 걸어야함.