TypeScript에서 enum은 “열거형 타입”을 정의하는 문법입니다. 서로 연관된 상수들의 집합을 선언할 때 쓰이며, Java나 C#의 enum과 유사한 기능을 제공합니다. 하지만 TypeScript의 enum은 독특한 기능과 한계를 동시에 가지고 있기 때문에, 실무에서는 사용 여부를 신중히 판단해야 합니다.
enum이란?
enum은 이름이 있는 상수 값들을 그룹화하여 관리하는 문법입니다. 상태 값, 권한 값, 방향 값 등 제한된 값 목록을 정의할 때 사용됩니다.
enum Direction {
Up,
Down,
Left,
Right
}
const d = Direction.Up;
기본적으로 숫자 열거형이 생성되며, 자동으로 0부터 증가하는 값이 부여됩니다.
숫자 열거형(Numeric Enum)
기본 enum은 숫자를 값으로 갖습니다.
enum Status {
OK = 200,
NOT_FOUND = 404,
SERVER_ERROR = 500
}
let code: Status = Status.OK;
숫자 enum은 중복 없이 명확한 의미를 부여하는 상황에서 유용하지만, 값이 문자열이 아니기 때문에 디버깅 시 혼동될 수 있습니다.
문자열 열거형(String Enum)
문자열로 값이 지정되므로 디버깅과 로깅에 더 유리합니다.
enum LogLevel {
DEBUG = "debug",
INFO = "info",
WARN = "warn",
ERROR = "error"
}
최근 실무에서는 숫자 enum보다 문자열 enum을 선호하는 경향이 강합니다.
enum의 양방향 매핑(Bidirectional Mapping)
숫자 enum은 값으로부터 키를 다시 얻을 수 있는 양방향 매핑을 제공합니다.
enum Direction {
Up,
Down
}
Direction.Up; // 0
Direction[0]; // "Up"
편리해 보이지만, 이 기능은 enum을 “런타임 객체”로 만들어 불필요한 코드가 번들에 포함되는 문제를 유발합니다.
const enum — 컴파일 시 제거되는 enum
런타임 오버헤드를 줄이기 위해 const enum을 사용할 수 있습니다. 컴파일 시 enum 전체가 제거되고 값만 인라인(inline)됩니다.
const enum Direction {
Up,
Down
}
let d = Direction.Up;
// 컴파일 후: let d = 0;
단, const enum은 isolatedModules 모드에서 사용할 수 없고 몇몇 번들러 환경에서는 문제가 발생할 수 있다는 단점이 있습니다.
enum의 대표적인 한계
enum은 강력해 보이지만 많은 프로젝트에서 enum 대신 리터럴 유니온 타입을 선호합니다. 이유는 다음과 같습니다.
1. 런타임 코드가 생성된다 (불필요한 번들 증가)
enum A { X, Y }
console.log(A);
이 코드는 컴파일 후 JavaScript 객체로 변환됩니다. 리터럴 타입은 컴파일 후 코드가 생성되지 않기 때문에 훨씬 “가벼운” 방식입니다.
2. enum은 타입 시스템보다 강한 “런타임 구조”를 가진다
TypeScript의 enum은 단순 타입 정의가 아니라 실제 런타임 객체이므로 의도치 않은 동작이 발생할 가능성이 있습니다.
3. union type이 enum을 완전히 대체할 수 있음
type Direction = "up" | "down" | "left" | "right";
리터럴 유니온의 장점:
- 런타임 코드 생성 없음
- 자동 완성 및 타입 체크 지원
- 값 비교가 더 직관적
- tree-shaking에 완벽하게 대응
이 때문에 React, Vue 등 프론트엔드 프로젝트에서는 enum보다 유니온 타입을 쓰는 것이 사실상 표준이 되었습니다.
4. 객체 freezing 방식이 더 안전
const LogLevel = {
DEBUG: "debug",
INFO: "info",
WARN: "warn",
ERROR: "error"
} as const;
type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
이 방식은 enum보다 타입 안정성, 확장성, 디버깅 모든 면에서 더 좋습니다.
enum을 사용해야 하는 경우
그럼에도 불구하고 enum이 유용한 상황도 존재합니다.
- 클래스 기반 OOP 스타일에서 상수 그룹 정의
- 숫자 기반 값이 필요하고 자동 증가 값이 유리할 때
- 기존 백엔드 언어나 레거시 코드가 enum 중심 구조일 때
- 런타임에서도 enum 객체가 필요할 때
다만 대부분의 프론트엔드 프로젝트에서는 union 타입이 사실상 더 좋은 선택입니다.
TypeScript의 enum은 편리하지만, 런타임 코드 생성이라는 특성과 유니온 타입으로 대체 가능한 점 때문에 최근에는 사용을 자제하는 흐름이 강합니다. 특히 프론트엔드 분야에서는 “enum 지양, 유니온 지향”이 사실상 표준입니다.
- enum = 런타임 객체가 필요한 경우만 사용
- 단순한 값 목록은 유니온 타입이 더 안전하고 가벼움
- as const 객체 + 유니온 조합이 가장 추천되는 패턴.
