## Jackson 어노테이션 완벽 정리 및 상세 설명 Jackson은 자바 객체를 JSON으로 직렬화하거나 JSON을 자바 객체로 역직렬화하는 데 가장 널리 사용되는 라이브러리입니다. Jackson은 기본적으로 많은 부분을 자동으로 처리하지만, 어노테이션을 사용하면 직렬화 및 역직렬화 과정을 세밀하게 제어할 수 있습니다. 이번 글에서는 Jackson에서 주로 사용되는 어노테이션들을 표로 정리하고, 각 어노테이션의 역할과 사용법을 상세히 설명하겠습니다. ### Jackson 주요 어노테이션 요약 표 | 어노테이션 | 적용 대상 | 설명 | | :----------------------------- | :---------------- | :--------------------------------------------------------------------------------------------------------------------------------- | | **클래스 레벨** | | | | `@JsonIgnoreProperties` | 클래스 | 직렬화 또는 역직렬화 시 특정 필드를 무시합니다. | | `@JsonPropertyOrder` | 클래스 | JSON으로 직렬화될 때 필드의 순서를 지정합니다. | | `@JsonRootName` | 클래스 | JSON 최상위 레벨의 이름을 지정합니다. (특정 설정 필요) | | `@JsonTypeInfo` | 클래스 | 상속 관계의 객체를 직렬화/역직렬화할 때 타입 정보를 포함하도록 설정합니다. | | `@JsonTypeName` | 클래스 | `@JsonTypeInfo` 와 함께 사용되어 특정 하위 타입의 이름을 지정합니다. | | `@JsonUnwrapped` | 필드 | 해당 필드의 내용을 현재 객체의 필드인 것처럼 펼쳐서 직렬화/역직렬화합니다. | | **필드 레벨** | | | | `@JsonProperty` | 필드, 메서드 | JSON 필드 이름과 자바 객체 필드 또는 메서드 이름을 매핑합니다. 역직렬화 시 필수 필드를 지정할 수도 있습니다. | | `@JsonIgnore` | 필드, 메서드 | 해당 필드 또는 메서드를 직렬화 및 역직렬화 과정에서 무시합니다. | | `@JsonIgnoreType` | 클래스, 필드 | 해당 타입의 모든 필드를 직렬화 및 역직렬화 과정에서 무시합니다. | | `@JsonSerialize` | 필드, 메서드 | 해당 필드 또는 메서드의 값을 직렬화할 때 사용할 사용자 정의 Serializer를 지정합니다. | | `@JsonDeserialize` | 필드, 메서드 | 해당 필드 또는 메서드의 값을 역직렬화할 때 사용할 사용자 정의 Deserializer를 지정합니다. | | `@JsonFormat` | 필드, 메서드 | 날짜, 시간 등의 특정 타입의 직렬화/역직렬화 포맷을 지정합니다. | | `@JsonValue` | 메서드 | 해당 메서드의 반환 값을 객체 전체의 JSON 값으로 사용합니다. (주로 Enum에서 사용) | | `@JsonCreator` | 생성자, 정적 팩토리 메서드 | 역직렬화 시 사용할 생성자 또는 정적 팩토리 메서드를 지정합니다. `@JsonProperty` 와 함께 사용하여 매개변수를 매핑합니다. | | `@JsonAnyGetter` | 메서드 | 동적으로 생성되는 필드들을 JSON 객체로 직렬화할 때 사용됩니다. (Map 타입 필드에 적용) | | `@JsonAnySetter` | 메서드 | JSON 객체의 알 수 없는 필드들을 역직렬화하여 자바 객체의 Map 타입 필드에 저장할 때 사용됩니다. | | `@JsonManagedReference` | 필드 | 순환 참조 관계에서 직렬화를 제어하는 데 사용됩니다. (Forward reference) | | `@JsonBackReference` | 필드 | 순환 참조 관계에서 역직렬화를 제어하는 데 사용됩니다. (Back reference, 직렬화 시 무시) | | `@JsonView` | 필드, 메서드 | 특정 View 인터페이스를 활성화했을 때만 해당 필드 또는 메서드를 직렬화에 포함합니다. | | `@JsonAlias` | 필드 | 역직렬화 시 JSON 필드 이름의 별칭을 지정합니다. 여러 개의 별칭을 지정할 수 있습니다. | | `@JsonInclude` | 클래스, 필드 | 특정 조건(null 값, 기본값 등)에 따라 필드를 직렬화에서 제외합니다. | ### Jackson 어노테이션 상세 설명 이제 각 어노테이션에 대해 더 자세히 알아보겠습니다. 예시 코드를 통해 각 어노테이션의 동작 방식을 명확히 이해할 수 있습니다. **클래스 레벨 어노테이션** * **`@JsonIgnoreProperties({"field1", "field2"})`**: * 직렬화 또는 역직렬화 시 지정된 이름의 필드를 무시합니다. * `ignoreUnknown = true` 속성을 사용하면 JSON에 정의되지 않은 필드가 있어도 예외를 발생시키지 않고 무시합니다. ```java @JsonIgnoreProperties({"internalId", "creationDate"}) public class User { public int id; public String name; private String internalId; private Date creationDate; // ... (생성자, Getter, Setter) } // JSON -> User 역직렬화 시 "internalId", "creationDate" 필드는 무시됨 // User -> JSON 직렬화 시 "internalId", "creationDate" 필드는 포함되지 않음 ``` * **`@JsonPropertyOrder({"name", "id", "email"})`**: * JSON으로 직렬화될 때 필드의 순서를 명시적으로 지정합니다. 지정되지 않은 필드는 지정된 필드 뒤에 알파벳 순으로 나타납니다. ```java @JsonPropertyOrder({"name", "id", "email"}) public class Person { public int id; public String name; public String email; // ... } // Person 객체를 JSON으로 직렬화하면 "name", "id", "email" 순서로 필드가 나타남 ``` * **`@JsonRootName("user")`**: * JSON을 최상위 레벨의 이름으로 래핑합니다. Jackson의 `SerializationFeature.WRAP_ROOT_VALUE` 설정을 활성화해야 동작합니다. ```java @JsonRootName("user") public class Profile { public String username; public String role; // ... } // ObjectMapper mapper = new ObjectMapper(); // mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); // mapper.writeValueAsString(new Profile("testuser", "admin")); // 결과: {"user":{"username":"testuser","role":"admin"}} ``` * **`@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type")`**: * 상속 관계에 있는 클래스들을 직렬화/역직렬화할 때 타입 정보를 JSON에 포함하도록 설정합니다. * `use`: 타입 정보를 식별하는 방식을 지정 (예: `NAME`, `CLASS`) * `include`: 타입 정보를 JSON에 포함하는 방식 (예: `PROPERTY`, `WRAPPER_OBJECT`) * `property`: 타입 정보를 저장할 JSON 필드 이름 * **`@JsonTypeName("circle")`**: * `@JsonTypeInfo` 와 함께 사용되어 특정 하위 타입의 이름을 지정합니다. ```java @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "@type") abstract class Shape { public String color; } @JsonTypeName("circle") public class Circle extends Shape { public double radius; } @JsonTypeName("rectangle") public class Rectangle extends Shape { public double width; public double height; } // Circle 객체를 직렬화하면 JSON에 "@type":"circle" 정보가 포함됨 ``` * **`@JsonUnwrapped`**: * 포함된 객체의 필드를 현재 객체의 필드처럼 펼쳐서 직렬화/역직렬화합니다. 이름 충돌에 주의해야 합니다. * `prefix` 속성을 사용하여 펼쳐진 필드 이름에 공통 접두사를 추가할 수 있습니다. ```java public class Address { public String street; public String city; } public class PersonInfo { public String name; @JsonUnwrapped public Address address; } // PersonInfo 객체를 직렬화하면 JSON은 {"name":"...", "street":"...", "city":"..."} 형태가 됨 ``` **필드 레벨 어노테이션** * **`@JsonProperty("fieldNameInJson")`**: * 자바 필드 이름과 JSON 필드 이름을 다르게 매핑합니다. * `required = true` 속성을 사용하여 역직렬화 시 해당 필드가 JSON에 반드시 존재해야 함을 명시할 수 있습니다. ```java public class Product { @JsonProperty("product_id") public int id; @JsonProperty(value = "product_name", required = true) public String name; // ... } // JSON {"product_id": 123, "product_name": "Laptop"} 은 Product 객체로 역직렬화됨 // 역직렬화 시 "product_name" 필드가 없으면 예외 발생 ``` * **`@JsonIgnore`**: * 해당 필드를 직렬화 및 역직렬화 과정에서 완전히 무시합니다. ```java public class Employee { public int id; public String name; @JsonIgnore private String password; // JSON으로 노출하지 않음 // ... } ``` * **`@JsonIgnoreType`**: * 해당 타입의 모든 필드를 직렬화 및 역직렬화 과정에서 무시합니다. ```java @JsonIgnoreType public class InternalInfo { public String securityCode; public Date lastAccessed; } public class SystemData { public String version; public InternalInfo internal; // InternalInfo 타입의 필드는 직렬화/역직렬화 시 무시됨 } ``` * **`@JsonSerialize(using = CustomSerializer.class)`**: * 해당 필드를 직렬화할 때 사용할 사용자 정의 Serializer 클래스를 지정합니다. 복잡한 객체나 특정 포맷으로 직렬화해야 할 때 유용합니다. * **`@JsonDeserialize(using = CustomDeserializer.class)`**: * 해당 필드를 역직렬화할 때 사용할 사용자 정의 Deserializer 클래스를 지정합니다. JSON 데이터를 특정 객체로 변환하는 로직을 직접 구현할 수 있습니다. * **`@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul")`**: * 날짜, 시간 등의 특정 타입의 직렬화/역직렬화 포맷을 지정합니다. `pattern`, `timezone`, `locale` 등의 속성을 사용할 수 있습니다. ```java public class Event { public String name; @JsonFormat(pattern = "yyyy-MM-dd") public Date eventDate; } // Event 객체의 eventDate는 "yyyy-MM-dd" 형식의 문자열로 직렬화됨 // 해당 형식의 문자열은 Date 객체로 역직렬화됨 ``` * **`@JsonValue`**: * 메서드에 적용하며, 해당 메서드의 반환 값을 객체 전체의 JSON 값으로 사용합니다. 주로 Enum에서 Enum의 특정 값을 JSON으로 표현하고자 할 때 사용됩니다. ```java public enum Status { OK("정상"), ERROR("오류"); private final String description; Status(String description) { this.description = description; } @JsonValue public String getDescription() { return description; } } // Status.OK 객체를 직렬화하면 "정상" 문자열이 됨 ``` * **`@JsonCreator`**: * 클래스의 생성자 또는 정적 팩토리 메서드에 적용하여 역직렬화 시 사용할 방법을 지정합니다. * 생성자 파라미터 또는 정적 팩토리 메서드의 매개변수에 `@JsonProperty` 어노테이션을 함께 사용하여 JSON 필드와 매핑합니다. ```java public class Point { private final int x; private final int y; @JsonCreator public Point(@JsonProperty("x_coordinate") int x, @JsonProperty("y_coordinate") int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } } // JSON {"x_coordinate": 10, "y_coordinate": 20} 은 Point 객체로 역직렬화됨 ``` * **`@JsonAnyGetter`**: * Map 타입의 필드에 적용된 메서드에 사용됩니다. 직렬화 시 Map의 키-값 쌍을 JSON 객체의 필드로 동적으로 추가합니다. ```java public class Attributes { private Map properties = new HashMap<>(); public void addProperty(String key, Object value) { this.properties.put(key, value); } @JsonAnyGetter public Map getProperties() { return properties; } } // Attributes 객체에 addProperty("color", "blue"), addProperty("size", "large")를 호출 후 직렬화하면 // JSON은 {"color":"blue", "size":"large"} 형태가 됨 ``` * **`@JsonAnySetter`**: * Map 타입의 필드에 적용된 메서드에 사용됩니다. 역직렬화 시 JSON 객체에 정의되지 않은 필드들을 Map에 동적으로 저장합니다. 메서드는 필드 이름과 값을 매개변수로 받아야 합니다. ```java public class UnknownProperties { private Map unknown = new HashMap<>(); @JsonAnySetter public void addUnknown(String name, Object value) { this.unknown.put(name, value); } public Map getUnknown() { return unknown; } } // JSON {"name":"Product A", "price": 25.99, "category": "Electronics"} 를 UnknownProperties 객체로 역직렬화하면 // "category": "Electronics" 정보는 unknown Map에 저장됨 ``` * **`@JsonManagedReference` & `@JsonBackReference`**: * 객체 간의 순환 참조(예: 부모-자식 관계)가 있을 때 직렬화/역직렬화 과정에서 무한 루프를 방지하고 관계를 올바르게 처리하는 데 사용됩니다. * `@JsonManagedReference`: 순환 참조의 "정방향" (부모 -> 자식) 관계를 나타내는 필드에 적용합니다. 이 필드는 정상적으로 직렬화됩니다. * `@JsonBackReference`: 순환 참조의 "역방향" (자식 -> 부모) 관계를 나타내는 필드에 적용합니다. 이 필드는 직렬화 시 무시됩니다. 역직렬화 시에는 `@JsonManagedReference` 필드를 통해 관계가 복원됩니다. ```java public class Parent { public String name; @JsonManagedReference public List children; } public class Child { public String childName; @JsonBackReference public Parent parent; } // Parent 객체를 직렬화하면 children 리스트가 포함되지만, 각 Child 객체의 parent 필드는 제외됨 // 역직렬화 시 children 정보를 바탕으로 각 Child 객체의 parent 필드가 올바르게 설정됨 ``` * **`@JsonView(MyView.Public.class)`**: * 특정 View 인터페이스를 정의하고, `@JsonView` 어노테이션을 사용하여 해당 View가 활성화되었을 때만 필드를 직렬화에 포함하도록 제어합니다. 다양한 수준의 정보 공개가 필요한 API 개발에 유용합니다. ```java public class Item { public interface Public {} public interface Internal extends Public {} @JsonView(Public.class) public int id; @JsonView(Public.class) public String itemName; @JsonView(Internal.class) public String internalCode; } // ObjectMapper mapper = new ObjectMapper(); // mapper.setConfig(mapper.getSerializationConfig().withView(Item.Public.class)); // Item 객체를 직렬화하면 id와 itemName만 포함됨 // mapper.setConfig(mapper.getSerializationConfig().withView(Item.Internal.class)); // Item 객체를 직렬화하면 id, itemName, internalCode 모두 포함됨 ``` * **`@JsonAlias({"oldName1", "oldName2"})`**: * 역직렬화 시 JSON 필드 이름의 별칭을 지정합니다. API 버전 관리 등으로 인해 필드 이름이 변경되었을 때 이전 이름으로도 역직렬화를 허용할 수 있습니다. ```java public class LegacyUser { @