20221224 TIL enum으로 리팩토링!
며칠 전 짝꿍님께서 올려주신 TIL에서 enum을 이용해서 리팩토링을 진행했다는 글을 보고,
나도 enum을 적용할 수 있는 부분이 있는 것 같아서 적용을 해보았다!
그렇지 않아도 작은 오타로 문제가 발생할 수 있을 것 같아서 리팩토링을 하는 게 좋지 않을까 생각해보고 있었는데,
짝꿍님의 도전으로 나도 용기를 내어 리팩토링을 시도해보았다.
enum은 인스턴스의 개수가 정해져있는 경우에 사용하게 된다.
대표적으로 상태 값을 사용할 때 유용한 것 같다.
내가 만든 VO 중에 질문 상태에 대한 VO가 있었고, 이 부분을 enum을 이용해서 리팩토링해보았다.
기존에는 아래와 같은 상태였다.
값 객체이기에 equals와 hashCode를 오버라이딩 해주었다.
// models/QuestionStatus.java
@Embeddable
public class QuestionStatus {
private static final QuestionStatus OPEN =
new QuestionStatus("open");
private static final QuestionStatus CLOSED =
new QuestionStatus("closed");
@Column(name = "questionStatus")
private String value;
public QuestionStatus() {
}
public QuestionStatus(String value) {
this.value = value;
}
public static QuestionStatus open() {
return OPEN;
}
public static QuestionStatus closed() {
return CLOSED;
}
public String value() {
return value;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
QuestionStatus otherQuestionStatus = (QuestionStatus) other;
return Objects.equals(value, otherQuestionStatus.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
enum을 사용하면, equals와 hashCode를 재정의해주지 않아도 되고, 아래와 같이 리팩토링 할 수 있다.
코드가 훨씬 간결해졌고, 실수가 발생할 확률이 낮아졌다.
// models/QuestionStatus.java
public enum QuestionStatus {
OPEN("open"),
CLOSED("closed");
private static final Map<String, String> STATUS_MAP = Collections.unmodifiableMap(
Stream.of(values()).collect(Collectors.toMap(QuestionStatus::value, QuestionStatus::name))
);
private String value;
QuestionStatus() {
}
QuestionStatus(String value) {
this.value = value;
}
public String value() {
return value;
}
public static QuestionStatus of(final String value) {
return QuestionStatus.valueOf(STATUS_MAP.get(value));
}
}
질문의 상태에 따라서 필터링을 해야했기 때문에 위와 같이
"open", "closed" 등의 status를 받아서 enum 타입으로 반환하는 정적 팩토리 메서드도 만들어 보았다.
아래와 같은 테스트 코드를 통해 잘 작동하는지 검증해볼 수 있다.
class QuestionStatusTest {
@Test
void of() {
String status = "open";
QuestionStatus questionStatus = QuestionStatus.of(status);
assertThat(questionStatus.value()).isEqualTo(status);
}
}
그리고 enum타입을 사용하고 있는 @Entity에서 아래와 같이 @Enumerated(EnumType.STRING)을 붙여주면
DB에 enum의 name (내 코드에서는 OPEN 과 CLOSED)이 숫자형 대신 저장된다.
// models/Question.java
@Enumerated(EnumType.STRING)
private QuestionStatus status;
한 가지 걱정되는 부분은 Specification을 정의해둔 부분이었는데,
enum타입을 사용하는 경우에도 criteriaBuilder.equal이 잘 적용이 된다!
프로젝트를 하면서 다채로운 방법들을 익히고 있다!
에러를 해결하지 못하는 동안은 끝이 없는 터널을 걷는 기분이지만, 해결을 하고 나면 생각보다 별 것이 아닌 것 같다.
더 좋은 방식이 있다면 과감하게 시도를 해보자.
참고