외부활동/우아한테크코스 [프리코스]

상수관리에 Enum은 필수적인가 ?

softmoca__ 2024. 11. 1. 12:51
목차

 

Enum은 상수 관리에 필수적인가 ?

프리코스 1주차 미션에 대한 코드 리뷰로  에러메세지에 관한 상수를 Enum을 사용해 보는 것에 대한 피드백을 받았다.

 

그리고 2주차 미션에 바로 적용해 보았다.

확실히 타입의 안정성과 재사용성 측면에서 좋은 장점이 있었다.

 

 

 

하지만 위와 같은 에러메세지가 길어 가독성이 떨어져 명확하게 보여지지 않는 피드백을 받고 생각해 보았다.

 throw new IllegalArgumentException(ErrorMessage.CAR_NAME_TOO_LONG.getMessage() + ": " + name);

확실히 위와같은 Enum 을 사용하는 코드와 아래와 같은 Enum을 사용하지 않고 일반 상수로 관리하는것이 명확하게 인지되는것 또한 느껴졌다.

throw new IllegalArgumentException(ErrorMessage.INVALID_PURCHASE_UNIT);

 

이러한 고민을 해결하기 이전에 우선 Enum에 대해 제대로 정리를 먼저 해보고자한다.

 

 

ENUM이란 ?

Enum-열거형이 생겨난 이유

문자열을 사용하는 방식은 다음과 같은 문제가 있다.

  • 타입 안정성 부족: 문자열은 오타가 발생하기 쉽고, 유효하지 않은 값이 입력될 수 있다.
  • 데이터 일관성: "GOLD", "gold", "Gold" 등 다양한 형식으로 문자열을 입력할 수 있어 일관성이 떨어진다.

String 사용 시 타입 안정성 부족 문제

  • 값의 제한 부족: String으로 상태나 카테고리를 표현하면, 잘못된 문자열을 실수로 입력할 가능성이 있다. 예를 들어, "Monday", "Tuesday" 등을 나타내는 데 String을 사용한다면, 오타("Munday")나 잘못된 값("Funday")이 입력될 위험이 있다.
  • 컴파일 시 오류 감지 불가: 이러한 잘못된 값은 컴파일 시에는 감지되지 않고, 런타임에서만 문제가 발견되기 때문에 디버깅이 어려워질 수 있다.

이런 문제를 해결하려면 특정 범위로 값을 제한해야 한다.

예를 들어 BASIC, GOLD, DIAMOND라는 정확한 문자만 메서드에 전달되어야 한다.

하지만 String은 어떤 문자열이든 받을 수 있기 때문에 자바 문법 관점에서는 아무런 문제가 없다.

결국 String 타입을 사용하는 문제는 해결할 수 없다.

 

 

타입 안전 열거형 패턴 - Type-Safe Enum Pattern

위와같은 문제를 해결하기 위해 많은 개발자들이 오랜기간 고민하고 나온 결과가 바로 타입 안전 열거형 패턴이다.

여기서 영어인 enum은 enumeration의 줄임말인데, 번역하면 열거라는 뜻이고, 어떤 항목을 나열하는 것을 뜻한다.

회원 등급인 BASIC, GOLD, DIAMOND를 나열하는 것이다.

여기서 중요한 것은 타입 안전 열거형 패턴을 사용하면 이렇게 나열한 항목만 사용할 수 있다는 것이 핵심이다.

나열한 항목이 아닌 것은 사용할 수 없다.

쉽게 이야기해서 앞서 본 String처럼 아무 문자열이나 다 사용할 수 있는 것이 아니라,  나열한 항목인 BASIC, GOLD, DIAMOND만 안전하게 사용할 수 있다는 것이다.

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() {}
}
  • 먼저 관련된 상수를 다루는 클래스를 만들고, 각각의 문자열 상수를 선언한다.
  • 이때 각각의 상수마다 별도의 인스턴스를 생성하고, 생성한 인스턴스를 대입한다.
  • 각각을 상수로 선언하기 위해 static, final을 사용한다.
    • static을 사용해서 상수를 메서드 영역에 선언한다.
    • final을 사용해서 인스턴스(참조값)를 변경할 수 없게 한다.
public class DiscountService {

    public int discount(ClassGrade classGrade, int price) {
        int discountPercent = 0;

        if (classGrade == ClassGrade.BASIC) {
            discountPercent = 10;
        } else if (classGrade == ClassGrade.GOLD) {
            discountPercent = 20;
        } else if (classGrade == ClassGrade.DIAMOND) {
            discountPercent = 30;
        } else {
            System.out.println("할인X");
        }

        return price * discountPercent / 100;
    }
}
  • discount() 메서드는 매개변수로 ClassGrade 클래스를 사용한다.
  • 값을 비교할 때는 classGrade == ClassGrade.BASIC와 같이 == 참조값 비교를 사용하면 된다.
    • 매개변수에 넘어오는 인수도 ClassGrade가 가진 상수 중에 하나를 사용한다. 따라서 열거한 상수의 참조값으로 비교(==)하면 된다.

 

타입 안전 열거형 패턴(Type-Safe Enum Pattern)의 장점

  • 타입 안정성 향상: 정해진 객체만 사용할 수 있기 때문에, 잘못된 값을 입력하는 문제를 근본적으로 방지할 수 있다.
  • 데이터 일관성: 정해진 객체만 사용하므로 데이터의 일관성이 보장된다.
  • 제한된 인스턴스 생성: 클래스는 사전에 정의된 몇 개의 인스턴스만 생성하고, 외부에서는 이 인스턴스들만 사용할 수 있도록 한다. 이를 통해 미리 정의된 값들만 사용하도록 보장한다.
  • 타입 안전성: 이 패턴을 사용하면, 잘못된 값이 할당되거나 사용되는 것을 컴파일 시점에 방지할 수 있다. 예를 들어, 특정 메서드가 특정 열거형 타입의 값을 요구한다면, 오직 그 타입의 인스턴스만 전달할 수 있다. 여기서는 메서드의 매개변수로 ClassGrade를 사용하는 경우, 앞서 열거한 BASIC, GOLD, DIAMOND만 사용할 수 있다.

 

 

열거형 - Enum Type

자바는 타입 안전 열거형 패턴(Type-Safe Enum Pattern)을 매우 편리하게 사용할 수 있는 열거형(Enum Type)을 제공한다.

쉽게 이야기해서 자바의 열거형은 앞서 배운 타입 안전 열거형 패턴을 쉽게 사용할 수 있도록 프로그래밍 언어에서 지원하는 것이다.

영어인 enum은 enumeration의 줄임말인데, 번역하면 열거라는 뜻이고, 어떤 항목을 나열하는 것을 뜻한다. "Enumeration"은 일련의 명명된 상수들의 집합을 정의하는 것을 의미하며, 프로그래밍에서는 이러한 상수들을 사용하여 코드 내에서 미리 정의된 값들의 집합을 나타낸다.

쉽게 이야기해서 회원의 등급을 상수로 정의한 BASIC, GOLD, DIAMOND만 사용할 수 있다는 뜻이다.

자바의 enum은 타입 안전성을 제공하고, 코드의 가독성을 높이며, 예상 가능한 값들의 집합을 표현하는 데 사용된다.

 

 

public enum Grade {
    BASIC,
    GOLD,
    DIAMOND
}
  • 열거형을 정의할 때는 class 대신에 enum을 사용한다.
  • 원하는 상수의 이름을 나열하면 된다.

앞서 직접 ClassGrade를 구현할 때와는 비교가 되지 않을 정도로 편리하다.
자바의 열거형으로 작성한 Grade는 다음 코드와 거의 같다.

 

앞서 직접 ClassGrade를 구현할 때와는 비교가 되지 않을 정도로 편리하다.
자바의 열거형으로 작성한 Grade는 다음 코드와 거의 같다.

 

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

    // private 생성자 추가
    private Grade() {}
}
  • 열거형도 클래스이다.
  • 열거형은 자동으로 java.lang.Enum을 상속 받는다.
  • 외부에서 임의로 생성할 수 없다.

 

열거형(ENUM)의 장점

  • 타입 안정성 향상: 열거형은 사전에 정의된 상수들로만 구성되므로, 유효하지 않은 값이 입력될 가능성이 없다. 이런 경우 컴파일 오류가 발생한다.
  • 간결성 및 일관성: 열거형을 사용하면 코드가 더 간결하고 명확해지며, 데이터의 일관성이 보장된다.
  • 확장성: 새로운 회원 등급을 타입을 추가하고 싶을 때, ENUM에 새로운 상수를 추가하기만 하면 된다.

 

열거형 - 주요 메서드

모든 열거형은 java.lang.Enum 클래스를 자동으로 상속 받는다.

따라서 해당 클래스가 제공하는 기능들을 사용할 수 있다.

 

 public static void main(String[] args) {

        //모든 ENUM 반환
        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 변환, 잘못된 문자면 IllegalArgumentException 발생
        String input = "GOLD";
        Grade gold = Grade.valueOf(input);
        System.out.println("gold = " + gold); //toString() 오버라이딩 가능
    }
  • values(): 모든 ENUM 상수를 포함하는 배열을 반환한다.
  • valueOf(String name): 주어진 이름과 일치하는 ENUM 상수를 반환한다.
  • name(): ENUM 상수의 이름을 문자열로 반환한다.
  • ordinal(): ENUM 상수의 선언 순서(0부터 시작)를 반환한다.
  • toString(): ENUM 상수의 이름을 문자열로 반환한다. name() 메서드와 유사하지만, toString()은 직접 오버라이드할 수 있다.

열거형 정리

  • 열거형은 java.lang.Enum을 자동(강제)으로 상속 받는다.
  • 열거형은 이미 java.lang.Enum을 상속 받았기 때문에 추가로 다른 클래스를 상속을 받을 수 없다.
  • 열거형은 인터페이스를 구현할 수 있다.
  • 열거형에 추가 메서드를 선언하고, 구현할 수 있다.
    • 이 경우 익명 클래스와 같은 방식을 사용한다. 

 

 

그래서 Enums은 언제 사용하는가 ?

다시 처음의 고민으로 돌아와 생각해보자. 그럼 Enum은 언제 사용해야하는가 ?

서로 연관된 상수에는 사용

그렇지 않은 경우는 사용 X

 

분명 Enum을 통해 상수를 관리한다면 타입안정성, 일관성, 확장성 과 같은 장점을 얻을 수 있다.

하지만 위와같이 Enum에 대해 정리해보며, 예외 메시지를 관리하기 위해 2주차에 Enum을 사용해보고 3주차에는 그저 클래스로 관리해보았고, 당첨등급과 같은 연관된 상수에는 Enum을 사용해본 결과,

연관성이 있는 상수가 아닌 경우에는 위와같은 장점 보다는 그저 '가독성', '간단한 사용'의 장점이 더 크다고 생각한다.

 

물론 이후 미션을 진행하며, 프로젝트를 진행하며 혹은 다른 리뷰어들의 의견을 통해 분명 나의 생각이 바뀐수 있다고 생각된다.