타입스크립트 교재 · 7편 / 20편

enum 과 const enum — 쓸지 말지

enum 의 모양과 함정, 그리고 거의 모든 경우 union literal 이 정답인 이유.

기초읽는 시간 6분2026-05-17
enum vs 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가지만

  1. 비트 플래그 (numeric enum 의 OR 연산) — 권한 비트마스크 등.
    enum Perm { Read = 1, Write = 2, Exec = 4 }
    const me = Perm.Read | Perm.Write;
  2. 외부 API 가 정수 코드만 받음 — gRPC enum, 레거시 시스템 등. JS 코드에 매직 넘버 박지 않으려고.
  3. 역방향 매핑이 정말 필요 — 숫자 → 이름 (예: 로그 출력용).

그 외 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

© 2026 주나이테크(주) @JUNAITECH