
타입스크립트를 사용해보셨다면 enum에 대해서 한번쯤 사용해 보셨을텐데요. 혹시 "enum 사용을 지양해야한다" 는 말을 들어보신 적이 있으신가요? 저는 예전 면접에서 처음 들었었는데요. 혹시 저처럼 처음들어 보시는 분들을 위해 enum 사용을 왜 조심히 해야하는 것인지에 대해 정리해 보려고 합니다.
enum은 열거형(enumeration)의 줄임말로, 연관된 상수들을 하나의 그룹으로 묶어 표현하는 TypeScript의 기능입니다. 이를 통해 코드의 가독성과 유지보수성을 높일 수 있습니다.
const user1 = {
name: "철수",
role: 0
}
const user2 = {
name: "영희",
role: 1
}
const user3 = {
name: "민수",
role: 2
}
위의 예시처럼 역할에 숫자 값을 부여했을 때 0,1,2만 보고 어떤 의미인지 바로 알기가 어렵습니다. 이런 경우 enum을 통해 개선할 수 있습니다.
enum Role {
ADMIN = 0,
USER = 1,
GUEST = 2
}
const user1 = {
name: "철수",
role: Role.ADMIN
}
const user2 = {
name: "영희",
role: Role.USER
}
const user3 = {
name: "민수",
role: Role.GUEST
}
enum을 활용하여 역할을 정의하고 그 의미를 명확하게 할 수 있습니다. 또한, 0 대신 Role.ADMIN 을 사용함으로써 코드의 가독성을 올릴 수 있으며 오타도 방지할 수 있습니다.
enum은 값을 명시적으로 할당하지 않으면 자동으로 0부터 시작해서 순차적으로 값을 할당합니다.
enum Role {
ADMIN, // 0
USER, // 1
GUEST // 2
}
또한, 특정 값에서 시작하도록 지정할 수도 있습니다.
enum Role {
ADMIN = 9, // 9
USER = 10, // 10
GUEST // 11
}
enum과 union의 차이점
- enum은 연관된 상수들을 하나의 그룹을 묶어 관리합니다.
enum Role { ADMIN = 0, USER = 1, GUEST = 2 }- union은 특정 변수나 속성이 여러 개의 정해진 값 중 하나를 가집니다.
type Role = "ADMIN" | "USER" | "GUEST";- 즉, enum은 하나의 객체처럼 동작하며 특정 값에 접근할 수 잇지만 union은 값들의 집합을 나타내는 타입이며 값들의 범위를 제한하는 역할을 합니다.
위 처럼 enum은 편리한 기능을 제공하지만, 몇 가지 단점이 존재합니다.
enum은 컴파일된 Javascript 코드에서 객체로 변환되어 추가적인 코드가 생성됩니다.


이러한 추가 코드는 번들 크기를 증가시키고 최적화에 불리할 수 있습니다.
Three Shaking은 사용하지 않는 코드를 제거하여 번들 크기를 줄이는 최적화 기법입니다. 그러나 enum은 실제로 런타임에 객체로 변환되기 때문에 이를 잘라내기 어렵습니다.
enum의 값은 컴파일된 후 객체 형태로 존재하고, 해당 객체를 참조하는 코드가 존재할 수 있기 때문에, tree shaking이 제대로 동작하지 않습니다. 이로 인해 사용하지 않는 enum 값들이 최종 번들에 포함되어 최적화가 어려워집니다.
enum은 숫자 값을 기본으로 하기 때문에, 역방향 매핑 을 지원합니다. 따라서 의도치 않은 값이 할당되는 문제가 발생할 수 있습니다.
enum Status {
Loading,
Success,
Error,
}
let statusName: string = Status[1]; // "Success"
let statusIndex: number = Status["Success"]; // 1
타입스크립트 공식 문서에서는 enum 대신 as const 사용을 지향하고 있습니다.
as const 는 TypeScript에서 객체나 배열을 리터럴 타입으로 고정할 수 있게 해줍니다.
리터럴 타입이란?
변경 불가능한 값을 나타내는 타입입니다. 예를 들어,"loading","success","error"같은 값을 리터럴 타입으로 지정하면, 해당 값만 허용되고 다른 값은 타입 오류가 발생합니다. 이를 통해 값의 범위를 제한하고 예상치 못한 값을 방지 할 수 있습니다.
따라서, as const 를 사용하면 값이 변경되지 않도록 고정되고, 타입은 해당 값에 정확히 맞춰지기 때문에 의도치 않은 값 할당을 방지할 수 있습니다.
const Status = {
Loading: "loading",
Success: "success",
Error: "error",
} as const;
type Status = typeof Status[keyof typeof Status];
let status: Status = "loading"; // 정상
status = "completed"; // 에러 발생
위 예시에서 Status는 as const 로 리터럴 타입으로 고정되었고, 그 결과 Status 타입은 "loading", "success", "error"로 제한됩니다. 이렇게 하면 원하는 기능을 사용하면서 enum에서 발생할 수 있는 여러 문제들을 피할 수 있게 됩니다!
좋은 글 감사합니다 !