(Project)Enum Converter
(Project)Enum Converter
Enum Converter
현재 상황
Controller와Request를 담당하는DTO에서Enum 타입을 사용하고 싶은데,소문자로 데이터가 들어올 경우 아래 문구의 예외가 발생하는 문제가 있다.IllegalArgumentException: No enum constant team20.issuetracker.domain.issue.IssueStatus.open]왜 굳이
Enum을 사용해야 하는가??웹 애플리케이션에서 데이터의 값을 사전에 정의된 값으로 제한을 하고 싶어서, Enum을 사용
우리가 제한한 데이터 외의 값이 들어올 경우 예외 처리를 편리하게 할 수 있는 장점이 있다.
1
2
3
public enum IssueStatus {
OPEN, CLOSED;
}
IssueStatus의 경우OPEN이라는 값이 있지만소문자로 들어온open과는 다른 값으로 취급 되고 있다.
문제 상황
첫번째 문제 상황
1
2
3
4
5
6
7
@GetMapping(params = "is")
public ResponseEntity<ResponseReadAllIssueDto> readOpenAndClosedIssues(
@RequestParam(value = "page", required = false, defaultValue = "1") String page,
@RequestParam("is") IssueStatus status)
{
...
}
Controller에서Enum으로@RequestParam데이터를 바인딩을 하려는 상황에서Query String으로 전달된 데이터가소문자일 경우 바인딩 에러가 발생하고 있다.
두 번째 문제 상황
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RequestUpdateMilestoneDto {
@NotEmpty
@Size(max = 50, message = "Milestone 의 제목은 20글자를 넘을 수 없습니다.")
private String title;
@Size(max = 800, message = "Milestone 의 본문은 800글자를 넘을 수 없습니다.")
private String description;
@FutureOrPresent(message = "과거의 시간을 설정할 수는 없습니다.")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dueDate;
private MilestoneStatus milestoneStatus;
public static RequestUpdateMilestoneDto of(String title, String description, LocalDate dueDate, MilestoneStatus milestoneStatus) {
return new RequestUpdateMilestoneDto(title, description, dueDate, milestoneStatus);
}
}
- 첫번째 상황과 비슷한 상황으로 이번에는
Milestone을Update할때 현재 또는 변경되는 상태의 값을 받아야 하는데 이 값 또한Enum 타입으로 받고 있는 상황에서 데이터가 소문자로 들어 올 경우 바인딩 에러가 발생하고 있다.
문제 이유
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static class StringToEnum<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
@Override
@Nullable
public T convert(String source) {
if (source.isEmpty()) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
- 스프링에서 기본적으로 제공해주는
StringToEnum컨버터의 경우 위에 코드에 나와 있듯이 source 를 공백만 제거한 다음 별 다른 처리 없이valueOf를 해주고 있어소문자로 들어오면 그대로 소문자로,대문자로 들어오면 대문자로바인딩을 해주기 때문에 문제가 발생하고 있다.
해결 시도
첫번째 시도
프론트쪽에서 소문자가 아닌 대문자로 데이터를 보내줄수 없는지 요청을 드렸었다 하지만, 여러 작업을 이미 소문자로 진행하였기 때문에 변경이 쉽지는 않다고 이야기를 하셔서 백엔드쪽에서 해결하도록 결정하였다.
두번째 시도
1
2
3
4
5
6
7
8
9
@GetMapping(params = "is")
public ResponseEntity<ResponseReadAllIssueDto> readOpenAndClosedIssues(
@RequestParam(value = "page", required = false, defaultValue = "1") String page,
@RequestParam("is") String status)
{
...
ResponseReadAllIssueDto findOpenAndCloseIssues = issueService.findAllOpenAndCloseIssues
(pageRequest, IssueStatus.valueOf(status.toUpperCase());
}
- 위에 문제가 되는 값을 String 으로 받은 후
String.toUpperCase()를 사용해서 대문자로 변경한 뒤Enum에 적용 시키는 방법을 시도 - 위에 방법으로 해결이 가능하기는 했으나,
Enum을 사용하고자 했던 취지에는 벗어나기 때문에 다른 방법이 없나 다시 한번 찾아보기 시작했다.
세번째 시도
- 스프링
기본 Converter를 사용하는 것이 아니라Custom Converter를 만들어서 사용
1
2
3
4
5
6
7
8
9
10
public class StringToIssueStats implements Converter<String, IssueStatus> {
@Override
public IssueStatus convert(String status) {
try {
return IssueStatus.valueOf(status.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
}
Converter인터페이스를 구현하는 클래스StringToIssueStatus를 만들어서convert메서드를Override해서 내가 원하는 방식으로 변경하면 된다.기존에는 별다른 처리 없이
Enum.valueOf를 통해 데이터를 변환했다면, 지금은status라는 값이 들어올 경우toUpperCase()메서드를 사용해 대문자로 변경을 한 뒤 데이터를 변환하는 방식으로 진행되게끔 만들었다.
1
2
3
4
5
6
7
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIssueStats());
}
}
위에 클래스를 만든다고 해서 그냥 사용이 되는것은 아니고 Bean 으로 등록을 하거나
Config 설정클래스에서Converter로 추가해주면 기존Converter가 아닌 내가 구현한Converter를 사용하게 된다.Milestone Update또한 같은 방식으로Converter를 구현해서 사용하여 쉽게 해결 됐다.
REFERENCE
This post is licensed under CC BY 4.0 by the author.