티스토리 뷰

요청 값 처리에 대한 설정 부분입니다. 

요청 문자열에 대한 type converter 설정 
Json 관련 설정(json to object, object json)

 

1. 요청 문자열에 대한 type converter 설정

    @Autowired(required = false)
    private Converter<?, ?>[] converters;
    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        if(converters != null) {
            for(final Converter<?, ?> converter : converters) {
                registry.addConverter(converter);
            }
        }
        NumberStyleFormatter numberFormatter = new NumberStyleFormatter();
        numberFormatter.setPattern("#,###,###,###.##");
        registry.addFormatter(numberFormatter);
    }

이 부분은 이전에 설명했던 부분으로 타입에 따른 Converter 설정 부분입니다. 

git에 간단한 converter 몇 가지를 추가했으니 참고하시면 좋을 거 같습니다. 

 

2. Json 관련 설정(json to object, object json)

요청시 request body에 json 형태로 값을 전달하고 객체에 매핑하는 경우 특정 규칙이 필요할 수 있습니다. 

RestController에서 결과 객체를 반환할 때 json으로 변환 시 업무적인 특성에 따라 메시지 컨버팅을 해야 하는 경우가 있습니다. 

위와 같은 예에서 사용자 정의 컨버터를 통해 구현 할 수 있습니다.

일반적으로 ObjectMapper를 이용해 MappingJackson2HttpMessageConverter를 생성합니다. 

ObjectMapper 참고 : https://eblo.tistory.com/62

 관련 설정 

    @Bean
    public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        jsonConverter.setObjectMapper(ObjectMapperUtil.getObjectMapper());
        return jsonConverter;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(customJackson2HttpMessageConverter());
        super.addDefaultHttpMessageConverters(converters);
    }

ObjectMapper는 Utility 클래스가 아닌 스프링빈으로도 많이 사용합니다. 

아래 유틸 클래스에 objectMapper() 부분을 @Configuration에 @Bean으로 추가하면 됩니다. 

그런데 @Bean이 injection 받아서 쓰기는 좋으나 테스트 클래스나 유틸 클래스에서 스프링 Context와 상관없이 쓰고자 할때는 불편합니다. 그래서 일부러 Util에 구현했습니다. 

 

위에 configureMessageConverters() 설정만으로 설정은 끝입니다. 

@UtilityClass
public class ObjectMapperUtil {

    private static final ObjectMapper objectMapper = objectMapper();

    private ObjectMapper objectMapper() {
        ObjectMapper om = new ObjectMapper();
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // unknown field 무시 
        // 매핑시 필드만 사용. 
        om.setVisibility(om.getSerializationConfig().getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
        
        om.setSerializationInclusion(Include.NON_EMPTY); // 빈 값 제거 
        //om.enable(SerializationFeature.WRAP_ROOT_VALUE); // root를 사용할 때 
        //om.enable(DeserializationFeature.UNWRAP_ROOT_VALUE); // root로 값을 넘길 때 사용. 

        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        simpleModule.addSerializer(Date.class, new DateSerializer());
        simpleModule.addDeserializer(Date.class, new DateDeserializer());
        simpleModule.addSerializer(Timestamp.class, new TimestampSerializer());
        simpleModule.addDeserializer(Timestamp.class, new TimestampDeserializer());
        simpleModule.addSerializer(String.class, new StringTrimSerializer());
        simpleModule.addDeserializer(String.class, new StringTrimDeserializer());
        om.registerModule(simpleModule);
        return om;
    }

    public static ObjectMapper getObjectMapper() {
        return objectMapper;
    }
    
    public static String getJsonString(Object obj) {
        try {
        	if(obj != null)  return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static <T> T getObject(String jsonStr, Class<T> type) {
        try {
            return objectMapper.readValue(jsonStr, type);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static <T> T getObjectFromFile(String filePath, Class<T> type){
        try {
            return objectMapper.readValue(new File(filePath), type);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void saveStringToJsonFile(Object json, String jsonFilePath) {
        File file = new File(jsonFilePath);
        try(FileOutputStream fileOutputStream = new FileOutputStream(file, false)){
            ObjectWriter writer = objectMapper.writer(new DefaultPrettyPrinter());
            writer.writeValue(fileOutputStream, json);
            fileOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

어떤 경우 유용할까요? 

 

요청 사항

결과값

- 정의된 것이 값이 없더라도 다 나와야 한다. 

- null 인경우 "" 값으로 내려와야 한다. 

- 모든 값은 쌍따옴표로 감싸야 한다. 

- 특정 필드는 Currency 형태로 표현 필요하다. 

요청값 

- 숫자 필드에 콤마가 들어 갈 수 있으니 제거하고 매핑해야 한다. 

 

위와 같은 경우 타입별 serializer, deserializer 개발하여 추가해 줌으로써 해결 가능합니다. 

 

git에 간단하게 구현한 Serializer, Deserializer 예제 들이 있으니 참고하시면 좋을 거 같습니다. 

 

github : https://github.com/kkaok/study-springboot/blob/main/src/main/java/eblo/study/springboot/web/servlet/config/WebMvcConfiguration.java 

댓글
댓글쓰기 폼