Files
spring-boot-examples/docs/24_jackson.md
2025-04-08 19:56:24 +09:00

18 KiB

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에 정의되지 않은 필드가 있어도 예외를 발생시키지 않고 무시합니다.
    @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으로 직렬화될 때 필드의 순서를 명시적으로 지정합니다. 지정되지 않은 필드는 지정된 필드 뒤에 알파벳 순으로 나타납니다.
    @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 설정을 활성화해야 동작합니다.
    @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 와 함께 사용되어 특정 하위 타입의 이름을 지정합니다.
    @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 속성을 사용하여 펼쳐진 필드 이름에 공통 접두사를 추가할 수 있습니다.
    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에 반드시 존재해야 함을 명시할 수 있습니다.
    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:

    • 해당 필드를 직렬화 및 역직렬화 과정에서 완전히 무시합니다.
    public class Employee {
        public int id;
        public String name;
        @JsonIgnore
        private String password; // JSON으로 노출하지 않음
    
        // ...
    }
    
  • @JsonIgnoreType:

    • 해당 타입의 모든 필드를 직렬화 및 역직렬화 과정에서 무시합니다.
    @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 등의 속성을 사용할 수 있습니다.
    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으로 표현하고자 할 때 사용됩니다.
    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 필드와 매핑합니다.
    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 객체의 필드로 동적으로 추가합니다.
    public class Attributes {
        private Map<String, Object> properties = new HashMap<>();
    
        public void addProperty(String key, Object value) {
            this.properties.put(key, value);
        }
    
        @JsonAnyGetter
        public Map<String, Object> getProperties() {
            return properties;
        }
    }
    
    // Attributes 객체에 addProperty("color", "blue"), addProperty("size", "large")를 호출 후 직렬화하면
    // JSON은 {"color":"blue", "size":"large"} 형태가 됨
    
  • @JsonAnySetter:

    • Map 타입의 필드에 적용된 메서드에 사용됩니다. 역직렬화 시 JSON 객체에 정의되지 않은 필드들을 Map에 동적으로 저장합니다. 메서드는 필드 이름과 값을 매개변수로 받아야 합니다.
    public class UnknownProperties {
        private Map<String, Object> unknown = new HashMap<>();
    
        @JsonAnySetter
        public void addUnknown(String name, Object value) {
            this.unknown.put(name, value);
        }
    
        public Map<String, Object> getUnknown() {
            return unknown;
        }
    }
    
    // JSON {"name":"Product A", "price": 25.99, "category": "Electronics"} 를 UnknownProperties 객체로 역직렬화하면
    // "category": "Electronics" 정보는 unknown Map에 저장됨
    
  • @JsonManagedReference & @JsonBackReference:

    • 객체 간의 순환 참조(예: 부모-자식 관계)가 있을 때 직렬화/역직렬화 과정에서 무한 루프를 방지하고 관계를 올바르게 처리하는 데 사용됩니다.
    • @JsonManagedReference: 순환 참조의 "정방향" (부모 -> 자식) 관계를 나타내는 필드에 적용합니다. 이 필드는 정상적으로 직렬화됩니다.
    • @JsonBackReference: 순환 참조의 "역방향" (자식 -> 부모) 관계를 나타내는 필드에 적용합니다. 이 필드는 직렬화 시 무시됩니다. 역직렬화 시에는 @JsonManagedReference 필드를 통해 관계가 복원됩니다.
    public class Parent {
        public String name;
        @JsonManagedReference
        public List<Child> 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 개발에 유용합니다.
    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 버전 관리 등으로 인해 필드 이름이 변경되었을 때 이전 이름으로도 역직렬화를 허용할 수 있습니다.
    public class LegacyUser {
        @