Transaction
스프링 프레임워크(Spring Framework)로 개발하면서 @Transactional
어노테이션(Annotation)을 본 적이 있다. 그때는 간단하게 이 트랜잭션(Transaction) 안에서 에러가 발생하면 커밋(Commit)하지 않고 롤백(Rollback)한다고 이해하고 넘어갔다. 너무 늦었지만, 이 트랜잭션에 대해서 정리해보자.
왜 Transaction을 알아야 할까?
은행에서 이체를 한 이후, 내 계좌에서는 돈이 빠졌는데 상대방 계좌에는 돈이 들어오지 않았다?
계좌에 돈을 입금한 다음 며칠 후에 잔액을 봤더니 입금 내역이 은행 시스템 장애로 사라져있다?
위의 예는 실제 경험해보지 못했지만 발생한다면 심각한 장애다. 특정 비즈니스에서는 데이터를 다룰 때 이러한 장애 상황을 만들지 않아야한다. 이때 우리는 트랜잭션을 사용한다. 트랜잭션은 Atomicity, Consistency, Isolation, and Durability(ACID)를 만족해야한다. 이 성질을 만족해야만 위의 장애 상황을 피할 수 있다.
원자성(Atomicity)
All or Nothing
으로 생각하면 된다. 이체를 할 때, 계좌의 잔액을 확인하고 일정 금액을 빼서 다른 계좌로 넣고 잔액을 갱신한다. 이체라는 하나의 논리적인 작업에 다수의 데이터 작업이 발생한다. 트랜잭션의 원자성은 이러한 다수의 데이터 작업이 모두 반영되거나 아니면 하나도 반영되지 않아야 한다.
일관성(Consistency)
일관성은 트랜잭션 전후의 데이터베이스의 상태는 일관해야 한다. 데이터베이스에서 제약 사항이 있다면 트랜잭션 후에도 이 제약 사항이 반드시 지켜진 상태여야 한다는 뜻이다. 예를 들어 상품 테이블이 있고 상품 테이블을 외래키로 참조하는 주문 테이블이 있다고 생각하자. 그리고 주문 데이터는 반드시 하나 이상의 상품 데이터를 참조해야 한다. 트랜잭션 후에도 주문 데이터는 반드시 하나 이상의 상품 데이터를 참조하고 있어야만 한다. 만약 상품 데이터를 참조하지 않고 있는 주문 데이터가 있다면 일관성이 깨진 것이다.
고립성(Isolation)
고립성은 트랜잭션은 서로 다른 트랜잭션과 고립된 상태여야 한다. 하나의 트랜잭션이 다른 트랜잭션에 영향을 주지 않아야 한다. 고립된 상태의 트랜잭션은 다른 트랜잭션에서 일어난 일에 대해서는 알지 못하고 영향을 주지도 않아야 한다. 이 고립 수준에 따라 트랜잭션 사이의 영향이 서로 다른데 더 자세한 내용은 고립성의 레벨에 따라 이해해야 한다. Transaction: Isolation Level에서 정리된 내용르 확인하자.
지속성(Durability)
지속성은 트랜잭션을 통한 작업이 계속 유지되어야 한다. 데이터베이스에 대한 예기치 못한 에러로 인해 시스템이 다운되었다면 앞서 진행했던 사항에 대해 복구를 해야한다. 지속성은 트랜잭션에 대한 로그를 남겨 시스템이 복구되었을 때, 데이터도 복구해야 한다. 예를 들어 은행 업무 중 입금에 대한 트랜잭션이 일어났지만 데이터베이스가 다운되었을 때를 생각해보자. 데이터베이스가 다시 살아났을 때 앞서 일어난 입금 업무에 대한 결과가 복구되어야 한다.