1. 개요

JSON 형식을 사용할 때 Spring Boot는 ObjectMapper 인스턴스를 사용하여 응답을 직렬화하고 요청을 역 직렬화합니다. 이 사용방법(예제)에서는 직렬화 및 역 직렬화 옵션을 구성하는 가장 일반적인 방법을 살펴 봅니다.

Jackson에 대해 자세히 알아 보려면 Jackson 예제 을 확인하십시오 .

2. 기본 구성

기본적으로 Spring Boot 구성은 다음을 수행합니다.

  • MapperFeature.DEFAULT_VIEW_INCLUSION 비활성화
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 비활성화
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 비활성화

간단한 예부터 시작하겠습니다.

  • 클라이언트는 / coffee? name = Lavazza에 GET 요청을 보냅니다.
  • 컨트롤러는 새로운 Coffee 객체를 반환 합니다.
  • Spring은 ObjectMapper사용 하여 POJO를 JSON으로 직렬화합니다.

StringLocalDateTime 개체를 사용하여 사용자 지정 옵션을 예시하겠습니다 .

public class Coffee {

    private String name;
    private String brand;
    private LocalDateTime date;

   //getters and setters
}

또한 직렬화를 보여주기 위해 간단한 REST 컨트롤러를 정의합니다 .

@GetMapping("/coffee")
public Coffee getCoffee(
        @RequestParam(required = false) String brand,
        @RequestParam(required = false) String name) {
    return new Coffee()
      .setBrand(brand)
      .setDate(FIXED_DATE)
      .setName(name);
}

기본적으로 GET http : // lolcahost : 8080 / coffee? brand = Lavazza호출 할 때의 응답 은 다음과 같습니다.

{
  "name": null,
  "brand": "Lavazza",
  "date": "2020-11-16T10:21:35.974"
}

null을 제외 하고 사용자 지정 날짜 형식 (dd-MM-yyyy HH : mm)을 사용하려고합니다. 최종 응답은 다음과 같습니다.

{
  "brand": "Lavazza",
  "date": "04-11-2020 10:34"
}

Spring Boot를 사용할 때 기본 ObjectMapper 를 사용자 정의 하거나 재정의 하는 옵션이 있습니다 . 다음 섹션에서이 두 가지 옵션을 모두 다룰 것입니다.

3. 기본 ObjectMapper 사용자 지정

이 섹션에서는 Spring Boot가 사용 하는 기본 ObjectMapper 를 사용자 지정하는 방법을 살펴 보겠습니다 .

3.1. 응용 프로그램 속성 및 사용자 지정 Jackson 모듈

매퍼를 구성하는 가장 간단한 방법은 응용 프로그램 속성을 사용하는 것 입니다. 구성의 일반적인 구조는 다음과 같습니다.

spring.jackson.<category_name>.<feature_name>=true,false

예를 들어 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS 를 비활성화하려면 다음을 추가합니다.

spring.jackson.serialization.write-dates-as-timestamps=false

언급 된 기능 범주 외에도 속성 포함을 구성 할 수도 있습니다.

spring.jackson.default-property-inclusion=always, non_null, non_absent, non_default, non_empty

환경 변수를 구성하는 것이 가장 간단한 방법입니다. 이 접근 방식의 단점은 LocalDateTime에 대한 사용자 지정 날짜 형식을 갖는 것과 같은 고급 옵션을 사용자 지정할 수 없다는 것 입니다. 이 시점에서 결과를 얻을 수 있습니다.

{
  "brand": "Lavazza",
  "date": "2020-11-16T10:35:34.593"
}

목표를 달성하기 위해 사용자 정의 날짜 형식으로 JavaTimeModule 등록 합니다.

@Configuration
@PropertySource("classpath:coffee.properties")
public class CoffeeRegisterModuleConfig {

    @Bean
    public Module javaTimeModule() {
        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(LOCAL_DATETIME_SERIALIZER);
        return module;
    }
}

또한 구성 속성 파일 coffee.properties 에는 다음이 포함됩니다.

spring.jackson.default-property-inclusion=non_null

Spring Boot는 com.fasterxml.jackson.databind.Module 유형의 모든 빈을 자동으로 등록합니다 최종 결과는 다음과 같습니다.

{
  "brand": "Lavazza",
  "date": "16-11-2020 10:43"
}

3.2. Jackson2ObjectMapperBuilderCustomizer

이 기능적 인터페이스의 목적은 구성 빈을 생성 할 수 있도록하는 것입니다. Jackson2ObjectMapperBuilder 를 통해 생성 된 기본 ObjectMapper에 적용됩니다 .

@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
      .serializers(LOCAL_DATETIME_SERIALIZER);
}

구성 빈은 특정 순서로 적용되며 @Order  어노테이션을 사용하여 제어 할 수 있습니다 . 이 우아한 접근 방식은 다른 구성이나 모듈에서 ObjectMapper 를 구성하려는 경우에 적합 합니다.

4. 기본 구성 재정의

구성을 완전히 제어 하려면 자동 구성을 비활성화하고 사용자 지정 구성 만 적용 할 수있는 몇 가지 옵션이 있습니다 . 이러한 옵션을 자세히 살펴 보겠습니다.

4.1. ObjectMapper

기본 구성을 재정의하는 가장 간단한 방법은 ObjectMapper을 정의하고 @Primary 로 표시하는 것입니다 .

@Bean
@Primary
public ObjectMapper objectMapper() {
    JavaTimeModule module = new JavaTimeModule();
    module.addSerializer(LOCAL_DATETIME_SERIALIZER);
    return new ObjectMapper()
      .setSerializationInclusion(JsonInclude.Include.NON_NULL)
      .registerModule(module);
}

직렬화 프로세스를 완전히 제어하고 외부 구성을 허용하지 않으려는 경우이 접근 방식을 사용해야합니다 .

4.2. Jackson2ObjectMapperBuilder

또 다른 깨끗한 접근법은 Jackson2ObjectMapperBuilder을 정의하는 입니다. 실제로 Spring Boot는 ObjectMapper를 빌드 할 때 기본적으로이 빌더를 사용 하고 정의 된 것을 자동으로 선택합니다.

@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
    return new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
      .serializationInclusion(JsonInclude.Include.NON_NULL);
}

기본적으로 두 가지 옵션을 구성합니다.

  • MapperFeature.DEFAULT_VIEW_INCLUSION 비활성화
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 비활성화

Jackson2ObjectMapperBuilder 문서 에 따르면 클래스 경로에있는 경우 일부 모듈도 등록합니다.

  • jackson-datatype-jdk8 : Optional 과 같은 다른 Java 8 유형 지원
  • jackson-datatype-jsr310 : Java 8 날짜 및 시간 API 유형 지원
  • jackson-datatype-joda : Joda-Time 유형 지원
  • jackson-module-kotlin : Kotlin 클래스 및 데이터 클래스 지원

이 접근 방식의 장점은 Jackson2ObjectMapperBuilderObjectMapper 를 빌드하는 간단하고 직관적 인 방법을 제공 한다는 것 입니다.

4.3. MappingJackson2HttpMessageConverter

MappingJackson2HttpMessageConverter 유형으로 빈을 정의 할 수 있으며 Spring Boot는 자동으로이를 사용합니다.

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
      .serializationInclusion(JsonInclude.Include.NON_NULL);
    return new MappingJackson2HttpMessageConverter(builder.build());
}

자세한 내용은 Spring Http Message Converters 기사 를 확인하십시오  .

5. 구성 테스트

구성을 테스트하기 위해 TestRestTemplate을 사용 하고 객체를 String으로 직렬화합니다 . 이런 식으로 Coffee 객체가 null없이 사용자 지정 날짜 형식으로 직렬화되었는지 확인할 수 있습니다 .

@Test
public void whenGetCoffee_thenSerializedWithDateAndNonNull() {
    String formattedDate = DateTimeFormatter.ofPattern(CoffeeConstants.dateTimeFormat).format(FIXED_DATE);
    String brand = "Lavazza";
    String url = "/coffee?brand=" + brand;
    
    String response = restTemplate.getForObject(url, String.class);
    
    assertThat(response).isEqualTo("{\"brand\":\"" + brand + "\",\"date\":\"" + formattedDate + "\"}");
}

6. 결론

이 예제에서는 Spring Boot를 사용할 때 JSON 직렬화 옵션을 구성하는 몇 가지 방법을 살펴 보았습니다.

기본 옵션을 구성하거나 기본 구성을 재정의하는 두 가지 접근 방식을 보았습니다.

항상 그렇듯이 기사의 전체 소스 코드는 GitHub에서 사용할 수  있습니다 .