상황
일정 등록 api를 구현하고 테스트하는 과정에서 예상치 못한 에러가 발생했다.

로그를 확인해보니 순환 참조 문제가 발생했다는 것을 확인할 수 있었다.
순환 참조(Circular Reference)
서로 다른 빈들이 서로 참조를 맞물리게 주입되면서 생기는 현상
문제
나같은 경우는 PartService와 ProductService가 서로를 참조하면서 발생한 문제였다.
- ProductService의 findProduct 메서드에서 partService.findPartsByProductId(productId)를 호출
- PartService는 다시 ProductService의 메서드를 호출하면서 무한 루프 발생
원인
ProductService에서 PartService를 직접 호출하여 Part 엔티티 리스트를 조회한 후 DTO 변환을 수행했는데, PartService도 ProductService에 의존하는 구조여서 순환 참조가 발생한 것이 원인이었다.
@Transactional(readOnly = true)
public ProductDetailResDto findProduct(Long productId) {
Product findProduct = productRepository.findByIdOrElseThrow(productId);
List<PartResDto> partResDtoList = partService.findPartsByProductId(productId)
.stream()
.map(part -> new PartResDto(part.getId(), part.getOpenAt(), part.getCloseAt(), part.getNumber()))
.collect(Collectors.toList());
return ProductDetailResDto.toDto(findProduct);
}
해결
@Transactional(readOnly = true)
public ProductDetailResDto findProduct(Long companyId, Long productId) {
Product findProduct = productRepository.findByCompanyIdAndId(companyId, productId);
if (findProduct == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "해당 업체가 가진 해당 ID의 상품이 없습니다.");
}
List<PartResDto> parts = findProduct.getParts().stream() // 엔티티에서 직접 조회
.map(part -> new PartResDto(part.getId(), part.getOpenAt(), part.getCloseAt(), part.getMaxQuantity()))
.collect(Collectors.toList());
return new ProductDetailResDto(
findProduct.getId(),
findProduct.getCompany().getId(),
findProduct.getName(),
findProduct.getDescription(),
findProduct.getPrice(),
findProduct.getAddress(),
findProduct.getSaleStartAt(),
findProduct.getSaleEndAt(),
findProduct.getCreatedAt(),
findProduct.getUpdatedAt(),
parts
);
}
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Part> parts = new ArrayList<>();
기존에는 ProductService에서 PartService를 호출하였는데, 나는 이를 해결하기 위해 서비스 간 직접 참조를 제거하고 JPA의 연관 관계 매핑을 활용하여 findProduct.getParts()로 바로 Part 리스트를 가져오도록 수정했다.
결과
서비스 간의 직접적인 참조를 제거하여 순환 참조 문제 해결함과 동시에 불필요한 서비스 호출을 줄여 성능을 최적화 하였다. 또한 JPA 연관 관계를 활용하여 더 간결하고 유지보수하기 쉬운 코드로 개선한 결과를 얻을 수 있었다.
'내배캠' 카테고리의 다른 글
| 최종 프로젝트 트러블슈팅(1) - pageable (0) | 2025.01.09 |
|---|---|
| 개인과제 트러블슈팅 - 일정 관리 앱 Develop (0) | 2024.11.29 |
| 팀 프로젝트 트러블슈팅 - 뉴스피드 프로젝트 (0) | 2024.11.21 |
| 개인과제 트러블슈팅 - 일정 관리 앱 Develop (0) | 2024.11.15 |
| 개인과제 트러블슈팅 - 일정 관리 앱 (0) | 2024.11.08 |