Java에서 객체 간 양방향(bidirectional) 관계를 JSON으로 직렬화할 때, 순환 참조로 인해 무한 루프가 발생하거나 원치 않는 데이터가 출력되는 문제가 자주 생깁니다. 이럴 때 유용하게 쓸 수 있는 것이 바로 Jackson 라이브러리의 @JsonManagedReference와 @JsonBackReference 어노테이션입니다.
두 어노테이션의 역할
@JsonManagedReference는 순방향 참조(forward reference)를 나타내며, 직렬화 과정에서 해당 필드가 정상적으로 포함됩니다. 반면 @JsonBackReference는 역방향 참조(back reference)를 나타내며, 직렬화 시 이 필드는 생략(omitted)됩니다. 즉, 부모-자식 관계처럼 서로를 참조하는 구조에서 한쪽 방향만 JSON에 노출시켜 무한 재귀를 방지할 수 있습니다.
예제 코드
아래 예제를 통해 두 어노테이션의 실제 동작을 확인해 보겠습니다.
import java.util.*;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
public class ManagedReferenceBackReferenceTest {
public static void main(String args[]) throws JsonProcessingException {
BackReferenceBeanTest testBean = new BackReferenceBeanTest(110, "Sai Chaitanya");
ManagedReferenceBeanTest bean = new ManagedReferenceBeanTest(135, "Adithya Ram", testBean);
testBean.addEmployees(bean);
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(bean);
System.out.println(jsonString);
}
}
class ManagedReferenceBeanTest {
public int empId = 115;
public String empName = "Raja Ramesh";
@JsonManagedReference
public BackReferenceBeanTest manager;
public ManagedReferenceBeanTest(int empId, String empName, BackReferenceBeanTest manager) {
this.empId = empId;
this.empName = empName;
this.manager = manager;
}
}
class BackReferenceBeanTest {
public int empId = 125;
public String empName = "Jai Dev";
@JsonBackReference
public List<ManagedReferenceBeanTest> list;
public BackReferenceBeanTest(int empId, String empName) {
this.empId = empId;
this.empName = empName;
list = new ArrayList<ManagedReferenceBeanTest>();
}
public void addEmployees(ManagedReferenceBeanTest managedReferenceBeanTest) {
list.add(managedReferenceBeanTest);
}
}실행 결과
{
"empId" : 135,
"empName" : "Adithya Ram",
"manager" : {
"empId" : 110,
"empName" : "Sai Chaitanya"
}
}결과 분석
출력 결과를 보면 직원(Employee) 객체인 bean은 manager 필드(@JsonManagedReference 적용)를 통해 관리자 정보를 정상적으로 포함하고 있습니다. 하지만 관리자 객체 내부의 list 필드(@JsonBackReference 적용), 즉 다시 직원 목록을 참조하는 부분은 JSON에서 제외된 것을 확인할 수 있습니다.
만약 두 어노테이션을 사용하지 않았다면, 직원 → 관리자 → 직원 → 관리자... 로 이어지는 무한 순환 참조가 발생해 StackOverflowError가 발생하거나 비정상적으로 거대한 JSON이 생성되었을 것입니다.
정리
- @JsonManagedReference: 직렬화 시 포함되는 순방향 참조 (부모 쪽 필드)
- @JsonBackReference: 직렬화 시 생략되는 역방향 참조 (자식 쪽 필드)
양방향 관계가 있는 엔티티를 REST API 응답 등으로 변환할 때 이 두 어노테이션을 함께 사용하면, 순환 참조 문제를 깔끔하게 해결하고 의도한 JSON 구조를 만들 수 있습니다.