TIL

20221224 TIL enum으로 리팩토링!

jiwoosmile 2022. 12. 24. 22:56

 

며칠 전 짝꿍님께서 올려주신 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이 잘 적용이 된다!

 

프로젝트를 하면서 다채로운 방법들을 익히고 있다!

에러를 해결하지 못하는 동안은 끝이 없는 터널을 걷는 기분이지만, 해결을 하고 나면 생각보다 별 것이 아닌 것 같다.

더 좋은 방식이 있다면 과감하게 시도를 해보자.

 

참고

https://sunghs.tistory.com/126