
ERROR 10 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: No enum constant com.example.SignServer.Entity.ReportReason.DISLIKE; nested exception is java.lang.IllegalArgumentException: No enum constant com.example.SignServer.Entity.ReportReason.DISLIKE] with root cause
java.lang.IllegalArgumentException: No enum constant com.example.SignServer.Entity.ReportReason.DISLIKE
at java.base/java.lang.Enum.valueOf(Unknown Source)
org.springframework.dao.InvalidDataAccessApiUsageException: No enum constant com.example.SignServer.Entity.ReportReason.DISLIKE; nested exception is java.lang.IllegalArgumentException: No enum constant com.example.SignServer.Entity.ReportReason.DISLIKE
스프링부트로 게시글과 댓글에 신고 기능을 구현하는 도중 이런 오류가 발생하며 신고데이터가 제대로 들어가지 않고 있습니다.
이 오류 로그를 자세히 살펴보니 InvalidDataAccessApiUsageException 예외와 IllegalArgumentException 예외에 대해서 출력이 된 것을 볼 수 있습니다.
InvalidDataAccessApiUsageException 예외는 스프링 데이터 액세스 계층에서 발생하는 예외입니다.
이 예외는 데이터 액세스 계층에서 잘못된 API 사용으로 인해 발생하며 잘못된 쿼리나 조건을 사용하여 데이터를 조회하려고 할 때나 잘못된 데이터 유형을 사용하여 데이터를 저장하려고 할 때, 또는 데이터베이스 연결이 올바르게 설정되지 않았을 때 등등 나타나는 예외입니다.
이 예외의 해결방법은 데이터 유형을 올바르게 매핑해야 합니다. 데이터베이스에서 사용하는 데이터 유형과 스프링에서 사용하는 데이터 유형이 일치하여야 한다는 뜻입니다.
다음으로 IllegalArgumentException 예외에 대해 살펴보겠습니다.
IllegalArgumentException 예외는 메서드에 잘못된 인수가 전달되었을 때 발생하는 예외입니다.
이 예외는 주로 잘못된 값이 열거형(Enum) 상수로 전달되었을 때나 메서드의 매개변수가 null이 아닌데 null로 전달되었을 때, 잘못된 형식의 인수가 전달되었을 때 위 예외가 발생합니다.
이 예외의 해결방법은 간단합니다. 열거형(Enum) 상수를 사용했다면 이 열거형을 사용할때 올바른 값을 사용하면 됩니다. 존재하지 않는 열거형 상수를 사용하려고 할 경우 이 예외가 발생하게 됩니다. 이말은 즉 열거형 상수를 변경하였을때도 발생이 가능하다는 것입니다.
이제 왜 이런 오류가 발생했었는지 제가 작성한 신고 이유 클래스를 한 번 살펴보도록 하겠습니다.
public enum ReportReason {
DISLIKE("내용이 마음에 들지 않아요"),
SEXUAL("내용이 선정적이에요"),
TERRORISM("내용이 테러를 조장해요"),
INAPPROPRIATE("대댓글이 부적절해요"),
SPAM("대댓글이 스팸이에요");
private String description;
ReportReason(String description) {
this.description = description;
}
@JsonCreator
public static ReportReason forValue(String value) {
for (ReportReason reportReason : ReportReason.values()) {
if (reportReason.name().equals(value)) {
return reportReason;
}
}
throw new IllegalArgumentException("신고 이유가 잘 못 됐습니다 : " + value);
}
@JsonValue
public String toValue() {
return name();
}
public String getDescription() {
return description;
}
}
이전의 댓글을 신고하는 클래스에서는 열거형(Enum) 값을 영어로 신고 이유를 설정해 주었습니다. 이렇게 되면 이전에 코드로 댓글을 신고할시 데이터베이스 신고 이유 컬럼에는 영어로 설정한 DISLIKE, SEXUAL, TERRORISM 등의 이유들이 영어로 저장되게 됩니다.
package com.example.SignServer.EnumData;
public enum ReportReason {
마음에들지않아요, 선정적이에요, 테러를조장해요,
부적절해요, 스팸이에요
이후 수정한 댓글 신고 이유 클래스입니다.
코드의 가독성을 위해 이전 코드와 달라진 점이 많이 보이실 겁니다.
그리고 중요한 것은 신고 이유를 영문에서 한국어로 변경해 주었다는 것입니다. 이 부분이 문제였던 것 같습니다.
다시 오류가 발생한 로그를 확인하면 문자열 "DISLIKE"가 Enum 값으로 변환되려면 신고 이유 클래스 Enum에 정확히 해당 문자열과 일치하는 값을 기대하고있는데 이 값을 변경과 동시에 찾을 수 없어 오류가 발생한 것으로 보입니다.
정리를 해보자면 이 오류는 신고 이유의 데이터를 추가하고 변경에 있어서 클라이언트로부터 받은 문자열이 신고 이유 Enum의 어떤 값과도 일치하지 않기 때문에 발생한 것 같습니다. 즉 "DISLIKE"라는 문자열이 신고 이유 Enum에 없기 때문에 이런 오류가 발생한 것 입니다.
이 문제를 해결하기 위해서는 신고 이유를 변경한 문자열로 변환하는 로직이 필요합니다.
String reportReasonString = payload.get("reportReason").replaceAll("수정할 Enum 값", "수정된 Enum 값");
위 코드는 해당 테이블의 컨트롤러 클래스의 신고 메서드에 위치시키면 됩니다.
위 코드를 이용한다면 클라이언트로부터 받은 신고 이유를 변경하고 그 결과를 신고 메서드에 전달하게 됩니다. 이렇게하면 서비스 클래스에서 이를 Enum 값으로 변환이 가능하게 될 것입니다.
이 방법이 어렵게 느껴지신다면 데이터베이스를 아에 초기화 하는 방법도 있습니다. 이렇게 하면 입력된 데이터들이 모두 초기화가 되고 해당 예외를 해결 할 수 있습니다.
하지만 이 방법도 테이블 구조가 외래키와 복잡한 관계를 가지고 있다면 해당 테이블만 초기화하는 것은 매우 복잡하고 어려워질 수 있습니다.
이 방법 조차도 어렵게 느껴지신다면 데이터베이스 자체를 삭제하고 다시 만드는 방법을 하시면 됩니다.
하지만 위 방법들은 데이터 손실을 초래할 수 있으니 잘 확인하시고 사용하시기 바랍니다.
웬만하면 신고 이유를 변경한 문자열로 변환하는 방법을 추천드립니다.
데이터를 초기화 시키거나 아무렇지 않게 지워도 상관이 없으시다면 귀찮아지지않게 데이터베이스를 아에 삭제하고 다시 생성하시는 방법도 추천드립니다.
열거형 Enum 값은 띄어쓰기와 대소문자 등을 구분하므로 까다롭다고 생각하시는 분들도 많으실 겁니다. 이는 클라이언트로부터 받은 문자열과 정확히 일치하는지 잘 확인해봐야하며 수정이 있다면 다시 프론트 쪽에 알려야 하는 등 쓸데없이 손봐야하는 일들이 많아져 귀찮아 질수도 있습니다.
클라이언트와 서버 사이에 데이터를 주고받을 때 열거형 Enum 값을 문자열로 주고받는 것이 아니라 각 Enum 값에 해당하는 고유한 코드를 주고받는 방식을 생각해보는 것도 이번 포스팅에서 다룬 예외를 피할 수 있는 방법 중 하나라고 생각합니다.
사진 출처 : 구글 이미지