1. JPA 지연 로딩 에러 발생 상황
- BoardEntity와 MemberEntity가 연관 관계를 가지며 Member가 부모 Board가 자식 관계이다.
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_no", nullable = false)
private Member member;
원래는 즉시 로딩으로 사용하다가 지연 로딩으로 변경 후 게시판 상세 조회 시 에러가 발생했다.
2. 지연 로딩 json 변환 시 에러 원인
RestContoller 코드
/**
* 게시판 상세 조회
*
* @param boardNo
* @return
*/
@GetMapping("/board/{boardNo}")
public CommonResponseEntity<BoardDto> selectBoard(@PathVariable Long boardNo){
BoardDto board = boardService.selectBoard(boardNo);
return new CommonResponseEntity<>(board, null);
}
Service코드
/**
* 게시판 상세 조회
*
* @param boardNo
* @return BoardDto
*/
public BoardDto selectBoard(Long boardNo) {
System.err.println("========================================================================");
Board findBoard = boardRepository.findById(boardNo).orElseThrow(()-> new ResourceNotFoundException("Board","boardNm",boardNo));
System.err.println("member : "+findBoard.getMember().getClass());
System.err.println("========================================================================");
return findBoard.toDto();
}
- 해당 함수가 실행 되면 Board안에 있는 Member 객체의 클래스를 찍어보았고 일반 객체가 아닌 프록시 객체였다.
여기까지는 프록시 객체가 문제라고 생각하지 못하여, 리턴하기 전에 해당 프록시 객체가 초기화되지 못하여 에러가 났나 생각했다.
그래서 아래와 같은 테스트도 진행을 했다,
public BoardDto selectBoard(Long boardNo) {
System.err.println("========================================================================");
Board findBoard = boardRepository.findById(boardNo).orElseThrow(()-> new ResourceNotFoundException("Board","boardNm",boardNo));
System.err.println(findBoard.getMember().getEmail());
System.err.println("member : "+findBoard.getMember().getClass());
System.err.println("========================================================================");
return findBoard.toDto();
}
- 프록시 객체의 값을 실제 사용하는 순간 해당 객체의 조회 쿼리가 발생한다(프록시의 초기화가 진행된다)
- 아래의 이미지와 같이 Board 조회 쿼리가 나간 바로 뒤 Member 조회 쿼리가 발생한다.
하지만 여전히 똑같은 에러가 발생하였고, 초기화 시점의 문제가 아닌 프록시 객체 그 자체가 문제였다.
원인
해당 프로젝트는 API 서버이므로 @RestController 어노테이션을 통해 모든 리턴 값을 Json으로 변환해준다.
그런데 Json으로 변환해 줄 때 직렬화가 발생하고, 프록시 객체를 직렬화 하면 에러가 발생한다.
Json 변환 => 직렬화 => 프록시 객체의 직렬화 == 에러
어떠한 이유로 직렬화 시 에러가 발생하는지 이해를 해보고 싶다. 아직 직렬화의 내부 동작을 이해하지 못하고 있어
학습이 필요하다.
3. 해결 방법
프록시 객체의 직렬화를 해결하는 방법은 Jackson의 설정을 바꾼다거나 하는 여러 방법들이 존재한다고 하지만,
나는 단순하게 join fetch를 사용하여 연관 객체를 프록시가 아닌 일반 객체로 조회를 하여 해결하였다.
Repository 코드
public interface BoardRepository extends JpaRepository<Board,Long>, PagingAndSortingRepository<Board, Long> {
@Override
@Query("select b from Board b join fetch b.member where b.boardNo =:boardNo")
Optional<Board> findById(Long boardNo);
}
Service 코드
public BoardDto selectBoard(Long boardNo) {
System.err.println("========================================================================");
Board findBoard = boardRepository.findById(boardNo).orElseThrow(()-> new ResourceNotFoundException("Board","boardNm",boardNo));
System.err.println(findBoard.getMember().getEmail());
System.err.println("member : "+findBoard.getMember().getClass());
System.err.println("========================================================================");
return findBoard.toDto();
}
결과
연관 관계인 Member 객체가 프록시가 아닌 일반 객체인게 확인이 되었고, 실제 응답까지 성공적으로 동작하였다.
반응형
'JPA > Spring Data Jpa' 카테고리의 다른 글
[SpringDataJpa] QueryDsl 정리 (0) | 2023.08.10 |
---|---|
[SpringDataJpa] findAll() n+1 문제 해결 (0) | 2023.08.09 |