개발중/Spring

@JsonTypeInfo 활용 사례와 예제

Binsoo 2025. 3. 17.
728x90
반응형

@JsonTypeInfo란?

@JsonTypeInfo는 Jackson 라이브러리에서 다형성(Polymorphism) 직렬화와 역직렬화를 지원하기 위해 사용되는 애너테이션입니다.
객체지향 프로그래밍에서 상속 구조를 가지는 클래스 계층이 있을 때, 상위 클래스 타입으로 선언된 필드에 여러 하위 클래스의 객체가 저장될 수 있습니다.
이러한 상황에서 객체를 JSON으로 직렬화할 때, 단순히 필드 값만 기록하면 역직렬화 시 원래의 구체적인 클래스 정보를 알 수 없습니다.
@JsonTypeInfo는 이 문제를 해결하기 위해, JSON 데이터에 타입 정보를 포함시켜 저장함으로써, 역직렬화 시 원래의 하위 클래스로 복원할 수 있도록 도와줍니다.

주요 속성은 다음과 같습니다:

  • use: 타입 식별자(strategy)를 지정합니다.
    예를 들어, JsonTypeInfo.Id.CLASS를 사용하면, 클래스의 FQCN(전패키지 이름 포함 클래스명)이 타입 식별자로 사용됩니다.
  • include: 타입 정보를 JSON의 어디에 포함시킬지 결정합니다.
    예: JsonTypeInfo.As.PROPERTY는 JSON 객체의 프로퍼티로 포함시킵니다.
  • property: 타입 정보를 저장할 프로퍼티 이름을 지정합니다.
    예를 들어, "@class"로 지정하면, JSON에 "@class": "com.example.MySubClass" 형태로 저장됩니다.

 

@JsonTypeInfo 활용법

@JsonTypeInfo를 활용하면, 상속 구조를 가진 객체들을 직렬화할 때 자동으로 타입 정보를 포함할 수 있습니다.


일반적인 활용 방법은 두 가지입니다

 

기본 클래스에 직접 적용하기

상위 클래스나 인터페이스에 @JsonTypeInfo를 직접 적용하여, 하위 클래스들을 직렬화할 때 타입 정보가 포함되도록 할 수 있습니다.

예제에서는 Animal 클래스에 타입 정보를 포함시키도록 지정했으므로, 하위 클래스의 인스턴스를 직렬화하면 JSON에 "@class" 필드가 추가됩니다.

import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class"
)
public abstract class Animal {
    private String name;
    // getters, setters, etc.
}

 

 

Mixin을 사용하여 적용하기

만약 기존의 클래스에 직접 애너테이션을 추가하기 어려운 경우, Jackson의 Mixin 기능을 활용하여 외부에서 애너테이션을 적용할 수 있습니다.

이렇게 하면, Animal 클래스에 직접 애너테이션을 붙이지 않아도, Mixin을 통해 타입 정보가 포함된 JSON 직렬화/역직렬화가 가능해집니다.

// Mixin 클래스 (실제 로직은 없고 애너테이션만 포함)
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class"
)
public abstract class AnimalMixin {}

// ObjectMapper 설정 시 Mixin을 등록
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Animal.class, AnimalMixin.class);

 

 

@JsonTypeInfo 예제

1. 상속 구조가 있는 클래스 예제

 
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class"
)
abstract class Animal {
    public String name;
}

class Dog extends Animal {
    public double barkVolume;

    public Dog() {}  // 기본 생성자 필수
    public Dog(String name, double barkVolume) {
        this.name = name;
        this.barkVolume = barkVolume;
    }
}

class Cat extends Animal {
    public int lives;

    public Cat() {}
    public Cat(String name, int lives) {
        this.name = name;
        this.lives = lives;
    }
}

public class JsonTypeInfoExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        Animal dog = new Dog("Buddy", 10.5);
        String dogJson = mapper.writeValueAsString(dog);
        System.out.println("Serialized Dog:\n" + dogJson);

        // 역직렬화 시 원래 클래스 타입으로 복원
        Animal deserializedDog = mapper.readValue(dogJson, Animal.class);
        System.out.println("Deserialized Dog Class: " + deserializedDog.getClass().getName());
    }
}

 

2. Mixin 활용 예제

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

abstract class Animal {
    public String name;
}

class Dog extends Animal {
    public double barkVolume;

    public Dog() {}
    public Dog(String name, double barkVolume) {
        this.name = name;
        this.barkVolume = barkVolume;
    }
}

// Mixin 정의
@JsonTypeInfo(
    use = JsonTypeInfo.Id.CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class"
)
abstract class AnimalMixin {}

public class MixinExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixIn(Animal.class, AnimalMixin.class);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        Animal dog = new Dog("Buddy", 10.5);
        String json = mapper.writeValueAsString(dog);
        System.out.println("Serialized with Mixin:\n" + json);

        Animal deserializedDog = mapper.readValue(json, Animal.class);
        System.out.println("Deserialized Dog Class: " + deserializedDog.getClass().getName());
    }
}

 

결론

  • @JsonTypeInfo란?
    다형성 직렬화/역직렬화를 지원하기 위해 JSON에 타입 정보를 포함시키는 Jackson 애너테이션입니다.
  • 활용법:
    1. 상위 클래스나 인터페이스에 직접 적용하거나,
    2. Mixin을 사용하여 외부에서 적용할 수 있습니다.
  • 예제:
    위 예제에서는 Animal, Dog, Cat 클래스 계층을 통해 타입 정보를 JSON에 포함시키고, 역직렬화 시 올바른 하위 클래스로 복원하는 방법을 보여줍니다.

 

이와 같이 @JsonTypeInfo를 활용하면, 상속 관계에 있는 객체를 안전하게 JSON으로 직렬화/역직렬화할 수 있어 복잡한 도메인 모델을 다룰 때 매우 유용합니다.

728x90
반응형

댓글