열거형 완전 정리 — 숫자·문자열·이종 enum 사용 가이드

TypeScript enum의 숫자 열거형, 문자열 열거형, 이종 열거형, 역방향 매핑, 상수 멤버를 코드 예제와 함께 완전히 정리하고 언제 enum을 쓸지 가이드합니다.

· 8 min read · PALDYN Team

지난 글에서 객체 타입을 살펴봤다. 이번 글에서는 TypeScript의 **열거형(enum)**을 완전히 정리한다. 숫자 열거형, 문자열 열거형, 이종 열거형, 역방향 매핑, const enum까지 다루고, 마지막으로 언제 enum을 사용할지, 언제 유니언 타입이 더 나은지 기준을 제시한다.

열거형이란

열거형(enum)은 이름이 있는 상수 집합을 정의하는 TypeScript 고유 기능이다. 순수 타입 레벨 구성이 아니라 컴파일 시 실제 JavaScript 객체로 변환된다.

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 = {}));

Direction.Up은 값 0이 되고, Direction[0]은 문자열 "Up"이 된다. 이것이 역방향 매핑이다.

숫자 열거형과 역방향 매핑

숫자 열거형 (Numeric Enum)

숫자 열거형은 첫 번째 멤버부터 0으로 시작해 자동으로 1씩 증가한다. 초기값을 직접 지정할 수도 있다.

enum HttpStatus {
  OK = 200,
  Created = 201,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
  InternalServerError = 500,
}

function handleResponse(status: HttpStatus): string {
  switch (status) {
    case HttpStatus.OK:
      return "요청 성공";
    case HttpStatus.NotFound:
      return "리소스를 찾을 수 없음";
    case HttpStatus.InternalServerError:
      return "서버 오류 — 나중에 다시 시도하세요";
    default:
      return `알 수 없는 상태: ${status}`;
  }
}

역방향 매핑 덕분에 숫자 값에서 이름을 얻을 수 있다.

console.log(HttpStatus[200]);   // "OK"
console.log(HttpStatus[404]);   // "NotFound"
console.log(HttpStatus.OK);     // 200

하지만 이 역방향 매핑은 숫자 열거형에만 적용된다. 문자열 열거형은 지원하지 않는다.

문자열 열거형

문자열 열거형 (String Enum)

문자열 열거형은 각 멤버에 명시적인 문자열 값을 할당한다. 자동 증가가 없으므로 모든 멤버에 값을 지정해야 한다.

enum ApiEndpoint {
  Users = "/api/users",
  Posts = "/api/posts",
  Comments = "/api/comments",
}

async function fetchData(endpoint: ApiEndpoint): Promise<unknown> {
  const response = await fetch(endpoint);
  return response.json();
}

// 사용 예
fetchData(ApiEndpoint.Users);
// fetchData("/api/users"); // 오류: string은 ApiEndpoint에 할당 불가

문자열 enum의 장점:

  • 런타임에 의미 있는 값을 유지해 디버깅이 쉽다.
  • 역직렬화(JSON 파싱 등) 시 예측 가능한 값이 나온다.
  • IDE 자동 완성과 리팩토링이 안전하다.

이종 열거형 (주의 필요)

숫자와 문자열을 섞은 **이종 열거형(Heterogeneous Enum)**은 TypeScript에서 허용하지만 실제로는 사용을 강력히 피해야 한다.

// 이종 열거형 — 실무에서 사용 금지
enum BoolLike {
  No = 0,
  Yes = "YES",
}

이종 열거형은 역방향 매핑이 일부에만 적용되고, 타입 안전성도 불분명해진다. 숫자 부분에만 역방향 매핑이 동작하므로 예상과 다른 결과가 발생하기 쉽다. 문자열 enum이나 숫자 enum 중 하나를 일관되게 사용하는 것이 올바른 선택이다.

const 멤버와 계산된 멤버

enum 멤버는 상수 멤버계산된 멤버로 나뉜다.

// 상수 멤버 (컴파일 타임에 값이 결정됨)
enum FileAccess {
  None,           // 0
  Read = 1 << 1,  // 2 (비트 시프트)
  Write = 1 << 2, // 4
  ReadWrite = Read | Write, // 6 (비트 OR)
}

// 계산된 멤버 (런타임에 값이 결정됨)
enum StringLength {
  Hello = "hello".length,  // 5 — 계산된 멤버
  World = "world".length,  // 5 — 계산된 멤버
}

계산된 멤버를 포함한 enum은 const enum으로 선언할 수 없다는 제약이 있다.

const enum: 인라인 최적화

const enum은 컴파일 시 enum 객체를 생성하지 않고 사용 지점에 직접 값을 인라인한다.

const enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

const move = Direction.Up;
// 컴파일 결과: const move = "UP";
// Direction 객체 자체는 JavaScript에 남지 않음

번들 크기를 줄일 수 있지만 트레이드오프가 있다. Babel, esbuild, SWC 같은 일부 트랜스파일러는 const enum을 올바르게 처리하지 못한다. isolatedModules: true 설정에서도 사용이 제한된다.

enum vs 유니언 타입 선택 기준

TypeScript에는 enum 대신 유니언 타입을 사용하는 패턴이 있다. 두 방식을 비교해보자.

// enum 방식
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

function moveEnum(dir: Direction) {
  console.log(dir); // "UP", "DOWN" 등 출력
}

// 유니언 타입 방식
type Direction2 = "UP" | "DOWN" | "LEFT" | "RIGHT";

function moveUnion(dir: Direction2) {
  console.log(dir);
}

// 호출 비교
moveEnum(Direction.Up);          // 반드시 enum 멤버 사용
moveUnion("UP");                  // 리터럴 문자열 직접 사용 가능

enum을 선택하는 경우:

  • 멤버 집합이 크고 이름이 의미를 명확히 표현할 때
  • 코드에서 반복적으로 참조하며 자동완성이 중요할 때
  • 컴파일된 JavaScript에서도 이름을 유지해야 할 때(로깅, 직렬화 등)

유니언 타입을 선택하는 경우:

  • 값이 문자열 리터럴이고 외부에서 직접 문자열을 넘기는 경우가 많을 때
  • 번들 크기나 트리 쉐이킹이 중요한 경우
  • Babel, esbuild 등 TypeScript가 아닌 트랜스파일러를 사용하는 경우
  • 간결함을 선호하고 enum의 런타임 동작이 필요 없을 때

현대 TypeScript 프로젝트에서는 유니언 타입 쪽을 선호하는 추세가 강하지만, 팀 컨벤션과 도구 환경에 따라 enum도 충분히 유효한 선택이다.

정리

TypeScript enum의 핵심을 정리하면:

  • 숫자 enum은 역방향 매핑을 지원하여 값에서 이름을 얻을 수 있다.
  • 문자열 enum은 런타임에 의미 있는 값을 유지해 디버깅이 편하다.
  • 이종 enum은 문법상 허용되지만 실무에서는 피해야 한다.
  • const enum은 번들 최적화에 유리하지만 일부 트랜스파일러와 호환성 문제가 있다.
  • 유니언 타입 리터럴이 더 간결하고 트리 쉐이킹 친화적이므로 두 방식을 비교해 선택한다.

지난 글: 객체 타입 완전 정리 — 프로퍼티·옵셔널·인덱스 시그니처

다음 글: const enum — 컴파일 타임 인라인과 트레이드오프


읽어주셔서 감사합니다. 😊