@Transactional은 어느 위치가 좋을까?
서론
Facade 패턴을 적용해서 리팩토링을 하던 중, org.hibernate.LazyInitializationException 에러가 발생했다.
구글링 해보니 아주 간단하게 문제를 찾고 해결할 수 있었는데, JPA에서 지연로딩을 하려면 항상 영속성 컨텍스트가 있어야 하는데 영속성 컨텍스트가 종료된 후에 지연로딩을 시도해서 발생한 오류였다.
해결 방법은 간단하다. 해당 객체를 조회하는 코드가 트랜잭션 안에서 동작하게 해주면 된다.
Facade 패턴을 적용하면서 새로 생긴 Facade 클래스에 @Transactional 어노테이션을 붙이지 않았었다. Service 레이어에서 이미 해당 메소드들을 @Transactional 어노테이션을 붙여주었기 때문에, Facade에서는 붙이지 않아도 된다고 생각했기 때문이다. 이미 하나의 메소드들이 트랜잭션안에 놓이게 했는데, 그럼 어떻게 해야할까?
아래의 세가지가 고민이 되었다.
- Facade 클래스에 있는 메소드에 Transactional
- Service 레이어에 Transactional
- 둘다 Transactional
사실 2번의 경우는 에러가 발생하기 때문에 제외지만, 어쨌든 이에 대한 결론을 짓기 위해서 우선 Transactional의 기능에 대해서 다시금 복습해보기로 했다.
트랜잭션이란?
우리가 데이터베이스에 연결하여 작업을 처리 할때, 하나의 작업단위로 묶여야 할 작업이 조금만 반영된다면 차라리 아예 적용되지 못하느니만 못한 상황이 발생할 수 있다.
예를 들어 돈을 송금하는 상황을 가정해보자!
돈을 송금하는 기능을 만들기 위해서는 대략적으로 아래의 2가지 작업이 필요하다고 할 수 있다.
실제로는 더 세밀한 작업이 필요하겠지만, 설명을 위해 간소화해서 설명해보도록 하자.
- 돈을 보내는 사람의 계좌에서 돈을 차감한다.
- 돈을 받는 사람의 계좌에 돈을 더한다.
만약, 위 두 작업이 하나만 이루어지면 어떻게 될까? 1번까지만 진행되고 어떠한 이유로 인해 작업이 제대로 이루어지지 않는다면, 보내는 사람의 계좌에서만 돈이 빠져나가고, 받는 사람의 계좌는 그대로 일 것 이다.
차라리 아예 아무일도 일어나지 않는 것과 위 같은 상황중 뭐가 더 해결하기 쉬울까??
당연히 아무것도 이루어지지 않는 것이 더 나은 상황일 것이다!
이처럼 하나의 작업으로 이루어져야할 작업 단위를 트랜잭션 이라고 하는데, 하나의 트랜잭션으로 묶어진 작업은 전체가 반영되거나, 전체가 취소된다. 따라서 위 상황에서도 아예 송금 자체가 취소되도록 도와준다.
@Transactional이란?
그럼 JPA의 @Transactional 어노테이션은 무엇을 하는 어노테이션일까?
JPA는 객체와 데이터베이스의 데이터를 맵핑해주기 때문에, 객체지향적으로 코딩을 하며 데이터베이스와 연결할 수 있도록 해준다.
따라서 우리가 Repository를 통해 데이터베이스에 접근하게 되는데, 이 과정에서 커넥션 객체를 가져오게 되고 이를 통해 데이터 베이스에 접근하게 된다.
따라서 데이터베이스 입장에서는 우리가 보낸 요청이 하나의 트랜잭션으로 묶여야 하는지, 아닌지 알 수 없으므로 @Transactional 어노테이션을 통해 이 작업이 하나의 작업단위라는걸 알려줘야, 해당 요청들을 하나의 커넥션 객체를 통해서 요청하게 된다.
이렇게 하면 데이터베이스에서 하나의 커넥션을 하나의 세션으로 인식하여 처리하기 때문에, 우리가 원하는 기능을 묶어 하나의 트랜잭션으로 처리할 수 있게 된다.
결론
사실 고민이 되서 구글링을 하며 찾아보다가 마땅히 명확한 대답이 안나오길래 다시 이론부터 복습해보자 해서 본건데, 답이 나왔다!
지금 내 프로젝트에서 하나의 트랜잭션으로 처리되기 위해 필요한 기능은 Facade에 있는 기능이다. 따라서 우선 Facade 클래스에 있는 메소드에 Transactional 어노테이션을 붙여주고, 혹시 나중에 Service에 있는 메소드들을 따로 사용하게 되면 그때 Service에 있는 메소드에 @Transactional을 붙여주기로 했다.
댓글남기기