본문 바로가기

강의 내용 정리/스프링부트와 JPA 활용 1

@Transactional 과 영속성 컨텍스트

@Transactional의 작동 원리와 흐름

그렇다면 @Transactional이 붙은 메서드를 호출할 경우, 우리 코드에는 어떤 일이 벌어질까?

 

@Transactional이 클래스 내지 메서드게 붙을 때, Spring은 해당 메서드에 대한 프록시를 만든다.

프록시 패턴은 디자인 패턴 중 하나로, 어떤 코드를 감싸면서 추가적인 연산을 수행하도록 강제하는 방법이다.

 

트랜잭션의 경우, 트랜잭션의 시작과 연산 종료시의 커밋 과정이 필요하므로, 프록시를 생성해 해당 메서드의 앞뒤에 트랜잭션의 시작과 끝을 추가하는 것이다.

이러한 로직은 AOP에 바탕을 두고 설계되었기 때문에, 이후 설명에서 해당 프록시는 트랜잭션 AOP로 명칭하겠다.

 

또한, 스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다.

서비스 클래스에서 @Transactional을 사용할 경우, 해당 코드 내의 메서드를 호출할 때 영속성 컨텍스트가 생긴다는 뜻이다. 영속성 컨텍스트는 트랜잭션 AOP가 트랜잭션을 시작할 때 생겨나고, 메서드가 종료되어 트랜잭션 AOP가 트랜잭션을 커밋할 경우 영속성 컨텍스트가 flush되면서 해당 내용이 반영된다. 이후 영속성 컨텍스트 역시 종료되는 것이다.

 

 

 

아래는 엔티티 코드이다. 작가와 책이 서로 연관 관계를 맺고 있으며, 지연 로딩 전략을 쓴다고 명시되었다.

@Entity
public class Book {
	@Id @GeneratedValue
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 전략
    private Author author;
    ...
}

이때 해당 책을 @Transactional이 적용된 서비스에서 가져온다고 해보자. 

다음은 컨트롤러 코드이다. 실행 시 예외가 발생한다!

class BookController {
    public String view(Long bookId) {
    	Book book = bookService.findBook(bookId);
        Author author = book.getAuthor();
        author.getName(); //여기서 예외 발생!!!
        ...
    }
}

왜 실행 도중 예외가 발생할까? 이유는 간단하다.

Book 엔티티는 Service에서 조회한 값인데, 이 때에는 영속성 컨텍스트가 올바르게 수행되었다.

그러나 서비스의 findBook 메서드가 종료되면서 영속성 컨텍스트가 닫혔고, 반환된 book 엔티티가 준영속 상태가 된 것이다.

author는 lazy loading 전략을 사용했으므로 비어있는 프록시 객체로 존재했는데, 해당 객체에서 실제로 값을 뽑아 쓰려고 하니 예외가 발생한다. (만약 준영속이 아니었다면 영속성 컨텍스트를 통해 author를 조회하고 값을 반환했을 것이다)

 

= 이는 우리가 OrderController의 order메서드에서 id값만들 받아서 service layer에서 실제 객체를 찾는 것과 같은 이유이다.!!!!

= 이는 우리가 OrderController의 order메서드에서 id값만들 받아서 service layer에서 실제 객체를 찾는 것과 같은 이유이다.!!!!

= 이는 우리가 OrderController의 order메서드에서 id값만들 받아서 service layer에서 실제 객체를 찾는 것과 같은 이유이다.!!!!

= 이는 우리가 OrderController의 order메서드에서 id값만들 받아서 service layer에서 실제 객체를 찾는 것과 같은 이유이다.!!!!

 

Recent Posts
Popular Posts
Recent Comments