본문 바로가기

JAVA

[JAVA] Enum 클래스 정복하기!

Enum이란?

JDK 1.5부터 추가된 Enum은 열거형이라고 하며, 서로 관련된 상수를 편리하게 선언하고 관리하기 위해 만들어졌습니다.
이러한 Enum이 나오기 전에는 관련된 상수들을 어떻게 관리하였는지 한번 살펴보겠습니다!


Enum의 등장 배경

private final int SPRING = 1;
private final int SUMMER = 2;
private final int AUTUMN = 3;
private final int WINTER = 4;

Enum이 등장하기 전까지는 위와 같은 정수 열거 패턴을 사용하였습니다. 하지만 이러한 정수 열거 패턴엔 여러 문제점이 존재하였습니다.

  1. 가독성이 떨어져 관리가 용이하지 않음
  2. 정수 상수들을 출력해도 1,2,3,4밖에 나오지 않고, 문자열로 출력하려면 따로 메소드를 만들어야 했다
  3. 정수 열거 그룹에 속한 모든 상수를 순회하는 법도 마땅치 않았다
  4. 오타로 인해 값이 변경되었다면 타입 세이프 하지 않기 때문에 SPRING, SUMMER가 같은 값으로 판별

이러한 문제들을 해결하기 위해 정수 열거 패턴의 단점을 모두 보완한 Enum이 등장하게되었습니다!

enum 열거형 이름{ 상수명1, 상수명2 ....}

public enum Season{
    SPRING, SUMMER, AUTUMN, WINTER
}

// enum을 클래스로 표현하면 밑의 코드와 비슷하다고 이해하시면 좋습니다!
public class Season{
    static final Season SPRING = new Season("SPRING");
    static final Season SUMMER = new Season("SUMMER");
    static final Season AUTUMN = new Season("AUTUMN");
    static final Season WINTER = new Season("WINTER");

    private String name;

    private Season(String name){
        this.name = name;
    }
}

Enum의 장점

  1. 타입 비교가 가능해 타입 세이프한 코드를 짤 수 있다(예기치 않은 문제를 미리 방지하고, 프로그램의 안정성 UP)
  2. 깔끔한 소스 작성을 할 수 있어 유지보수가 편리하다
  3. 위에서 언급한 단점들 해결 가능!

Enum 메서드

사용 예제를 보면서 메서드들을 이해해보겠습니다!

public class enumTest {
    public enum DayOfWeek{
        MON, TUE, WED, THU, FRI, SAT, SUN
    }

    public static void main(String[] args) {
        DayOfWeek.MON.getDeclaringClass(); // == Class<DayOfWeek>

        Arrays.stream(DayOfWeek.values()).forEach( day -> {
            System.out.println("ordinal : " + day.ordinal() +" name : "+day.name());
        });

//        ordinal : 0 name : MON
//        ordinal : 1 name : TUE
//        ordinal : 2 name : WED
//        ordinal : 3 name : THU
//        ordinal : 4 name : FRI
//        ordinal : 5 name : SAT
//        ordinal : 6 name : SUN

        DayOfWeek.valueOf("MON"); // == DayOfWeek.MON
        DayOfWeek.valueOf("TUE"); // == DayOfWeek.TUE
    }
}

ordinal() 메서드는 보통 해당 상수가 enum에서 몇번째 위치에 있는지 확인하려고 사용하는데, 이는 사용할때 주의해야 할 점이 있습니다.

public enum Ordinal{
    // 원래 상수 순서
    // ONE, TWO, THREE ... NINE, TEN
    TEN, ONE, TWO, THREE // 이와 같이 유지보수하면서 상수 선언 순서 바뀜
}

의도한 상수 순서가 만약에 유지보수 하면서 바뀐다면? ordinal()을 호출했던 로직들을 모두 깨질 것입니다
-> 이를 해결하려면 enum에 인스턴스 변수를 추가하는 방법을 사용해야 합니다.

public enum Ordinal{
    TEN(10), ONE(1), TWO(2) .....

    private final int value;

    Ordinal(int value) { this.value = value;}

    public int getValue() { return value; }
}

Enum에 멤버 추가하기

Enum에 멤버를 추가하는 이유는 enum 상수와 연관된 데이터를 상수 자체에 포함시켜 용이하게 관리하기 위함입니다!

이러한 인스턴스 필드를 추가하려면 enum 상수의 이름 옆에 원하는 값을 괄호와 함께 적어주면 됩니다!

public enum SearchSite{

    NAVER("https://www.naver.com"),
    DAUM("https://www.daum.net"),
    GOOGLE("https://www.google.com"),
    BING("https://www.bing.com");

    private final String url; // 인스턴스 필드 추가

    SearchSite(String url){
        this.url = url;
    }

    public String getUrl(){
        return url;
    }
}
// NAVER 예시
public class SearchSite{
    static final SearchSite NAVER = new SearchSite("NAVER", "https://www.naver.com");

    private final String name;
    private final String url;

    SearchSite(String name, String url){
        this.name = name;
        this.url = url;
    }
}

실제 업무 적용 사례 예시

보통 장기간 변경되지 않는 국가코드, 전화번호 앞자리 등 어떤 코드들을 DB에 저장해서 사용했던 것을 Enum으로 변경해서 DB 쿼리를 최소화해 부하를 줄인 사례가 있어 간단하게 살펴보겠습니다!!!

회원가입을 할때 지역번호 select 박스에 나오는 값들을 매번 DB의 테이블에서 읽어서 뿌려줬는데 이러한 지역번호는 거의 바뀌지 않기에, enum으로 만들어서 메모리에 올려놓고 호출하는 방법으로 리팩터링하였습니다.

DB 커넥션 -> 쿼리 실행(회원 가입 창에 접근할때 마다) 과정을 static으로 생성된 리스트를 호출하는 방법으로 변경해 서버 자원 낭비를 최소화하였습니다.

public enum AreaCode {

    A01("02", "서울특별시"),  A02("031", "경기도"), A03("032", "인천광역시"), A04("033", "강원도"), A05("041", "충청남도"),
    A06("042", "대전광역시"), A07("043", "충청북도"), A08("044", "세종특별자치시"), A09("051", "부산광역시"), A10("052", "울산광역시"),
    A11("053", "대구광역시"), A12("054", "경상북도"), A13("055", "경상남도"), A14("061", "전라남도"), A15("062", "광주광역시"),
    A16("063", "전라북도"), A17("064", "제주특별자치도");

    private String areaCode;
    private String sido;

    AreaCode(String areaCode, String sido) {
        this.areaCode = areaCode;
        this.sido = sido;
    }

    private static final List<Map<String, String>> areaCodes = Arrays.stream(values())
                                                                                    .map(AreaCode -> {
                                                                                           Map<String, String> m = new HashMap();
                                                                                           m.put("cd", AreaCode.name());
                                                                                           m.put("nm", AreaCode.areaCode);
                                                                                           return m;
                                                                                    })
                                                                                       .collect(Collectors.toList());

    public static List<Map<String, String>> getAreaCodes() {
        return areaCodes;
    }
}

지금까지 긴글 읽어 주셔서 감사드리고, 이해하시는데 조금이라도 도움이 되셨으면 좋겠습니다!


참고

https://effortguy.tistory.com/25

http://www.tcpschool.com/java/java_api_enum