Jackson 라이브러리에서 @JsonIdentityInfo 어노테이션은 객체 간에 부모-자식(parent-child) 관계가 존재하거나 순환 참조(circular reference)가 발생하는 상황에서 필수적으로 사용됩니다. 이 어노테이션은 직렬화(serialization) 및 역직렬화(deserialization) 과정에서 객체의 고유 식별자(identity)를 명시하여, 동일한 객체가 반복해서 출력되거나 무한 루프에 빠지는 문제를 방지해 줍니다.
특히 ObjectIdGenerators.PropertyGenerator는 객체 식별자로 사용할 값을 POJO의 특정 프로퍼티(필드)에서 가져오는 경우를 나타내는 추상 플레이스홀더 클래스입니다. 즉, 개발자가 지정한 프로퍼티 값이 곧 객체의 ID 역할을 하게 됩니다.
문법(Syntax)
@Target(value={ANNOTATION_TYPE,TYPE,FIELD,METHOD,PARAMETER})
@Retention(value=RUNTIME)
public @interface JsonIdentityInfo예제(Example)
아래 예제는 User와 Address 클래스 간의 양방향 관계에서 @JsonIdentityInfo를 적용한 모습입니다. 두 클래스 모두 PropertyGenerator를 사용하며, userId 프로퍼티를 객체 식별자로 지정했습니다.
import java.util.*;
import java.io.*;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonIdentityInfoTest {
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
User user = new User(115, "Raja", "Ramesh");
Address address = new Address(125, "Madhapur", "Hyderabad", user);
user.addAddress(address);
String jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(address);
System.out.println(jsonString);
}
}
// User 클래스
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "userId")
class User {
private int userId;
private String firstName;
private String lastName;
private List<Address> addresses;
public User(int userId, String firstName, String lastName) {
this.userId = userId;
this.firstName = firstName;
this.lastName = lastName;
this.addresses = new ArrayList<Address>();
}
public int getUserId() {
return userId;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void addAddress(Address address) {
addresses.add(address);
}
}
// Address 클래스
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "userId")
class Address {
private int userId;
private String city;
private String street;
private User user;
public Address(int userId, String street, String city, User user) {
this.userId = userId;
this.street = street;
this.city = city;
this.user = user;
}
public int getUserId() {
return userId;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public User getUser() {
return user;
}
}출력 결과(Output)
{
"userId" : 125,
"city" : "Hyderabad",
"street" : "Madhapur",
"user" : {
"userId" : 115,
"firstName" : "Raja",
"lastName" : "Ramesh"
}
}이처럼 @JsonIdentityInfo를 활용하면 객체 참조 구조가 복잡한 도메인 모델도 안전하고 효율적으로 JSON으로 변환할 수 있습니다. 순환 참조로 인한 StackOverflowError를 예방하고, 동일 객체의 중복 출력을 줄여 JSON 크기까지 최적화할 수 있다는 점에서 실무에서 매우 유용하게 활용되는 어노테이션입니다.