[Spring] Enum 직렬화/역직렬화 문제: @JsonValue와 @JsonCreator로 해결하기

eunoia73·2025년 2월 23일

trouble shooting

목록 보기
15/16

1. 문제 개요

❗️문제 코드


public enum ContentType {
    APPLICATION_JSON("application/json"),
    APPLICATION_X_WWW_FORM_URLENCODED("application/x-www-form-urlencoded");

    private final String value;
    ContentType(String value) {
        this.value = value;
    }
    @Override
    public String toString() {
        return value;
    }
}

❗️오류 상황
AI를 이용한 사내 캘린더 만들기 프로젝트에서 팀 캘린더에 웹훅을 등록하는 기능을 postman으로 테스트하던 중 오류가 발생했다.

POST /api/notification/webhook

{
  "calendarId": 1,
  "payloadUrl": "https://example.com/webhook",
  "contentType": "application/json"
}

2. 오류 코드 및 원인 분석

❗️오류 문구 : org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type 'com.elice.iliceworksbe.common.constant.ContentType' from String "application/x-www-form-urlencoded": not one of the values accepted for Enum class: [APPLICATION_X_WWW_FORM_URLENCODED, APPLICATION_JSON]

💡오류 원인:
ContentType이 Enum타입인데, JSON에서 "application/json" 값을 받았지만, ContentType Enum에서는 APPLICATION_X_WWW_FORM_URLENCODED, APPLICATION_JSON 값만 유효함.
-> Enum이 허용하는 값과 실제 JSON에서 온 값이 다르기 때문에 Spring이 변환할 수 없어서 예외(HttpMessageNotReadableException)가 발생함.

3. 해결방법

✅ 선택한 해결 방법

@JsonValue @JsonCreator 사용

public enum ContentType {
    APPLICATION_JSON("application/json"),
    APPLICATION_X_WWW_FORM_URLENCODED("application/x-www-form-urlencoded");

    private final String value;

    ContentType(String value) {
        this.value = value;
    }

    @JsonValue //JSON 직렬화 시 사용
    public String getValue() {
        return value;
    }

    @JsonCreator //JSON 역직렬화시 사용
    public static ContentType fromValue(String value) {
        for (ContentType type : ContentType.values()) {
            if (type.value.equalsIgnoreCase(value)) {
                return type;
            }
        }
        throw new BaseException(ErrorCode.INVALID_CONTENT_TYPE);
    }
}

❓선택 이유
Json 요청과 응답에서 application/json 유지됨
Enum 내부적으로는 APPLICATION_JSON을 유지하여 코드의 가독성을 높임

☑️ 직렬화와 역직렬화

구분설명예시
직렬화 (Serialization)Java 객체 → JSON 변환API 응답 시 (@ResponseBody)
역직렬화 (Deserialization)JSON → Java 객체 변환API 요청 처리 (@RequestBody)

다른 해결 방법

1. 컨트롤러에서 수동 변환

 @PostMapping
    public ResponseEntity<String> receiveWebhook(@RequestBody Map<String, Object> requestBody) {
        try {
        	// 요청 본문에서 contentType 추출
            String contentTypeValue = (String) requestBody.get("contentType");
            // ContentType Enum으로 변환
            ContentType contentType = ContentType.fromValue(contentTypeValue);

            // Dto 변환 처리
            return ResponseEntity.ok("Received: " + contentType);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("Invalid content type");
        }
    }

❗️ 유지보수가 어렵고 비효율적

2. @JsonProperty 사용 -> 완전한 해결책❌

public enum ContentType {
    @JsonProperty("application/x-www-form-urlencoded")
    APPLICATION_X_WWW_FORM_URLENCODED,

    @JsonProperty("application/json")
    APPLICATION_JSON
}

❗️ 직렬화는 가능하지만 역직렬화는 제대로 작동하지 않을 가능성이 크다.
직렬화할 때는 "application/json"으로 변환되어 정상작동하지만
역직렬화할 때는 application/json -> APPLICATION_JSON으로 변환되지 않아 오류 발생(HttpMessageNotReadableException)

3. @JsonEnumDefaultValue 사용

public enum ContentType {
    APPLICATION_JSON("application/json"),
    APPLICATION_X_WWW_FORM_URLENCODED("application/x-www-form-urlencoded");

    private final String value;

    ContentType(String value) {
        this.value = value;
    }

    @JsonEnumDefaultValue
    public static final ContentType DEFAULT = APPLICATION_JSON;
}

❗️잘못된 값을 보내도 예외가 발생하지 않아 디버깅 어려움


✅ 결론

💡 @JsonValue@JsonCreator를 사용한다.

  • JSON에서 "application/json"을 그대로 받아서 APPLICATION_JSON으로 변환 가능
  • JSON 응답에서도 "application/json"이 유지됨
  • Enum을 그대로 사용할 수 있어 코드 가독성이 높음

0개의 댓글