
내일배움캠프 24일차 TIL : Spring - JackSon 동작원리
원래 JWT 토큰 생성하고 인증/인가 과정을 만드는 코드를 적으면서 TIL을 작성하려 했지만 어렵기도 했고....
이번에 개인 프로젝트 하면서 양방향 관계에서 발생한 순환 참조 문제가 있었기에 이거 정리 해보려 한다.
개인프로젝트 링크 -> 커밋 목록 -> 순환 참조 방지
일단 순환 참조를 해결하기 전에 왜 순환 참조가 일어나는지 알아야 한다.
JPA에서 양방향으로 연결된 Entity를 Json 형태로 직렬화 하는 과정에서
서로의 정보를 계속 순환하며 참조하는 현상이다.
Spring Boot에서 @ResponseBody 선언을 하면 Object를 Json으로 변환하기 위해 HttpMessageConverters에서 jackson 라이브러리를 이용한다.
// 1
@OneToMany(mappedBy = "toDoList", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
// 2
@ManyToOne
@JoinColumn(name = "to_do_list_id")
private ToDoList toDoList;
일단 위의 코드를 보면 서로가 1 : N, N : 1 로 양방향 연관 관계를 선언해두었다.
양방향 관계를 통해 toDoList 가 comment 를 조회할 수 있고 반대의 상황도 가능하다.
이제 여기서 toDoList를 호출하기 위해서 1번 코드를 통해 가져오게 된다.
이 toDoList를 Json으로 반환하면 직렬화 하는 과정에서 List<Comment> comments가 직렬화 되고, comments 가 직렬화 하다보면 다시 toDoList가 있기 때문에 다시 직렬화 되고, toDoList를 Json으로 반환하면 직렬화 하는 과정에서 List<Comment> comments가 직렬화 되고, comments 가 직렬화 하다보면 다시 toDoList가 있기 때문에 다시 직렬화 되고,
ㅋㅋㅋㅋ
두 번 밖에 안썼는데 점심 나가서 먹겠네..
요약하자면
Entity 클래스의 멤버를 직렬화 하는 과정에서 서로를 만나기 때문에 계속 반복한다.
일단 나같은 경우는 어노테이션을 하나 붙이는 것으로 해결했다.
@JsonManagedReference, @JsonBackReference
이 어노테이션은 양방향 관계에서 직렬화 방향을 설정하여 순환 참조를 해결할 수 있도록 설계된 어노테이션이다.
사용법은 아래와 같다.
// 1
@OneToMany(mappedBy = "toDoList", cascade = CascadeType.ALL)
@JsonManagedReference
private List<Comment> comments = new ArrayList<>();
// 2
@ManyToOne
@JoinColumn(name = "to_do_list_id")
@JsonBackReference
private ToDoList toDoList;
- 연관관계 주인 반대 Entity 에 선언
- 정상적으로 직렬화 수행
- 연관관계 주인 Entity에 선언
- 직렬화가 되지 않도록 수행
사실 이 문제는 양방향 관계에서 나오기도 하지만 Entity를 직접 반환하는 문제도 있다고 한다.
DTO 사용해서 필요한 데이터를 return 하도록 하자.
사실 적으면서 양방향 매핑시 연관관계의 주인에 대해 잠깐 혼동이 왔는데
연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다. 반면에 주인이 아닌 쪽은 읽기만 할 수 있다.
연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야 한다.
데이터베이스 테이블의 다대일, 일대다 관계에서는 항상 다 쪽이 외래 키를 가진다.
@ManyToOne은 항상 연관관계의 주인이 되므로 mappedBy를 설정할 수 없다.
참고