enum 과 const enum — 쓸지 말지
enum 의 모양과 함정, 그리고 거의 모든 경우 union literal 이 정답인 이유.
TypeScript 의 enum 은 다른 언어의 enum 과 비슷한 이름의 다른 것 — JS 에는 enum 이 없으므로 TS 가 컴파일 시 객체로 만들어냅니다. 편리하지만 의외성이 있어, "enum 보다 union literal 이 거의 항상 낫다" 는 게 2026년의 컨센서스입니다. 7편은 그 결론에 도달하는 과정을 정리합니다.
enum 기본형 — numeric · string
// numeric enum (기본 0부터 증가)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
Direction.Up; // 0
Direction[0]; // "Up" ← 양방향 매핑
// string enum (권장 — 값이 명시적)
enum Theme {
Light = "light",
Dark = "dark",
Auto = "auto",
}
Theme.Light; // "light"
Theme["Light"]; // "light" (string enum 은 단방향)
enum 의 컴파일 결과 — 런타임 코드 생성
// TS
enum Theme { Light = "light", Dark = "dark" }
// 컴파일 결과 (JS)
var Theme;
(function (Theme) {
Theme["Light"] = "light";
Theme["Dark"] = "dark";
})(Theme || (Theme = {}));
이게 enum 의 가장 큰 특이점입니다. 타입만이 아니라 실제 객체가 런타임에 만들어집니다. 모든 다른 TS 타입 표기는 컴파일 시 사라지는데 enum 만 다릅니다.
const enum — 인라인되어 런타임 코드 없음
const enum Theme { Light = "light", Dark = "dark" }
const t = Theme.Light;
// 컴파일 결과: const t = "light"; ← Theme 객체 자체가 사라짐
const enum 의 함정. ① babel·esbuild·swc 등 일부 빌드 도구는 const enum 을 지원 안 함(컴파일 시 타입 정보가 필요한데 그들은 파일별 변환). ② isolatedModules 옵션 켜면 사용 불가. ③ d.ts 로 노출된 const enum 은 사용처에서 인라인이 깨질 수 있음. 결론: 대부분 안 쓰는 게 낫다.
왜 union literal 이 더 나은가
// enum
enum Status { Pending, Paid, Shipped, Done }
function ship(s: Status) { ... }
ship(Status.Pending); // 0 으로 컴파일 (의미 사라짐)
ship(99); // ❌ 막힘 (다행)
// union literal
type Status = "pending" | "paid" | "shipped" | "done";
function ship(s: Status) { ... }
ship("pending"); // "pending" 그대로 — JSON 호환
ship("hello"); // ❌ 컴파일 차단
// JSON 시리얼라이즈 / 디버깅
JSON.stringify({status: Status.Pending}); // {"status":0} ← 의미 없음
JSON.stringify({status: "pending"}); // {"status":"pending"} ← 읽힘
union literal 의 장점:
- 런타임 코드 0 — 컴파일 시 모두 사라짐
- JSON·API 와 자연 호환 — 서버가 "pending" 보내면 그대로
- 구분 가능 유니온(5편) 으로 자연스럽게 확장
- switch exhaustive 검사 (20편 패턴) 더 단순
enum 이 정당한 경우 — 3가지만
- 비트 플래그 (numeric enum 의 OR 연산) — 권한 비트마스크 등.
enum Perm { Read = 1, Write = 2, Exec = 4 } const me = Perm.Read | Perm.Write; - 외부 API 가 정수 코드만 받음 — gRPC enum, 레거시 시스템 등. JS 코드에 매직 넘버 박지 않으려고.
- 역방향 매핑이 정말 필요 — 숫자 → 이름 (예: 로그 출력용).
그 외 99% 의 상태값·테마·HTTP 코드는 union literal 이 답입니다.
마이그레이션 패턴 — enum → union
// before
enum Status { Pending = "pending", Paid = "paid", Done = "done" }
// after — 같은 코드, 더 가벼움
const STATUS = ["pending", "paid", "done"] as const;
type Status = typeof STATUS[number]; // "pending" | "paid" | "done"
// 사용처 변경
ship("pending"); // 그대로 OK
STATUS.includes(input); // runtime 검증도 가능
한 줄 결론. 새 코드는 union literal + as const. enum 은 (1) 비트 플래그, (2) 정수 외부 API, (3) 명시적 역방향 매핑이 필요할 때만. const enum 은 거의 안 씀.
8편 — 클래스 + 접근 제어자
class 에 타입 붙이기, public·private·protected, implements, abstract 까지.
이전: 6편 제네릭 · 현재: 7편 (기초) · 다음 → 8편 클래스 · 진행: 7/20