Enum과 static

김정훈·2024년 7월 4일
0

어노테이션을 생성하는 도중에 Attribute value must be constant 이런 에러가 발생했습니다.

Enum으로 이름들을 관리하는데 대체 뭐가 문제일까요?

에러 분석

Attribute value must be constant를 직역하면 "속성값은 상수여야 한다" 입니다.

Enum의 쓰임

Enum을 쓰기 전 상수 관리는 final static으로 했습니다.

  public static class TokenName{
        public static final String accessToken = "accessToken";
        public static final String refreshToken = "refreshToken";
    }

하지만 final static의 단점은 타입 안정성이 부족하는 점 입니다. 뭘 말하는 걸까요?

public static final int man = 0;
public static final int woman = 1;

public static final int true = 1;
public static final int false = 0;

만약 이렇게 상수를 지정했다고 했을 떄 true == woman 이라는 말도 안되는 일이 벌어집니다. 이 문제가 발생하는 이유는 상수들의 타입이 int로 동일하기 때문입니다.

때문에 enum과 같은 열거형 타입이 필요합니다.

public enum Gender {
    MAN, WOMAN
}
public enum TrueFalse {
    TRUE, FALSE
}

Enum은 상수인가?

상수 클래스 vs. Enum 클래스의 차이 및 열거형의 장점

상수를 사용하여 클래스를 구성하는 방식과 열거형(enum)을 사용하는 방식은 겉보기에 비슷해 보이지만, 실제로는 몇 가지 중요한 차이점이 있습니다. 이 차이점은 타입 안전성, 가독성, 유지보수성에 영향을 미칩니다.

상수 클래스 사용:

  • 정의: public static final 키워드를 사용하여 클래스 내에 상수를 정의합니다.
  • 예시: OrangeApple 클래스에서 NAVELBLOOD를 독립적으로 정의합니다.
  • 문제점:
    • 타입 안전성 부족: 메서드가 int를 인자로 받을 때, Orange.NAVEL, Apple.NAVEL, 또는 다른 모든 int 값이 유효한 인자가 될 수 있습니다. 이는 메서드가 의도하지 않은 값으로 호출될 수 있음을 의미합니다.
    • 유지보수성: 상수 값이 변경되거나 추가될 때 여러 클래스에서 수정이 필요할 수 있으며, 이러한 변경이 다른 클래스에 영향을 미칠 수 있습니다.

Enum 클래스 사용:

  • 정의: enum 키워드를 사용하여 상수의 집합을 타입 안전하게 정의합니다.
  • 예시: Fruit 열거형은 OrangeApple의 상수를 포함하며, 각 상수는 고유한 속성(navel, blood)을 가집니다.
  • 장점:
    • 타입 안전성: 열거형을 사용하는 메서드는 오직 Fruit 타입의 인자만 받을 수 있습니다. 이는 메서드가 Fruit에 정의된 값들로만 호출될 수 있도록 제한하며, 잘못된 값 사용을 방지합니다.
    • 가독성 및 유지보수성: 열거형은 관련 상수들을 하나의 묶음으로 관리할 수 있게 해주어 코드의 구조가 명확해집니다. 또한, 수정이 필요한 경우 한 곳에서만 변경하면 되므로 유지보수가 용이합니다.
    • 기능 확장성: 열거형은 메서드와 속성을 포함할 수 있으며, 각 상수에 고유한 동작을 정의할 수 있습니다. 이는 단순한 상수보다 더 복잡한 로직을 구현할 때 유리합니다.

결론

상수 클래스를 사용하는 것이 아니라 열거형을 사용하는 것이 권장되는 주된 이유는 열거형이 타입 안전성을 제공하고, 코드의 가독성 및 유지보수성을 향상시키며, 복잡한 로직을 캡슐화 할 수 있는 능력 때문입니다. 열거형을 사용하면 코드의 의도가 명확해지고, 오류 발생 가능성이 줄어들며, 확장성이 향상됩니다.

왜 Enum은 어노테이션 인자로 사용할 수 없는가?

  1. 컴파일 시점의 값: 어노테이션 속성 값은 컴파일 시점에 완전히 알려져 있어야 하며, 이는 리터럴 값 또는 상수 표현식만 가능하다는 것을 의미합니다. 반면, Enum 값은 실행 시점에 생성되는 객체입니다.

  2. 타입 시스템과의 호환성: Enum은 기본적으로 클래스의 일종으로, Enum 타입의 각 상수는 실제로 해당 Enum 클래스의 인스턴스입니다. 따라서, 이러한 Enum 상수를 어노테이션의 인자로 사용하려면, 이들이 실제로는 객체이기 때문에 컴파일러는 이를 허용하지 않습니다.

  3. 실행 시점의 객체: Enum 상수는 내부적으로 해당 Enum 클래스의 인스턴스를 참조하는 형태로 존재합니다. 이는 컴파일 시점에 할당된 단순한 상수와는 다르며, 어노테이션에서 사용되기에는 이러한 객체 참조가 허용되지 않습니다.

예시와 대안

public enum Role {
    ADMIN, USER, GUEST;
}

// 이런 식으로 Enum을 어노테이션 인자로 사용할 수 없습니다.
//@RolesAllowed(Role.ADMIN)  // 이 코드는 컴파일 오류를 일으킵니다.

// 대신 문자열 상수를 사용해야 합니다.
@RolesAllowed("ADMIN")
public void secureMethod() {
    // 메소드 내용
}

그래서 전 다음과 같이 정의했습니다.

public class Token{
    public enum TokenType{
        ACCESS,REFRESH;
    }
    public enum TokenTime{
        accessToken(10 * 1000 * 60L),
        refreshToken(60*1000*60L);
        @Getter
        private final long expiredTime;

        TokenTime(long expiredTime){
            this.expiredTime = expiredTime;
        }
    }
    // 어노테이션의 변수로 쓰기 위해서는 constant 문제로 컴파일 전에 상수 정의가 되어 있어야 함
    public static class TokenName{
        public static final String accessToken = "accessToken";
        public static final String refreshToken = "refreshToken";
    }

}

enum과 static을 섞어 쓰는게 옳은 건지 모르겠네요..

결론

Enum을 직접적으로 어노테이션의 인자로 사용할 수 없는 이유는 Enum이 컴파일 시점에 결정되는 리터럴 값이 아니라, 실행 시점에 생성되는 객체이기 때문입니다. 이로 인해 Enum이 제공하는 많은 장점들에도 불구하고, 어노테이션에서는 사용이 제한됩니다. Enum의 사용은 타입 안전성, 코드의 가독성 및 유지보수성을 향상시키는 데에는 유리하지만, 어노테이션의 인자로는 문자열 리터럴이나 숫자 리터럴 등을 사용해야 합니다.

profile
백엔드 개발자

0개의 댓글