어노테이션을 생성하는 도중에 Attribute value must be constant 이런 에러가 발생했습니다.
Enum으로 이름들을 관리하는데 대체 뭐가 문제일까요?
Attribute value must be constant를 직역하면 "속성값은 상수여야 한다" 입니다.
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)을 사용하는 방식은 겉보기에 비슷해 보이지만, 실제로는 몇 가지 중요한 차이점이 있습니다. 이 차이점은 타입 안전성, 가독성, 유지보수성에 영향을 미칩니다.
public static final
키워드를 사용하여 클래스 내에 상수를 정의합니다.Orange
와 Apple
클래스에서 NAVEL
과 BLOOD
를 독립적으로 정의합니다.int
를 인자로 받을 때, Orange.NAVEL
, Apple.NAVEL
, 또는 다른 모든 int
값이 유효한 인자가 될 수 있습니다. 이는 메서드가 의도하지 않은 값으로 호출될 수 있음을 의미합니다.enum
키워드를 사용하여 상수의 집합을 타입 안전하게 정의합니다.Fruit
열거형은 Orange
와 Apple
의 상수를 포함하며, 각 상수는 고유한 속성(navel
, blood
)을 가집니다.Fruit
타입의 인자만 받을 수 있습니다. 이는 메서드가 Fruit
에 정의된 값들로만 호출될 수 있도록 제한하며, 잘못된 값 사용을 방지합니다.상수 클래스를 사용하는 것이 아니라 열거형을 사용하는 것이 권장되는 주된 이유는 열거형이 타입 안전성을 제공하고, 코드의 가독성 및 유지보수성을 향상시키며, 복잡한 로직을 캡슐화 할 수 있는 능력 때문입니다. 열거형을 사용하면 코드의 의도가 명확해지고, 오류 발생 가능성이 줄어들며, 확장성이 향상됩니다.
컴파일 시점의 값: 어노테이션 속성 값은 컴파일 시점에 완전히 알려져 있어야 하며, 이는 리터럴 값 또는 상수 표현식만 가능하다는 것을 의미합니다. 반면, Enum 값은 실행 시점에 생성되는 객체입니다.
타입 시스템과의 호환성: Enum은 기본적으로 클래스의 일종으로, Enum 타입의 각 상수는 실제로 해당 Enum 클래스의 인스턴스입니다. 따라서, 이러한 Enum 상수를 어노테이션의 인자로 사용하려면, 이들이 실제로는 객체이기 때문에 컴파일러는 이를 허용하지 않습니다.
실행 시점의 객체: 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의 사용은 타입 안전성, 코드의 가독성 및 유지보수성을 향상시키는 데에는 유리하지만, 어노테이션의 인자로는 문자열 리터럴이나 숫자 리터럴 등을 사용해야 합니다.