끄적끄적

[JAVA] Enum 삽질기.. 본문

Back-end/Java

[JAVA] Enum 삽질기..

mashko 2021. 11. 11. 21:31
반응형

오늘은 이넘에 대한 삽질기를 기록해 둘까한다.
그리고 구글검색을 해도 너무 자료가 없었다..
그래서 정리해 두려고 한다.
먼저 요구사항이 Enum에 대한 리스폰스시 이넘코드에 대한 값을 json형태로 내려달라는 요청이 왔다..
처음에 그냥 @JsonFormat(shape = Shape.OBJECT)로 내렸는데
문제가 있었다.
스웨거에서도 리퀘스트 요청에 대해서 json 키밸류가 전부 파라미터로 요청받게 되어버렸기 때문에 실제로 스웨거를 통해 요청을 할때 혼돈이 있을 수 있었다.
개인적으로 이런걸.. 정말 싫어하기에 리펙토링을 시작하며 삽질이 시작되었다.
그럼 @JsonFormat(shape = Shape.OBJECT)을 사용하지 않고 데이터를 내려볼때만 변환시켜주면 안될까해서
비슷한경우를 찾아보니 ObjectMapper를 이용해서 이넘마다 따로 JsonCreator같은걸 두지 않아도 처리하는 방법을 찾았다.
운이 좋게도 나의 형태와 굉장히 비슷했다. 이러저러한 이유가 있어 BaseEnum이란 구현체를 통해 이넘에 대한 처리를 관리하고 있었는데 적절한 예시였다.
먼저 삽질할때의 과정은 따로.. 일일히 찍어두지 않아 결과만 보겠다.

public interface BaseEnum {
    Object getCode();
    String getName();
    String getDesc();

    /**
     * 엔티티에 세팅된 코드 검증
     */
    static <S extends BaseEnum> S getEnum(Class<S> cls, Object code) {
        for (BaseEnum e : cls.getEnumConstants()) {
            if (code.equals(e.getCode()) || code.equals(e.getName()) || code.equals(e.getDesc())) return (S) e;
        }

        throw new UnknownEnumCodeException(cls, code);
    }
}
@Getter
@AllArgsConstructor
public enum DiscountType implements BaseEnum {
    RATE(4002, "RATE", "정률"),
    PRICE(4001, "PRICE", "정액");

    private final Integer code;
    private final String name;
    private final String desc;
}
@Configuration
public class JacksonConfiguration {

    @Bean
    @Primary
    public ObjectMapper enumTypeSerializing() {
        ObjectMapper objectMapper = new ObjectMapper();

        SimpleModule module = new SimpleModule();

        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<BaseEnum> modifyEnumDeserializer(DeserializationConfig config,
                                                                 final JavaType type,
                                                                 BeanDescription beanDesc,
                                                                 final JsonDeserializer<?> deserializer) {
                return new JsonDeserializer<BaseEnum>() {
                    @Override
                    public BaseEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
                        Class<? extends BaseEnum> rawClass = (Class<? extends BaseEnum>) type.getRawClass();

                        return BaseEnum.getEnum(rawClass, jp.getValueAsString());
                    }
                };
            }
        });

        module.addSerializer(BaseEnum.class, new StdSerializer<BaseEnum>(BaseEnum.class) {
            @Override
            public void serialize(BaseEnum value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                jgen.writeStartObject();
                jgen.writeStringField("code", String.valueOf(value.getCode()));
                jgen.writeStringField("name", value.getName());
                jgen.writeStringField("desc", value.getDesc());
                jgen.writeEndObject();
            }
        });

        objectMapper.registerModules(new JavaTimeModule(), module);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        return objectMapper;
    }
}

이렇게 개발을 해두니 requestBody에 대한 이넘이나 response시에 이넘에 대한 Json으로 잘 되는것 같다
JsonDeserializer에서는 이넘안에 해당되는 값들중 하나라도 매칭이 되면 매칭된 값을 반환해주는 형태로 만들어두었고,
addSerializer에서는 뽑을때 필요한 값들을 key-value형태로 만들었다.
마지막으로 ObjectMapper 모듈을 등록하고 테스트해보니 LocalDateTime같은 날짜형식에서 문제가 되었다.
그래서 new JavaTimeModule()에 대한 모듈을 추가해주니 잘된다.
파라미터에 관련된 부분도 분명히 필요할듯해서 어떻게 처리했는지 남겨두겠다.

public class StringToEnumConverterFactory implements ConverterFactory<String, BaseEnum> {

    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnumConverter(targetType);
    }

    private static class StringToEnumConverter<T extends BaseEnum> implements Converter<String, T> {

        private Class<T> enumType;

        public StringToEnumConverter(Class<T> enumType) {
            this.enumType = enumType;
        }

        public T convert(String source) {
            if (Objects.isNull(source)) return null;

            // 스트링 값으로 변환하여 검증
            return BaseEnum.getEnum(enumType, source.toUpperCase());
        }
    }
}

생각보다 할일이 정말 많았다..

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        /**
         * request parameter converters
         */
        registry.addConverterFactory(new StringToEnumConverterFactory());
    }
}

마지막 등록을 끝으로 테스트를 해보았다.
이젠 모든 요청에 대해 잘된다...
하루종일 삽질한 보람이 있는 듯 했다

반응형

'Back-end > Java' 카테고리의 다른 글

[SPRING + JPA] QueryDsl FetchJoin  (0) 2021.11.16
[JAVA] JavaTimeModule 포멧팅  (0) 2021.11.12
[JAVA] Spring Boot json Enum json object or jsonvalue  (0) 2021.11.10
[JAVA] JVM 동작원리  (0) 2021.05.10
[JAVA] 의존성 주입(DI)  (0) 2021.05.06
Comments