728x90

 

개요

Spring boot를 사용한 백엔드 개발을 하다보면 자연스럽게 JSON데이터를 객체로 매핑해야하는 경우가 있습니다.

 

이러한  Java 객체JSON 간의 변환 작업, 즉 '직렬화(Serialization)' '역직렬화(Deserialization)'는 매우 중요한 과정인데, 본 글에서는 이 과정을 효율적으로 처리하기 위한 강력한 도구인 ObjectMapper에 대해서 정리하고자 합니다.

 

 

직렬화 & 역직렬화란❓

직렬화(Serialization)

직렬화는 객체의 상태를 바이트 스트림이나 다른 형식(JSON, XML 등)으로 변환하는 과정입니다. 이 과정을 통해 메모리에 존재하는 객체를 외부에서 사용할 수 있는 형태로 변환합니다.

 

주요 목적:

  • 네트워크를 통한 데이터 전송
  • 파일 시스템에 데이터 저장
  • 다른 시스템이나 언어와의 데이터 교환

역직렬화(Deserialization)

역직렬화는 직렬화의 반대 과정으로, 바이트 스트림이나 다른 형식(JSON, XML 등)의 데이터를 다시 객체로 변환하는 과정입니다.

 

주요 목적:

  • 전송받은 데이터를 응용 프로그램에서 사용 가능한 객체로 복원
  • 저장된 데이터를 메모리에 로드

 

Spring에서의 직렬화 & 역직렬화 : ObjectMapper

Spring(Java)에서 직렬화는 일반적으로 Jackson 라이브러리를 활용합니다. 이 중 Jackson 라이브러리의 ObjectMapper라는 클래스가 있는데 이 클래스가 직렬화를 처리합니다.

 

Spring Boot는 기본적으로 Jackson을 내장하고 있으며, 이를 활용하여 JSON 데이터를 Java 객체로 변환하거나, Java 객체를 JSON으로 변환할 수 있습니다.

 

client에서 보낸 Json 데이터 DTO 객체로 받으면, Spring에서 알아서 Json의 Key값과 DTO 객체의 멤버변수를 매칭해서 Value값을 DTO 객체에 자동으로 대입해주었는데, 그 과정을 바로 SpringBoot의 Object Mapper 클래스가 해주게 됩니다.


ObjectMapper란❓

ObjectMapper는 Jackson 라이브러리의 핵심 클래스로, Java 객체와 JSON 데이터 간의 변환을 담당합니다. 객체 지향 프로그래밍과 데이터 교환 형식 간의 다리 역할을 수행합니다.

 

주요 기능:

1. 직렬화(Serialization)

String json = objectMapper.writeValueAsString(myObject);

 

Java 객체 JSON 문자열로 변환합니다.


2. 역직렬화(Deserialization)

MyClass obj = objectMapper.readValue(jsonString, MyClass.class);

 

JSON 문자열을 Java 객체로 변환합니다.


3. 다양한 입출력 지원

objectMapper.writeValue(new File("data.json"), myObject);
MyClass obj = objectMapper.readValue(new File("data.json"), MyClass.class);

 

문자열뿐만 아니라 파일, 스트림, URL 등 다양한 소스에서 읽고 쓸 수 있습니다.


4. 트리 모델 지원

JsonNode rootNode = objectMapper.readTree(jsonString);
String name = rootNode.get("name").asText();

 

JSON을 트리 구조로 처리할 수 있는 JsonNode API를 제공합니다.

 

ObjectMapper 커스텀

기본적으로 제공되는 ObjectMapper를 사용해도 되지만, 커스텀이 필요한 경우가 있습니다.

 

커스터마이징할 상황:

  1. 날짜/시간 형식 지정
    • 기본적으로 JSON에서는 날짜를 타임스탬프로 표현하지만, "yyyy-MM-dd HH:mm:ss"와 같은 특정 형식으로 직렬화/역직렬화하고 싶은 경우
  2. null 값 처리
    • null 값을 가진 필드를 JSON에서 제외하거나 특정 방식으로 처리해야 하는 경우
  3. 알 수 없는 속성 처리
    • API 버전 호환성을 위해 알 수 없는 JSON 속성이 있어도 예외가 발생하지 않도록 설정
  4. Enum 값 처리
    • Enum 값을 단순 이름이 아닌 다른 방식(예: toString() 메소드 결과)으로 직렬화/역직렬화
  5. 명명 전략 적용
    • Java의 camelCase를 JSON의 snake_case로 변환하는 등의 필드 이름 전략 설정
  6. 특정 타입 변환기 등록
    • 복잡한 객체 타입을 위한 커스텀 직렬화/역직렬화 로직 추가
  7. 모듈 등록
    • Java 8 날짜/시간 타입 지원이나 추가 데이터 타입 처리를 위한 모듈 등록
  8. 보안 설정
    • 민감한 데이터를 자동으로 마스킹하거나 제외시키는 설정
  9. 성능 최적화
    • 특정 기능을 비활성화하여 성능을 향상시키는 설정
  10. 다형성 처리
    • 상속 관계에 있는 클래스들을 적절하게 처리하기 위한 설정

이 외에도 자신의 프로젝트에 맞는 커스텀 ObjectMapper가 필요한 경우가 있을 것 입니다. 이러한 커스텀이 필요한 경우를 살펴보겠습니다.


예시: 명명 전략 적용 (camelCase ↔ snake_case)

@Configuration
public class JacksonConfig {
    
    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        var objectMapper = new ObjectMapper();
        
        // 모듈 등록
        objectMapper.registerModule(new Jdk8Module());  // Java 8 타입 지원
        objectMapper.registerModule(new JavaTimeModule());  // Java 시간 관련 타입 지원
        
        // 기본 설정
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);  // 날짜를 타임스탬프가 아닌 ISO-8601 형식으로
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);  // 알 수 없는 속성 무시
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);  // 빈 객체 직렬화 허용
        
        // 명명 전략 설정 (camelCase -> snake_case)
        objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());
        
        return objectMapper;
    }
}

 

SnakeCaseStrategy를 적용하여 Java의 camelCase 필드명(예: firstName)을 JSON의 snake_case 형식(예: first_name)으로 변환합니다. 

 

이런식으로 default로 제공되는 objcetMapper 이외에 추가적인 커스텀이 필요한 경우  ObjectMapper커스텀하여 Bean에다 등록하여 사용할 수 있습니다.