[JAVA] 열거형(ENUM)에 대하여

2025. 6. 29. 19:01·자바

이번 글에서는 자바에서 열거형(enum)이 왜 필요한지, 그리고 어떻게 사용하는지에 대해 차근차근 살펴보겠다. 단순한 이론보다 실제 예제를 통해 문제점을 파악하고, 열거형으로 어떻게 개선할 수 있는지 과정을 따라가 보자.

 

먼저 많은 개발자들이 초기에 작성하는 코드부터 살펴보자. 아래는 고객의 등급에 따라 할인율을 적용하는 코드다.

public class DiscountService {
    public int discount(String grade, int price){
        int discountPercent = 0;

        if (grade.equals("BASIC")) {
            discountPercent = 10;
        } else if (grade.equals("GOLD")) {
            discountPercent = 20;
        } else if (grade.equals("DIAMOND")) {
            discountPercent = 30;
        } else {
            System.out.println(grade + ": 할인X");
        }

        return price * discountPercent / 100;
    }
}

 

이제 이 서비스 클래스를 활용해 실제로 할인 금액을 구해보자.

public class StringGradeEx0_1 {
    public static void main(String[] args) {
        int price = 10000;
        DiscountService discountService = new DiscountService();

        int basic = discountService.discount("BASIC", price);
        int gold = discountService.discount("GOLD", price);
        int diamond = discountService.discount("DIAMOND", price);

        System.out.println("BASIC 등급의 할인 금액: " + basic);
        System.out.println("GOLD 등급의 할인 금액: " + gold);
        System.out.println("DIAMOND 등급의 할인 금액: " + diamond);
    }
}

 

언뜻 보기엔 잘 동작하는 것처럼 보이지만, 사실 이 방식엔 여러 문제가 숨어 있다.

문자열 방식의 문제점

  • "basic", "Gold"처럼 대소문자 실수에 취약하다.
  • 존재하지 않는 등급을 넣어도 컴파일 에러가 발생하지 않는다.
  • 오타가 있어도 실행 전까지는 전혀 알 수 없다.
  • 코드 유지보수가 어렵고, 실수가 발생할 여지가 많다.

그래서 이런 문제를 해결하기 위해 상수 클래스를 사용해보자.

 

상수 클래스를 활용한 개선

다음처럼 등급을 상수로 관리하면 오타를 줄이고 비교도 안정적으로 할 수 있다.

public class StringGrade {
    public static final String BASIC = "BASIC";
    public static final String GOLD = "GOLD";
    public static final String DIAMOND = "DIAMOND";
}

 

그리고 할인 서비스도 이렇게 수정할 수 있다.

public class DiscountService {
    public int discount(String grade, int price){
        int discountPercent = 0;

        if (grade.equals(StringGrade.BASIC)) {
            discountPercent = 10;
        } else if (grade.equals(StringGrade.GOLD)) {
            discountPercent = 20;
        } else if (grade.equals(StringGrade.DIAMOND)) {
            discountPercent = 30;
        } else {
            System.out.println(grade + ": 할인X");
        }

        return price * discountPercent / 100;
    }
}

 

조금 나아졌지만, 여전히 다음과 같은 문제가 남아 있다.

  • 여전히 String이기 때문에 타입 안정성이 없다.
  • 다른 개발자가 StringGrade 클래스의 존재를 모르고 문자열을 직접 사용할 수 있다.

 

이제부터는 좀 더 근본적인 해결 방법을 살펴보자. 아예 클래스 자체로 등급을 정의하고, 외부에서 생성하지 못하도록 막는 방법이다.

public class ClassGrade {
    public static final ClassGrade BASIC = new ClassGrade();
    public static final ClassGrade GOLD = new ClassGrade();
    public static final ClassGrade DIAMOND = new ClassGrade();

    private ClassGrade() {} // 외부에서 생성 못하게 막는다
}

 

이 방식의 핵심은 private 생성자다. 이렇게 하면 ClassGrade.BASIC처럼 정의된 상수만 사용할 수 있다. 잘못된 값을 넣을 가능성이 원천 차단된다.

하지만 이렇게 직접 구현하는 건 다소 번거롭고, 코드도 복잡해진다. 이럴 때 사용할 수 있는 게 바로 자바에서 제공하는 enum이다.

 

열거형(enum)의 등장

열거형을 사용하면 위에서 구현한 방식과 같은 효과를 훨씬 간단하게 얻을 수 있다.

public enum Grade {
    BASIC, GOLD, DIAMOND
}

 

사실 이 코드는 내부적으로 아래와 비슷한 형태로 동작한다.

public class Grade {
    public static final Grade BASIC = new Grade();
    public static final Grade GOLD = new Grade();
    public static final Grade DIAMOND = new Grade();

    private Grade() {}
}

즉, 열거형은 타입 안정성도 보장하면서 코드도 훨씬 간결하게 만든다.

 

열거형(enum)을 사용하면 복잡한 if 문을 줄일 수 있다

앞에서 본 것처럼, enum을 단순히 상수를 나열하는 데만 그치지 않고, 관련 데이터를 함께 보관하고 로직까지 함께 정의할 수 있다는 점이 큰 장점이다.

이 방식은 특히 if-else, switch와 같은 조건문을 반복해서 사용해야 하는 상황에서 큰 효과를 발휘한다.

예를 들어 처음에 작성했던 discount() 메서드를 다시 떠올려보자.

public int discount(String grade, int price){
    int discountPercent = 0;

    if (grade.equals("BASIC")) {
        discountPercent = 10;
    } else if (grade.equals("GOLD")) {
        discountPercent = 20;
    } else if (grade.equals("DIAMOND")) {
        discountPercent = 30;
    } else {
        System.out.println(grade + ": 할인X");
    }

    return price * discountPercent / 100;
}

이 코드는 등급이 추가되거나 할인 정책이 바뀔 때마다 조건문을 수정해야 하고, 잘못된 문자열 입력에 대한 방어 코드도 필요하다. 변경에 취약하고, 코드가 길어질 수밖에 없다.

 

enum에 로직을 넣으면 if 문이 사라진다

하지만 enum 안에 할인율과 할인 계산 로직을 함께 넣어두면, 다음과 같이 간결하고 명확한 코드로 바꿀 수 있다.

public enum Grade {
    BASIC(10), GOLD(20), DIAMOND(30);

    private final int discountPercent;

    Grade(int discountPercent) {
        this.discountPercent = discountPercent;
    }

    public int discount(int price) {
        return price * discountPercent / 100;
    }
}

이렇게 해두면 DiscountService는 더 이상 어떤 등급이 얼마를 할인해주는지를 몰라도 된다. 그저 enum에 위임하면 된다.

 

 

즉 처음과 같이 비효율적인 코드는 아래와 같이 유지보수와 가독성이 좋게 바뀐다.

 

개선전: 

public class DiscountService {
    public int discount(String grade, int price){
        int discountPercent = 0;

        if (grade.equals("BASIC")) {
            discountPercent = 10;
        } else if (grade.equals("GOLD")) {
            discountPercent = 20;
        } else if (grade.equals("DIAMOND")) {
            discountPercent = 30;
        } else {
            System.out.println(grade + ": 할인X");
        }

        return price * discountPercent / 100;
    }
}
public class OriginalMain {
    public static void main(String[] args) {
        int price = 10000;
        DiscountService discountService = new DiscountService();

        int basic = discountService.discount("BASIC", price);
        int gold = discountService.discount("GOLD", price);
        int diamond = discountService.discount("DIAMOND", price);

        System.out.println("BASIC 등급의 할인 금액: " + basic);
        System.out.println("GOLD 등급의 할인 금액: " + gold);
        System.out.println("DIAMOND 등급의 할인 금액: " + diamond);
    }
}

 

개선 후:

public enum Grade {
    BASIC(10),GOLD(20),DIAMOND(30);

    private final int discountPercent;

    Grade(int discountPercent) {
        this.discountPercent = discountPercent;
    }

    public int getDiscountPercent() {
        return discountPercent;
    }

    public int discount(int price){
        return discountPercent * price /100;
    }
}
public class EnumMain {
    public static void main(String[] args) {
        int price=10000;

        for (Grade grade : Grade.values()) {
            printDiscount(grade, price);
        }
    }
    //프린트 함수
    private static void printDiscount(Grade grade,int price) {
        System.out.println(grade.name() + " 등급의 할인 금액: "+ grade.discount(price));
    }
}

 

예를 들어, SILVER 등급이 추가된다고 가정해보자.
개선 전 코드는 조건문과 문자열 비교를 일일이 수정해야 하기 때문에 코드 변경이 번거롭고 오류 가능성도 높다.
반면 enum을 사용하는 개선 후 코드는 SILVER 항목을 열거형에 하나 추가해주는 것만으로 모든 로직이 자동으로 연동되기 때문에 훨씬 간단하고 안전하다.

 

정리하며

처음에는 문자열로 등급을 관리했지만, 오타나 타입 오류에 취약한 것을 직접 확인했다. 이후 상수 클래스를 쓰거나 객체 패턴을 도입하면서 조금씩 개선했지만, 결국 가장 깔끔하고 안정적인 방법은 열거형(enum)을 활용하는 것이다.

열거형은 단순한 값 나열을 넘어서, 타입 안정성과 기능 캡슐화를 동시에 만족시켜준다. 그래서 복잡하고 유지보수가 어려운 코드를 개선하고 싶다면, 열거형을 적극 활용하는 것이 좋다.

 

감사합니다.

 
 

 

'자바' 카테고리의 다른 글

[JAVA] 중첩 클래스, 내부 클래스 1  (2) 2025.07.07
[JAVA] 날짜와 시간  (1) 2025.07.06
[JAVA] 래퍼 클레스에 대하여  (3) 2025.06.24
[JAVA] String 클래스에 대하여  (0) 2025.06.24
[JAVA] 불변객체에 대하여  (0) 2025.06.24
'자바' 카테고리의 다른 글
  • [JAVA] 중첩 클래스, 내부 클래스 1
  • [JAVA] 날짜와 시간
  • [JAVA] 래퍼 클레스에 대하여
  • [JAVA] String 클래스에 대하여
0kingki_
0kingki_
자바 + 스프링 웹 개발
  • 0kingki_
    0kingki_
    0kingki_
  • 전체
    오늘
    어제
    • 분류 전체보기 (134)
      • 코딩 테스트 (54)
      • 자바 (21)
      • 스프링 (27)
      • 타임리프 (16)
      • 스프링 데이터 JPA (8)
      • 최적화 (2)
      • QueryDSL (4)
      • AWS (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    mvc
    자바
    SOLID
    spring
    dfs
    예외처리
    BFS
    타임리프
    스프링
    SpringDataJpa
    객체지향
    LocalDateTime
    스프링 컨테이너
    재귀
    예외 처리
    다형성
    쿼리
    JPA
    스프링 데이터 JPA
    최적화
    fetch join
    코딩 테스트
    QueryDSL
    백준
    코딩테스트
    Java
    thymeleaf
    쿼리dsl
    불변객체
    컬렉션
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
0kingki_
[JAVA] 열거형(ENUM)에 대하여
상단으로

티스토리툴바