[Spring] 트랜젝션

유형찬·2022년 11월 9일
0

Code States

목록 보기
18/21

트랜젝션 이란

트랜젝션은 데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

예를 들어서 계좌 이체를 하는 경우에는 출금과 입금이라는 두 가지의 작업이 필요하다.

이 두 가지의 작업은 반드시 한꺼번에 수행되어야 하며,

둘 중 하나라도 실패하면 이체는 취소되어야 한다. 이러한 작업의 단위를 트랜젝션이라고 한다.

트랜젝션의 특징 ACID

원자성

모든 작업이 정상적으로 수행되었을 때만 데이터베이스의 상태가 변화되어야 한다.

실패 했을 때는 모든 작업이 취소되어야 한다.

일관성

트랜젝션이 정상적으로 종료 될 경우 데이터는 비지니스 규칙을 만족하는 일관된 상태로 유지되어야 한다.

예를 들어서 Expected 한 결과로 커피를 3잔 주문 했으면 쿠폰이 3개 발급되어야 한다. 4개면 안된다.

고립성

트랜젝션은 동시에 실행되는 다른 트랜젝션과 격리되어야 한다.

이 뜻은 한 트랜젝션이 실행되는 동안에는 다른 트랜젝션이 해당 트랜젝션의 연산에 영향을 미치지 못한다는 것이다.

이 고립성은 데이터 베이스의 특성에 따라서 다르게 적용된다. 간단하게 종류와 특징을 정리해보자.

Read Uncommitted

트랜젝션이 커밋되지 않은 데이터를 읽을 수 있다.

Read Committed

트랜젝션이 커밋된 데이터만 읽을 수 있다.

Repeatable Read

트랜젝션이 실행되는 동안에는 항상 같은 데이터를 읽을 수 있다.

Serializable

트랜젝션은 서로 독립적으로 실행되어야 한다.

지속성

트랜젝션이 성공적으로 완료되면 결과는 영구적으로 반영되어야 한다.

트랜젝션 커밋과 롤백

트랜젝션은 원자성을 가지기 때문에 트랜젝션의 작업이 모두 성공적으로 완료되면 커밋을 하고,

트랜젝션의 작업 중 하나라도 실패하면 롤백을 한다.

Spring Transaction

Spring Transaction은 트랜젝션을 관리하기 위한 추상화된 인터페이스를 제공한다.

트랜잭션은 상당히 복잡한 내용을 가지고 있지만 사용하는 것 자체는 어려운 일이 아니다.

Spring Transaction을 사용하면 트랜젝션을 관리하기 위한 코드를 직접 작성하지 않아도 된다.

일반적으로 비지니스 로직이 적용된 서비스 계층에서 트랜젝션을 관리한다.

예시를 한번 봐보자

@Service
@Transactional
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void save(User user) {
        userRepository.save(user);
    }
}

위 코드는 UserService 클래스에 @Transactional 어노테이션을 붙여서 트랜젝션을 관리한다.

이렇게 하면 UserService 클래스의 모든 메소드는 트랜젝션을 관리하게 된다.

일반적으로 트랜젝션을 관리하는 코드는 아래와 같다.

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public void save(User user) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            userRepository.save(user);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
        }
    }
}

에러 처리를 위해 try-catch를 사용하고, 트랜젝션을 커밋하거나 롤백하는 코드를 작성해야 한다.

그런데 굳이 이렇게 코드를 작성할 필요는 없다.

그리고 @Transactional 어노테이션의 적용 법위가 있는데 클래스에 적용하면 클래스의 모든 메소드에 트랜젝션을 적용하고,

메소드에 적용하면 해당 메소드에만 트랜젝션을 적용한다.

private 메소드는 트랜젝션을 적용할 수 없다.

private 메소드에 트랜젝션 적용안되는 이유

private 메소드에 트랜젝션을 적용할 수 없는 이유는 private 메소드는 프록시를 통해 호출되지 않기 때문이다.

Spring AOP는 프록시를 통해 메소드를 호출하기 때문에 private 메소드는 프록시를 통해 호출되지 않는다.

그래서 private 메소드에 트랜젝션을 적용할 수 없다.

private 메소드에 트랜젝션 적용하려면?

private 메소드에 트랜젝션을 적용하려면 public 메소드를 만들어서 private 메소드를 호출하면 된다.


@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void save(User user) {
        saveUser(user);
    }

    private void saveUser(User user) {
        userRepository.save(user);
    }
}
profile
rocoli에요

0개의 댓글