본문으로 건너뛰기
  1. Posts/

Spring Framework 에서 트랜잭션 다루기 (1) - 트랜잭션의 개념

·3 분· loading · loading ·
Mwjeon
Techtopic Spring Springframework Transaction
InnoFactory
작성자
InnoFactory
스마트팩토리, 산업자동화, Digital Transformation, 디지털팩토리, PLM, ALM, Digital Manufacturing, Visualization, 3D CAD, Digital Twin, Big Data, IIoT 솔루션 전문업체
작성자
mwjeon
A developer of Innofactory

본 포스팅에서는 Spring Framework 에서의 트랜잭션 작동 원리와 관리 방식을 다루며, 특히 프로그래밍적으로 트랜잭션을 제어하는 방법과 ASTRA 에서의 적용 사례를 제공합니다.

트랜잭션의 개념
#

일반적인 개발용어로써 ***트랜잭션(transaction)***은 “쪼갤 수 없는 업무 처리의 최소 단위” 를 뜻합니다. 다시 말해 데이터 베이스의 상태를 변화시키는데 반드시 동시에 수행되어야 하는 작업들을 묶는 단위라고 볼 수 있습니다.

트랜잭션의 필요성
#

ASTRA 에서 사용자를 추가하는 과정에 빗대어 트랜잭션의 필요성을 설명하겠습니다.

화면상에서 사이트 관리자는 해당 사이트의 사용자를 추가할 수 있는데, ASTRA 내부적으로 “사용자 추가” 절차는 다음 3 단계를 거칩니다.

  1. 화면상에서 입력된 정보로 “사용자” 객체 생성
  2. 해당 사용자의 개인 작업 공간인 “개인 캐비넷” 생성
  3. 소속 그룹을 지정했을 경우 해당 그룹과의 “링크” 객체 생성

위 3 단계의 절차는 “사용자 추가” 라는 한 업무, 즉 트랜잭션에 묶여있으며, 두 번째 개인 캐비넷 생성 작업에서 임의의 런타임 예외가 발생했다고 가정해보겠습니다.

예외 발생으로 “사용자 추가” 업무는 즉시 중단되고, 세 번째 링크 객체 생성 작업은 수행되지 않습니다.

하지만 첫 번째 사용자 객체 생성 작업은 완료된 상태로, 만약 한 트랜잭션으로 묶여있지 않았다면 데이터베이스에 개인 캐비넷과 그룹 링크 데이터가 존재하지 않는 불완전한 사용자 데이터만 남아있을겁니다.

이 때 특정 업무가 수행되기 이전의 상태로 돌리는 것을 롤백(rollback) 이라고 하는데, “사용자 추가” 업무를 롤백하고자한다면 개발자가 수동으로 데이터베이스를 조작하거나 수행된 작업을 찾아 삭제해주는 코드를 실행해야할 것입니다.

하지만 한 트랜잭션으로 묶여있다면 위 “사용자 추가” 업무 3 단계 중 어느 부분에서 예외가 발생해도 작업의 롤백을 보장합니다.

트랜잭션 관리
#

Java 개발환경에서 개발자는 트랜잭션을 적용하기 위해 JDBC 커넥션을 가져와 각 업무의 단계마다 성공과 실패 여부에 따라 적절히 커밋(commit)롤백(rollback) 을 수행해야합니다.

하지만 Spring Framework 는 선언적 · 프로그래밍적 방식을 통해 손쉬운 트랜잭션 관리를 제공합니다.

선언적 트랜잭션 관리는 Spring Framework 의 강력한 AOP(Aspect Oriented Programming) 기능을 이용해 일련의 작업들을 트랜잭션으로 묶을 수 있습니다. 개발자는 단순히 Spring AOP 에서 제공하는 @Transactional 어노테이션을 트랜잭션으로 묶고자하는 메서드에 선언해주면 됩니다.

아래는 “사용자 추가” 작업을 @Transactional 어노테이션을 이용해 트랜잭션화시킨 예입니다.

@Transactional
public User createUser() {
	// 1. 사용자 객체 생성 작업
    // 2. 개인 캐비넷 생성 작업
    // 3. 그룹 링크 객체 생성 작업
}

Spring AOP 를 이용한 @Transactional 어노테이션 동작 원리와 동작 조건은 다음 포스팅에서 상세히 다루겠습니다.

프로그래밍적 트랜잭션 관리 는 Spring Framework 에서 제공하는 PlatformTransactionManager 구현체 또는 TransactionTemplate 클래스를 이용해 개발자가 상황에 따라 트랜잭션의 시작, 종료 시점을 임의로 지정할 수 있도록 합니다.

아래는 TransactionTemplate Bean 을 주입받아 “사용자 추가” 작업을 트랜잭션화시킨 예이며, “transactionTemplate” Bean 으로 등록되어 있는 상황을 가정합니다.

public User createUser() {
    TransactionTemplate template = SpringContext.getBean("transactionTemplate");
    template.execute(new TransactionCallbackWithoutResult() {
    	@Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            try {
                // 1. 사용자 객체 생성 작업
                // 2. 개인 캐비넷 생성 작업
                // 3. 그룹 링크 객체 생성 작업
            } catch (Exception e) {
                e.printStackTrace();
                status.setRollbackOnly();	// 작업 중 예외 발생시 롤백
            }
        }
    });
}

PlatformTransactionManager 구현체를 이용하는 방법도 있지만 Spring 개발팀은 TransactionTemplate 사용을 권장하고 있습니다.

클래스나 메서드 단위로 지정이 가능한 선언적 트랜잭션 관리 방식에 비해 이 방식은 개발자가 트랜잭션으로 묶을 작업을 자유롭게 지정하고, 특정 상황에서만 트랜잭션이 적용되도록하는 것도 가능합니다.

콜백 패턴을 작성하는 자세한 방식도 또한 다음 포스팅에서 상세히 다루겠습니다.

정리를 하면, 각 트랜잭션 관리 방식의 장단점은 다음과 같습니다.

  • 선언적 트랜잭션 관리
    • 어노테이션 선언만으로 간편한 트랜잭션 지정
    • 격리 수준(Isolation Level), 전파 방식(Propagation) 등과 관련된 다양한 옵션 제공
    • 클래스 또는 메서드 단위로만 지정 가능
  • 프로그래밍적 트랜잭션 관리
    • 개발자가 트랜잭션 적용 여부 또는 시/종점을 임의로 지정 가능
    • 개발자가 순수 비즈니스 로직에 벗어난 코드를 작성함으로써 Spring AOP 정책에 어긋남