2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

349
docs/24_jackson.md Normal file
View File

@@ -0,0 +1,349 @@
## 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<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에 동적으로 저장합니다. 메서드는 필드 이름과 값을 매개변수로 받아야 합니다.
```java
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` 필드를 통해 관계가 복원됩니다.
```java
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 개발에 유용합니다.
```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 {
@