[250829] _ Enum (열거형)

호두·2025년 8월 30일

✔️ Java / Spring

목록 보기
25/25

✅ 타입 안전 열거형 패턴

📌 장점 : 클래스를 정의해서 정해진 객체만 사용가능하게 제한

  1. 정해진 객체만 사용할 수 있어서 잘못된 값을 입력할 수 있는 문제를 해결할 수 있다.
  2. 정해진 객체만 사용해 값이 일관성을 보장한다.
  3. 잘못된 값을 사용하는 걸 컴파일 시점에서 방지 할 수 있다. (컴파일 오류)

📌 단점 : 많은 코드를 작성해야한다. (접근제한자, 반환타입 등)


✅ 열거형 - Enum Type

"타입 안전 열거형 패턴"을 더 간편하게 사용하기 위해 자바는 Enum 이라는 개념을 제공한다.

enumenumeration 의 줄임말로 '열거' 라는 뜻으로 항목을 나열한다는 의미이다. enum을 사용하면 문자열이나 숫자 상수 대신, 의미가 분명한 이름을 가진 상수를 사용하게 되므로 코드의 가독성과 안정성이 높아지고, 예상 가능한 값들의 집합을 표현하는데 사용된다. 또 컴파일 시점에 잘못된 값 입력을 방지해 런타임 오류를 막을 수 있다.


  1. 열거형을 정의할 때 class대신 enum 을 사용한다.
  2. 원하는 상수 이름을 나열하면 된다.
  3. 열거형도 클래스다.
  4. 라이브러리 java.lang.Enumenum 의 기능과 틀이 이미 정의되어 있고, 모든 enum은 이를 상속받기 때문에, 개발자는 이름만 선언해도 자동으로 상수 인스턴스가 만들어진다.
  5. 외부에서 임의로 객체를 생성할 수 없다. (private)
  6. 열거형은 toString() 을 재정의해서 참조값을 직접 확인 할 수 없다.
  7. 열거형은 인터페이스를 구현할 수 있다.
  8. 열거형은 추상메서드를 선언하고 구현할 수 있다.

Enum 클래스👇

package enumeration.ex3;

public enum Grade {
    BASIC, GOLD, DIAMOND
}

📌 장점 : 타입의 안정성, 간결성 및 일관성, 확장성 (추가하고 싶을 때 Enum 에 새로운 상수를 추가 하면 됨)


🖍️ 주요 메서드

package enumeration.ex3;

import java.util.Arrays;

public class EnumMethodMain {

    public static void main(String[] args) {

        Grade[] values = Grade.values();
        System.out.println("values = "+ Arrays.toString(values));
        for (Grade value : values) {
            System.out.println("name = "+ value.name() + ", ordinal = "+ value.ordinal());
        }
        // String 을 ENUM 으로 변환
        String input = "GOLD";
        Grade gold = Grade.valueOf(input);
        System.out.println("gold = "+ gold);
    }
}
  • values() : 모든 Enum 상수를 배열 형태로 반환한다.
  • valueOf() : 배열 안에 주어진 문자와 일치하는 상수를 반환한다.
  • ordinal() : 상수의 선언 순서를 반환한다. (웬만하면 사용❌)
  • name() : 상수의 이름을 문자열로 반환한다.
  • toString() : 상수의 이름을 문자열로 반환하며 재정의가 가능하다.

✅ 열거형 리팩토링

해당하는 등급에 맞게 할인율을 적용하려고 한다. enum 을 사용해서 코드를 작성해보자.

package enumeration.ref2;

public enum Grade {
    BASIC(10), GOLD(20), DIAMOND(30); // 상수 초기화

    private final int discountPercent;

    Grade(int discountPercent) { // 생성자
        this.discountPercent = discountPercent;
    }

    public int getDiscountPercent() { // 메서드
        return discountPercent;
    }
}

➡️ 각각의 등급(enum 상수)에 할인율을 생성자 인자(매개변수)로 넣어 초기화한다. 할인율을 담을 변수를 enum 내부에 한 번만 선언해두고, 각 상수 생성 시 값을 대입해 관리한다.


package enumeration.ref2;

public class DiscountService {

    public int discount(Grade classGrade, int price) {
        return price * classGrade.getDiscountPercent() / 100;
    }
}

➡️ 상품 가격에 해당하는 등급의 할인율을 적용하는 클래스다.


package enumeration.ref2;


public class ClassGradeRefMain2 {

    public static void main(String[] args) {

        int price = 10000;

        // 호출이 조건에 맞는지 비교하고 그에 맞는 할인율을 대입해 계산하는 방법 vs 조건에 그에 맞는 할인율을 대입해두고 호출하면 바로 계산에 돌입하기
        DiscountService discountService = new DiscountService();
        int basic = discountService.discount(Grade.BASIC, price);
        int gold = discountService.discount(Grade.GOLD , price);
        int diamond = discountService.discount(Grade.DIAMOND, price);

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

➡️ 등급별 상수enum와 가격을 discount() 메서드에 전달하면, 메서드는 해당 등급의 할인율을 적용해 계산을 수행하고 결과 금액을 반환한다. 이후 메인 메서드에서 이 값을 출력해 등급별 할인 금액을 확인한다.


이 코드는 일부 보완되었지만 여전히 리팩토링 여지가 있다. 현재 할인 계산 클래스는 ClassGrade메서드를 호출하기만 하는 중간자 역할로, 자체 책임이 없다. 할인율의 선언과 계산 책임을 ClassGrade로 통합하면 캡슐화가 강화되고 사용도 더 직관적이다.


package enumeration.ref3;

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 price * discountPercent / 100;
    }
}
 

➡️ 할인율과 관련된 기능을 하나의 클래스에 정의했다.


package enumeration.ref3;


public class EnumRefMain3_1 {

    public static void main(String[] args) {

        int price = 10000;

        System.out.println("BASIC 등급의 할인 금액 : " + Grade.BASIC.discount(price));
        System.out.println("GOLD 등급의 할인 금액 : " + Grade.GOLD.discount(price));
        System.out.println("DIAMOND 등급의 할인 금액 : " + Grade.DIAMOND.discount(price));
    }
}

➡️ 할인율 계산이 enum 내부의 책임으로 들어가면서, 단순히 연결만 하던 DiscountService 클래스는 불필요해졌다.
이제 각 등급 상수에서 직접 할인율을 계산하므로, 메서드 호출을 출력문에서 바로 실행할 수 있어 코드가 훨씬 간결해졌다.


📌 중복되는 코드 지우기

package enumeration.ref3;

public class EnumRefMain3_3 {
    public static void main(String[] args) {
        int price = 10000;

        Grade[] grades = Grade.values(); // 배열로 객체들 싹 가져와서
        for (Grade grade : grades) { // 배열과 price 로 호출을 반복한다.
            printDiscount(grade,price);
        }
    }

    private static void printDiscount(Grade grade, int price) {
        System.out.println(grade.name() + " 등급의 할인 금액 : "+ grade.discount(price)); // 출력마다 달라지는 이름은 name() 으로 해결했다
    }
}

➡️ 메서드 호출 부분과 출력문은 중복되는 코드들이 있다. 이 둘을 반복문으로 더 간결하게 만들었다.



✅ 인증 등급 문제

인증등급을 입력받아서 각 등급별로 접근할 수 있는 화면을 출력한다. 밑의 main 클래스를 보고 enum 을 작성하자.

package enumeration.test.http;

import java.util.Scanner;

public class HttpStatusMain {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("HTTP CODE : ");
        int httpCodeInput = scanner.nextInt();

        HttpStatus status = HttpStatus.findByCode(httpCodeInput);
        if (status == null) {
            System.out.println("정의되지 않은 코드");
        } else {
            System.out.println(status.getCode()+" "+status.getMessage());
            System.out.println("isSuccess = "+ status.isSuccess());
        }
    }

}

답안 :

package enumeration.test.http;

public enum HttpStatus {
    OK(200, "OK")
    ,BAD_REQUEST(400, "Bad Request")
    ,NOT_FOUND(404, "Not Found")
    ,INTERNAL_SERVER_ERROR(500, "Internal Server Error");

    private final int code; // final 빼먹지 말것❗️
    private final String message;

    HttpStatus(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

  // 코드 값을 받아서 해당하는 코드를 가진 등급을 반환하는 메서드
    public static HttpStatus findByCode(int code) { 
        for (HttpStatus status : values()) {
            if (status.getCode() == code) { // static.getCode 를 생각못했다.
                return status;
            }
        }
        return null; // 해당하는 등급이 없을 때
    }

    public boolean isSuccess() { // 코드 입력 가능 범위로 성공 판단
        return code >= 200 && code <= 299;
    }
}

0개의 댓글