코딩못하는사람

JPA/ could not initialize proxy - no Session 본문

issue 기록

JPA/ could not initialize proxy - no Session

공부절대안함 2021. 9. 14. 14:47

1.문제점

JPA를 통한 개발을 하다보면 한번쯤 만날 수 밖에없는 에러이다.

JPA를 사용하여 DB에 저장된 리소스를 불러와서 반환하는 경우, 혹은 사용할 때 발생했던 에러이다.

 

2.접근

JPA에 대한 이해가 부족하다 생각하여 JPA강의와 강의자료를 다시 보며 개념을 스터디 했다.

JPA의 전반적인 동작 과정 트랜잭션,영속성 컨텍스트,Proxy,Lazy Loading에 대한 이해가 있어야 에러를 이해하고 수정할 수 있다.

 

3.원인

간단한 예시를 들어서 설명해보겠다.

Member와 Message가 연관관계를 맺고 있고 1:N관계를 가지고 있다.

Message 엔티티에서는 Member Fetch 전략을 Lazy로 설정해 준 상태이다.

 

어플리케이션의 메시지 단건 조회 API의 과정은 다음과 같다

 

그럼 이제 오류가 나는 컨트롤러 코드를 보자.

 

메시지를 message PK 로 messageService를통해 단건 조회 했다.

그 후 메시지 제목,내용,작성자 이름,받는사람 이름 등의 정보가 들어있는 MessageResponseDTO로 변환하여 리소스를 반환하는 API이다. 이 코드를 통해 메시지 단건 조회를 하면 어떻게 될까?

 

왜 이런 에러가 발생할까?

 

위 에러는 영속성 컨텍스트의 생명주기와 프록시에 대한 이해가 부족해서 일어나는 문제이다.

Message를 단건조회하면 Message와 Lazy Loading으로 연관된 Member는 바로 초기화 되지 않고 필요할때 정보가 채워지는 프록시 객체로 채워진다.

첫줄을 실행하면 컨트롤러 단에서 다음의 메시지 객체를 갖게 된다는 것이다.

문제점은 여기서 발생한다. 다음줄에서 이 message를 통해 MessageResponseDto로 변환하는 작업을 한다.

하지만 변환할 수 있을까??

없다.  여기서 에러가 발생하는 것이다. Member의 값을 써써 DTO를 채워나가야 하는데 Member의 값이 초기화 되지 않은 상태라서 DTO를 만들 수 없는것이다. 

 

Lazy loading방식이니 변환하면서 데이터를 사용할때 쿼리를 날려 Proxy객체를 채우지 않을까??

못 채운다.  왜?? 우리는 서비스단에서 트랜잭션이 일어나도록 설정을 해두었다. 그런데 JPA의 영속성 컨텍스트는 보통 트랜잭션과 생명주기를 같이한다. 그 말은 컨트롤러 단으로 나오면서 영속성 상태가 끝난다는 뜻이다.

그럼 더이상 영속성 컨텍스트에서 관리하지 않고 Member에 필요한 값이 있을때 쿼리를 날려 Proxy 객체를 채우지 않는다는 뜻이다. 그래서 에러가 발생하는 것이다.

 

4.해결방법

여러가지 방법이 있겠지만 두가지 방법을 알아보자.

 

1. Message -> DTO 변환을 컨트롤러 단에서 서비스 단으로 변경

컨트롤러 단에서 서비스 단으로 변경하면 트랜잭션 안이므로 ResponseMessageDTO로 변환할때 쿼리를 날려서 Message값을 채우고 DTO가 정상적으로 만들어진다.

서비스 단에서 Message를 받는것이 아닌 MessageResponseDto를 받아왔다.

2.Message에있는 Member를 즉시로딩(Eager)으로 변경한다

추천하는 방식은 아니지만 Message를 가져왔을때 Member 값이 바로 채워지므로 프록시 객체가 아니라 실제값이 들어가 있어서 프록시 초기화 관련 에러가 나지 않는다.

 

5.배운점

  • 에러를 해결하기 위해 전반적인 JPA의 개념을 한번 더 잡아서 좋았다.
  • 영속성 컨텍스트의 프록시,Lazy Loading의 개념이 확실해졌다. 
  • 컨트롤러 단,서비스 단에서의 작업을 더 명확히 분리해야겠다.

 

인프런 김영한 JPA 강의를 참고하였습니다.

Comments