enum을 무작정 as const로 교체하지 마세요
Source: Dev.to
Introduction
아마도 많은 글에서 TypeScript enum을 as const 객체로 교체하라고 본 적이 있을 겁니다. 그 이유는 다음과 같은 장점 때문이죠:
- 더 나은 트리‑쉐이킹
- 번들 크기 감소
- 런타임 코드 감소
그 조언이 전혀 틀린 것은 아니지만, 종종 지나치게 단순화됩니다.
진짜 질문은 “enum을 사용하지 말아야 할까?”가 아니라 TypeScript가 내부에서 무엇을 생성하고, 어떤 트레이드‑오프를 감수하고 있는지를 우리가 이해하고 있는가? 입니다.
Numeric enums and the generated code
숫자형 enum의 경우, TypeScript는 추가적인 런타임 코드를 생성합니다.
enum Direction {
Up,
Down,
Left,
Right,
}위 코드는 대략 다음과 같이 JavaScript로 변환됩니다:
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));처음 보면 이상해 보입니다. TypeScript는 IIFE(Immediately Invoked Function Expression)를 만들고 Direction["Up"] = 0을 할당합니다. 이 할당은 0을 반환하므로 TypeScript는 Direction[0] = "Up"도 설정합니다.
런타임에서는 다음과 같은 객체가 만들어집니다:
{
Up: 0,
Down: 1,
Left: 2,
Right: 3,
0: "Up",
1: "Down",
2: "Left",
3: "Right"
}이를 **역 매핑(reverse mapping)**이라고 합니다:
Direction.Up // 0
Direction[0] // "Up"as const objects
이제 as const가 붙은 일반 객체와 비교해 보세요:
const DirectionConst = {
Up: "Up",
Down: "Down",
Left: "Left",
Right: "Right",
} as const;이는 단순한 JavaScript 객체이므로 역 매핑이나 추가 런타임 코드가 없습니다. 그래서 많은 개발자가 as const를 다음과 같은 경우에 유용하게 사용합니다:
- 프론트엔드 애플리케이션
- 설정 객체
- 라우트 이름
- 상태값
- 액션 타입
- 기타 JS‑우선 패턴
Enums aren’t “bad”
문제가 enum이 나쁘다는 것이 아니라, 많은 사람들이 사용 사례를 고려하지 않고 무작정 교체한다는 점입니다. 모든 enum이 동일하지는 않습니다:
- 숫자형 enum은 역 매핑을 생성합니다.
- 문자열 enum은 역 매핑을 생성하지 않습니다.
- **
const enum**은 다른 트레이드‑오프를 가집니다(컴파일 시 인라인됩니다).
as const는 모든 enum 사용 사례를 1:1로 대체할 수 없습니다. 교체하기 전에 스스로에게 물어보세요:
- 역 매핑이 필요합니까?
- 런타임에 enum 객체가 필요합니까?
- 숫자형 enum을 사용하고 있습니까, 아니면 문자열 enum을 사용하고 있습니까?
- 이것이 애플리케이션 코드인가, 라이브러리 코드인가, 아니면 프로토콜/컴파일러 스타일 코드인가?
- 명시성, 번들 크기, 혹은 JavaScript 단순성을 위해 최적화하고 있습니까?
Conclusion
as const는 훌륭한 패턴이며, enum이 자동으로 잘못된 것은 아닙니다. “시니어스러운 움직임”은 “enum을 사용하지 말라”는 슬로건을 따르는 것이 아니라, TypeScript가 내부적으로 어떻게 동작하는지를 이해하고 상황에 맞는 도구를 선택하는 것입니다.