문제
- 단건 조회시 해당 엔티티에 딸린 댓글들을 가져올 때 엔티티를 그대로 반환하면서 무한 루프 문제가 발생했다.
- Todo와 Comment는 서로 양방향 연관관계로 연결되어있다.
원인
@RestController는 반환값을 자동으로 JSON 직렬화한다고 한다. 그때 양방향 연관관계에서
- Jackson이 Todo 객체를 JSON으로 변환하려 함
- Todo 내부의 comments를 직렬화하려 함
- 각 Comment는 다시 todo를 가지고 있어서 Todo 직렬화 시도
- Todo → Comment → Todo → Comment … 🔁
와 같은 순환문제가 발생한다. 즉, 객체가 서로를 참조하고 있어서 JSON 변환이 무한 루프에 빠지는 것
해결
해결방법은 여러가지가 있다고 하는데 나는 @JsonManagedReference / @JsonBackReference 대신 DTO 변환 방식으로 해결 하기로 했다.
3Layer를 각 계층이 하는일 외우고 배우기만 했지 경계가 무너지면서 생기는 문제를 경험하지 못해서 크게 와닿지 않았다. 엔티티는 데이터베이스 맵핑용 객체고 DTO는 외부 응답요청용 데이터 객체로 역할을 나누어놨는데 처음엔 왜 이미 데이터가 다 들어있는데 굳이 DTO로 또감싸나? 라는 생각을 갖게되면서 그냥 엔티티 자체를 반환해버렸다.
결과
아래와 같은 JSON response를 만들기 위해 TodoResponse 안에 CommentResponse를 포함하는 DTO 구조로 설계. JSON이 계층적 구조를 가지기 때문에 우리도 이와같은 구조를 만들어 주기 위해 DTO가 다른 DTO를 포함하는 관계로 만들어 주어야 한다.
{
"id": 1,
"title": "스프링 공부",
"username": "kim",
"comments": [
{
"id": 1,
"username": "userA",
"content": "화이팅!"
},
{
"id": 2,
"username": "userB",
"content": "나도 이거 해봤어!"
}
]
}
예시
DTO
public class GetOneTodoResponse {
private Long id;
private String username;
private String title;
private List<GetOneCommentResponse> comments;
}
GetOneTodoResponse가 GetOneCommentResponse를 필드로 가지고 있게 되면 하나의 Todo 응답에 여러개의 Comment 응답을 가지고 있다고 해석 할 수 있다.
배운 점
이번 루프 문제를 겪으면서 엔티티를 그대로 반환하면 시스템 경계가 무너진다는 걸 깨달았다
- 엔티티는 DB와 맞닿은 내부 구조
- DTO는 외부와 통신하는 응답 구조
둘을 구분하지 않으면 내부 설계가 외부에 그대로 드러나고, 이번처럼 무한 루프 같은 예기치 못한 문제도 생길수도 있다는 사실을 알게됨
'Dev. > Error.' 카테고리의 다른 글
| [Error] MySQL 버전 충돌 (macOS/Homebrew) (0) | 2025.11.08 |
|---|---|
| [Error] 일정 트러블 슈팅 (1) | 2025.11.05 |
| [Error] 자바에서 객체의 동등성과 동일성 문제 (0) | 2025.10.29 |
| [Error] 키오스크 트러블 슈팅 3 - 장바구니 구조 설계 (0) | 2025.10.28 |
| [Error] 키오스크 트러블 슈팅 2 -책임 분리 (0) | 2025.10.27 |