본문 바로가기
Backend

트랜잭션에 내가 오해하고 있던 것

by 지금갑시다 2024. 1. 11.

 
 
트랜잭션이란, 런타임의 코드에서 데이터베이스와 연결을 맺어 진행되고 있는 부분이 무조건! 성공하게 하고 싶거나, 성공하지 못한다면 마치 실행되기 이전의 상태로 아무런 변경이 없었다는듯이 돌아가게끔 보장하는 개념이다.
 
컴퓨터 공학에서는 이를 트랜잭션의 원자성이 보장됨이라 말한다.
 
하지만, 위의 뭔가 애매~~한 한국어의 늬앙스 덕에 트랜잭션안에서 일어나는 모든 코드의 변경이 원상태로 모두 돌아간다고 이해를 했다.
 
 
예를 들어, 아래는 트랜잭션 안에서 데이터를 변경하는 코드를 수도코드로 간략히 작성해보았다.

EntityManager em;

tx.begin();

try {
    // 데이터의 변경 로직
    Member member = new Member();
    member.setName("newName");
    em.persist(member);
    
    tx.commit();
} catch (Exception e) {
	tx.rollback();
} finally {
	em.close();
}

 
em.persist()를 하면서 member를 영속화 하고, tx.commit()시점에 데이터베이스에 값이 저장된다.
만약 try { } 안에서 예외가 발생한다면, catch { }로 들어가게 되며, tx.rollback()으로 값이 롤백되어 저장되지 않는 것이다.
 
롤백이라 함은 tx.begin() 시점부터 tx.commit()까지의 변경 내용을 데이터베이스에 반영해주지 않는 것이다.
 

여기서 뭔가 오해를 했는데, 아래의 코드를 보자

EntityManager em;

Member weirdMember = new Member();

weirdMember.setName("Imweird");
Bool exceptionRaised = false;

tx.begin();

try {
	
    // 데이터의 변경 로직
    Member member = new Member();
    member.setName("newName");
    em.persist(member);
    
    // weirdMember
    weirdMember.setName("changed");
    em.persist(weirdMember);
    throw new Exception();
    
} catch (Exception e) {
	exceptionRaised = true;
	tx.rollback();
} finally {
	if (!exceptionRaised) {
		tx.commit();    	
    }

	em.close();
}

 
트랜잭션 안에서 일부러 Exception을 발생했고, finally { } 엔 Exception이 발생하지 않았으면 커밋을 하도록 로직을 변경했다.
(잘못된 코드란 사실은 알지만, 내 이해가 잘못되었음을 구현해보기 위함이다)
 

이때, try { } 안의 weirdMember의 Name은 결과적으로 모든 코드의 동작이 끝났을때
처음의 Imweird일까 changed일까?

 
트랜잭션이 취소되었으니, try{ } 안에서 값을 바꿨어도 처음의 값이 되지 않을까?
하지만, 이를 프린트해보면

System.out.println("weirdMember = " + weirdMember.getName());
// changed 출력

 
changed가 출력된다.
 

트랜잭션은 데이터베이스를 롤백하지(값을 원상태로 돌려줄 뿐), 그 책임이 객체의 변경사항까지 미치는 것은 아니기 때문이다. 
 
이로써 알 수 있는 건,

트랜잭션이란, DB에서 동작하는 개념이지 객체의 값까지 관리하고 있는 것이 아니다!

 

이렇게 동작하는 데에는 몇가지 이유가 있다고 하는데,

'실용주의 관점에서 예외가 발생했을 때 객체에 어떤 값이 들어가서 실패한 것인지 확인이 가능하기 때문' 이라 한다.

 

추가로, 트랜잭션에 실패한 엔티티는 비즈니스 로직에 사용하면 안된다. 위의 이유와 같이 어떤 데이터를 넣었을 경우에 예외가 발생하는지 로그를 남기는 정도 가 좋다!! 즉 예시에서의 weirdMember 객체와 같은 사용은 좋지 않은 사용법이다!

 

 

따라서 내가 트랜잭션에 대해 한 오해인

트랜잭션이 시작되면 끝날 때까지의 코드는 모두 원상태로 되돌아 간다! 라는 것은 잘못된 이해이고

 

트랜잭션이 시작되면 끝날 때까지 트랜잭션 내의 데이터베이스 변경사항이 반영되거나 다 되지 않는다. 또한 이는 객체와는 상관이 없다. 가 맞는 이해이다


끘!!!!