본문 바로가기
백엔드

[Spring] @Transactional

by 코린이 프로도 2022. 11. 21.
반응형

발생한 문제


분명 테이블에 데이터를 저장 했는데, RollBack을 하더라.

 만들고자 하는 Spring Boot 서비스의 특징으로 로그를 남길 때 2가지 방식을 사용한다.

  1.  spring-logback을 이용해 서버에 파일 형태로 저장하는 방식.
  2.  DB에 로그 테이블을 만들어 API의 성공 실패에 대한 모든 정보를 저장하는 방식.

  JPA로 2번 방식을 개발하여 비즈니스 로직에서 Exception을 발생시켜 실패에 대한 데이터를 로그 테이블에 저장하는 테스트를 해보니 데이터가 저장되지 않는 것을 발견했다. console창에 출력된 로그를 자세히 살펴보니 저장된 데이터가 다시 롤백되고 있었다. 정확히는 JPA 인터페이스가 동작하는 서비스에서 Exception을 Aspect 클래스로 던져 로그에 저장하는 로직을 개발했는데, 이 저장된 부분이 롤백되고 있었다. 아무래도 @Transactional 어노테이션을 남발하여 Exception이 발생한 부분이 다음 로직까지 영향을 미쳐 롤백을 하는 듯 했다. 

 자세한 내용을 파악하기 위해서 JPA의 영속성의 개념과 @Transactional 어노테이션 사용법을 찾아보았다. 이번 글은 @Transaction 어노테이션을 살펴보겠다.

 

DB Transaction


 먼저 Transaction이 무엇인지 알아야 한다. 아래 페이지의 내용을 참고했다.

https://brunch.co.kr/@skeks463/27

 

[DataBase] DB를 지탱하는 트랜잭션

데이터 베이스는 우리가 흔히 사용하는 파일 시스템과는 달리 기본적으로 4가지 특징이 존재한다.   1) 실시간 접근성 2) 계속적인 변화 3) 동시 공유 4) 내용에 따른 참조  이외에도 디비의 장

brunch.co.kr

위에서 언급한 @Transactional 어노테이션은 DB의 Transaction 특징을 유지하는데 도움을 주는 기능을 한다.

트랜잭션 (Transaction) : DB 상태를 변화시키는 하나의 작업 단위를 말한다.

 

 예를 들어, DB에 save, update, delete하는 세 개의 메소드가 있다고 하자. 서비스 개발 로직에 따라 이 메소드 각각 하나씩을 트랜잭션으로 처리할 수 있고 이 중 2개 3개 동작을 연달아 수행하는 것이 하나의 트랜잭션이 될 수도 있는 것이다. 아무튼 DB관련 로직을 처리하는 하나의 작업 단위를 트랜잭션이라고 한다. 

 

 이 트랜잭션 내에서 테이블의 데이터 상태가 얽히고설켜 서비스에 잘못된 영향을 끼치면 안 되기 때문에 DB 트랜잭션 설계는 4가지 Transaction 특징을 고려해 설계한다.

트랜잭션 4가지 특징

  1. 원자성(Atomicity) : 트랜잭션이 DB에 모두 반영되거나 전혀 반영되지 않아야 한다.
  2. 일관성(Consistency) : 작업 처리 결과는 항상 일괸성 있어야 한다.
  3. 독립성(Isolation) : 두 개 이상의 트랜잭션이 있을 때, 서로 영향을 끼치면 안된다.
  4. 지속성(Durability) : 트랜잭션이 완료되면 그 결과는 지속되어야 한다.

트랜잭션의 연산

트랜잭션의 연산에는 두 가지가 있다.

  • commit
  • rollback 

트랜잭션이 시작한 후 commit 연산이 실행되는 시점에 DB에 반영되는 것이다.

rollback은 트랜잭션이 시작되고 로직을 수행하던 중 실패가 발생하면 이전에 수행한 로직을 되돌리는 연산이다. 

트랜잭션 상태

트랜잭션의 연산은 5가지의 상태 변화를 통해 수행한다.

  • active : 트랜잭션 시작한 상태
  • partially committed 마지막 연산이 실행된 직후의 상태 (commit 직전)
  • committed : 완료 상태로 DB에 모두 반영된 상태.
  • failed : 트랜잭션에 오류가 발생해 중단된 상태
  • aborted : 트랜잭션이 비정상적으로 종료되어 rollback된 상태. 

@Transactional 어노테이션


아래 페이지를 참고했다. 

https://kafcamus.tistory.com/30

 

@Transactional 어노테이션의 이해

나는 보통 서비스 코드에 @Transactional 어노테이션을 활용해준다. 그런데 사실 뜻도 잘 모르고 좋다고 그래서 쓴거라...지나고 보니 정확히 설명하기가 어려웠다. 그런고로, 해당 어노테이션의 작

kafcamus.tistory.com

 이 어노테이션을 클래스, 인터페이스,  메소드에 붙이는 경우 위에 정리한 '트랜잭션 상태 변화'에 맞춰 관리할 수 있게 도움을 준다. 클래스나 인터페이스에 어노테이션을 붙이는 경우 클래스에 포함된 모든 메소드에 @Transactional 기능이 적용되는 것이고, 메소드에 붙이는 경우에는 해당 메소드만 @Transactional 기능이 적용된다. 이는 직접 객체를 만들 필요 없이 선언만으로 동작하기 때문에 '선언적 트랜잭션'이라고도 한다.

 위에서 참고한 @Transactional 어노테이션의 작동 원리와 흐름을 이해하기 위해선 '프록시 패턴'이 무엇인지 이해해야 할 필요가 있다. 이것까지 다루기엔 내용이 너무 많아서 다음을 기약해본다...

 알아야 할 내용은 @Transactional 어노테이션이 붙은 메소드가 실행되기 직전에 Spring에서는 해당 메서드에 대한 프록시를 만든다는 것이다. 그리고 이 프록시를 생성해 해당 메서드의 앞, 뒤에 트랜잭션의 시작과 끝을 추가하는 것이다. 이러한 로직이 바로 AOP라 할 수 있다.

이런식으로 트랜잭션의 앞, 뒤에 트랜잭션 AOP가 추가되면서 영속성 컨텍스트 전략을 수행할 수 있게 된다. 시작 부분에서  영속성 컨텍스트 생겨나고 메소드가 종료되면서 트랜잭션 AOP의 뒷 부분에서는 트랜잭션을 커밋하고 영속성 컨텍스트가 flush되어 해당 내용이 반영되는 것이다. 이후 영속성 컨텍스트 역시 종료된다. 

 여기서 말하는 '영속성 컨텍스트'는 간단히 말해서 애플리케이션과 DB 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 다시 말하면 아직 DB에 저장되기 전 단계라고 할 수 있다. 이것도 추후에 다뤄보자...

 결론


 내가 설계한 DB에 로그 형식으로 데이터를 저장하는 로직은 AOP로 설계되었다. 위에 보이는 그림과 같이 @Transactional 어노테이션이 붙은 메소드는 시작 전 트랜잭션 AOP를 통해 이미 트랜잭션이 시작된 셈이다.

 따라서 메소드 동작 중에 에러가 발생하여 예외를 던지고 해당 예외를 또 다시 AOP에서 잡아 DB에 저장하는 것은 모두 같은 트랜잭션 내 동작이라고 할 수 있다. 그래서 DB에 저장하더라도 같은 트랜잭션에서 에러가 발생한 것이기 때문에 롤백 처리를 한 것 같다. 

숙제

  1. 해결책 찾아오기
  2. 프록시 패턴 알아보기
  3. 영속성 컨텍스트 알아보기
  4. @Transactional 속성 값 알아보기

 

반응형

'백엔드' 카테고리의 다른 글

[Spring] static method VS bean  (0) 2022.12.31
[Spring] 싱글톤 컨테이너  (0) 2022.12.21
[Spring] Bean  (0) 2022.12.14