-
연관관계 엔티티 직렬화 오류와 해결 (DTO 패턴)Jpa 2025. 7. 7. 19:20
서론
Spring Boot로 영화 및 리뷰 관리 API를 개발하던 중, 영화 리뷰 목록을 조회하는 API에서 직렬화 오류가 발생했습니다. 해당 오류는 Review 엔티티가 Movie 엔티티와 연관관계를 맺고 있는 상황에서 발생했으며, 문제를 해결하기 위해 3가지 방법을 고려했습니다. 이 글에서는 문제의 원인과 각 해결 방법을 소개하고, 최종적으로 선택한 DTO 패턴 방식에 대해 포스팅하겠습니다.
문제 상황
영화 리뷰 목록 조회 API:
GET /movies/{movieId}/reviews?sort=rating정상적으로 리뷰 데이터를 받아올 것으로 예상했지만, 아래와 같은 에러가 발생했습니다.
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor ...해당 에러는 Review 엔티티가 @ManyToOne으로 Movie를 참조하고 있고, 지연 로딩(LAZY)으로 인해 Movie가 프록시 객체로 변환되면서 발생한 직렬화 오류입니다.
원인 분석
Review 엔티티 구조 일부
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "movie_id", nullable = false) private Movie movie;- Movie 필드는 JPA의 지연 로딩(LAZY) 설정으로 인해 실제 객체가 아닌 Hibernate의 프록시 객체(ByteBuddyInterceptor)로 감싸진 상태였고, Jackson은 이를 직렬화할 수 없어 예외가 발생했습니다.
고려한 3가지 해결 방안
1. @JsonIgnore 사용
@JsonIgnore private Movie movie;- 장점: 간단한 방법으로 직렬화 제외.
- 단점: 일부 API에서는 movie 정보가 필요할 수 있어 재사용이 어렵고 유연하지 않음. API 스펙을 위해 엔티티를 변경하는 것은 매우 좋지 않음.
2. @JsonIgnoreProperties 사용
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) private Movie movie;- 장점: LAZY 로딩 프록시 속성만 무시 가능.
- 단점: hibernateLazyInitializer, handler 속성만 무시할 수 있어, 연관 객체 전체가 필요한 경우에는 여전히 문제가 발생할 수 있음. JPA 내부 구현에 의존하므로 유지보수에 불리함.
3. DTO 패턴 사용 (선택한 방법)
- 엔티티 대신 DTO 객체에 필요한 필드만 담아서 응답:
@Getter public class ReviewDto { private Long id; private String content; private double rating; private String createdAt; public ReviewDto(Review review) { this.id = review.getId(); this.content = review.getContent(); this.rating = review.getRating(); this.createdAt = review.getCreatedAt().toString(); } }- 컨트롤러에서 DTO로 변환하여 응답:
@GetMapping("/movies/{movieId}/reviews") public List<ReviewDto> getReviews(@PathVariable Long movieId, @RequestParam(defaultValue = "createdAt") String sort) { return reviewService.findAll(movieId, sort).stream() .map(ReviewDto::new) .toList(); }최종 선택: DTO 방식
선택 이유:
- 응답 구조를 명확하게 분리함으로써 클라이언트에 불필요한 내부 구현 세부사항이 노출되지 않음
- 엔티티의 순환 참조 문제를 원천 차단
- 향후 API 변경이나 확장에 유연하게 대응 가능
- Jackson 직렬화 문제를 해결하는 가장 표준적이고 유지보수성 높은 방식
느낀 점
Spring Boot에서 JPA를 활용할 때, 엔티티 그대로 리턴하는 방식은 간단해 보이지만 위험한 선택일 수 있다. 연관관계가 복잡해질수록 직렬화 문제가 쉽게 발생하고, 유지보수성도 떨어지기 때문입니다.
앞으로는 API의 모든 요청과 응답에서 DTO를 사용하여 데이터 구조를 명확히 관리하고, 엔티티는 서비스 및 리포지토리 계층 등 비즈니스 로직 내부에서만 사용하도록 설계할 계획입니다.'Jpa' 카테고리의 다른 글
@Transactional에 대해서 (0) 2025.07.30 JPA 양방향 연관관계, 왜 편의 메서드를 써야 할까? (3) 2025.07.30 Spring Data JPA에서 동적 검색 해결: 사용자 정의 리포지토리와 Querydsl의 도입기 (3) 2025.07.21