타입스크립트에서 enum 대신 Object.freeze를 사용하기
1. 타입스크립트 enum
타입스크립트의 enum은 간단하고 직관적입니다. 다음과 같이 사용할 수 있습니다.
export enum Color {
RED = 'red',
BLUE = 'blue',
GREEN = 'green',
}
Color.RED 바로 사용할 수 있고, 타입도 잘 잡힙니다. 하지만 이 enum에는 몇 가지 문제점이 존재합니다.
문제 1: 런타임에 실제 객체가 생성됩니다
타입스크립트의 enum은 단순히 타입만 만들어주는 것이 아니라, 컴파일 후 자바스크립트 코드에도 실제 객체로 남습니다. 예를 들어 컴파일 후 코드를 보면 다음과 같습니다.
export var Color
;(function (Color) {
Color['RED'] = 'red'
Color['BLUE'] = 'blue'
Color['GREEN'] = 'green'
})(Color || (Color = {}))
문제 2: 트리 셰이킹에 불리합니다
enum은 런타임 객체로 변환되기 때문에, 모듈 번들러(예: Webpack, Vite 등)가 안 쓰는 코드를 잘라내는 트리 셰이킹을 할 때도 전체가 포함돼 버립니다.
문제 3: 자바스크립트와의 호환성
enum은 타입스크립트만의 기능이라서, 순수 자바스크립트 환경에서는 다루기 불편할 수 있습니다. 예를 들어 라이브러리를 만들 때 JS 환경에서도 사용해야 한다면 제약이 될 수 있습니다.
2. as const vs Object.freeze
타입스크립트의 enum을 바로 사용하는 것보다 객체 + 타입 추론 방식으로도 enum을 구현할 수 있습니다. 그러면 위에 있는 문제점들을 해결 할 수 있습니다.
런타임 비용이 없습니다 객체 그대로 사용하기 때문에 컴파일 후에도 불필요한 추가 코드가 붙지 않습니다.
트리 셰이킹에 유리합니다 필요한 값만 가져오면 나머지는 자연스럽게 번들에서 제거됩니다.
자바스크립트와 완벽하게 호환됩니다 객체는 자바스크립트에서도 자연스럽게 사용할 수 있으므로, 타입스크립트/자바스크립트 혼합 환경에서도 문제가 없습니다.
먼저 as const
라는 타입스크립트 전용 기능을 이용하여 enum을 만들어 보겠습니다.
export const Color = {
RED: 'red',
BLUE: 'blue',
GREEN: 'green',
} as const
as const는 타입스크립트 컴파일 타임에서만 작동합니다. 각 프로퍼티의 타입을 리터럴 타입으로 고정해서 타입 안전성을 높여줍니다. 하지만 주의할 점이 있습니다: 실행 시에는 아무 일도 일어나지 않습니다. 즉, 런타임에서는 그냥 평범한 객체일 뿐이므로 실제 실행 중에는 객체를 변경할 수 있습니다.
// 컴파일 환경
Color.RED = 'something else' // 타입 오류 발생!
// 런타입 환경
Color.RED = 'SOMETHING_ELSE' // 런타임에서는 변경됨!
console.log(Color.RED) // 'SOMETHING_ELSE'
위 문제점을 보완한 것이 Object.freeze
입니다.
Object.freeze
는 자바스크립트의 런타임 기능입니다. 이걸 쓰면 프로퍼티의 추가, 삭제, 변경이 모두 막힙니다. (엄격 모드에서는 에러가 발생하고, 그렇지 않으면 무시됩니다.)
const Color = Object.freeze({
RED: 'red',
BLUE: 'blue',
GREEN: 'green',
})
한가지 더 알아야 할 부분이 있습니다. Object.freeze
를 활용하여 만든 enum을 사용해보겠습니다.
function colorFunction(color: typeof Color) {
return color
}
colorFunction('red')
이렇게 사용 할경우 아래와 같은 타입 오류가 발생합니다. 현재 해당 함수의 파라미터 Color는 기대하는 건 “red”라는 문자열이 아니라 전체 객체 그 자체 를 원하고 있습니다.

그래서 아래와 같이 타입을 유니온 타입으로 변경하는 작업이 필요합니다.
import type { ValueOf } from 'type-fest'
export const Color = Object.freeze({
RED: 'red',
BLUE: 'blue',
GREEN: 'green',
})
export type ColorType = ValueOf<typeof Color> // 'red' | 'blue' | 'green'
function colorFunction(color: ColorType) {
return color
}
colorFunction('red') // 정상 동작!
또 하나의 주의할 점이 남았습니다.
Object.freeze는 얕은(shallow) 고정만 합니다.
const obj = Object.freeze({
a: 1,
b: { c: 2 },
})
obj.a = 999 // 변경되지 않습니다!
obj.b.c = 999 // 변경 됩니다!
이처럼 객체 안에 중첩된 객체가 있으면 그 안쪽 값들은 변경할 수 있습니다. 그러나 보통 enum처럼 쓰는 객체는 단순히 문자열이나 숫자만 포함되기 때문에 이런 얕은 고정 문제는 발생하지만 사용할때는 이 부분의 주의가 필요합니다.