TypeScript enum을 쓰다가 번들 크기와 값 비교에서 겪은 문제
문제 발생
상태 값을 enum으로 정의해서 쓰고 있었는데, 두 가지 문제를 겪었습니다. 첫째, 번들 크기를 분석해보니 enum이 생각보다 많은 런타임 코드를 만들어내고 있었습니다. 둘째, 숫자 enum에 범위 밖의 숫자를 그냥 넣어도 타입 에러가 나지 않았습니다.
enum Status {
Idle,
Loading,
Success,
}
function setStatus(s: Status) { /* ... */ }
setStatus(999); // 숫자 enum은 이렇게 넣어도 에러가 안 남원인 분석
TypeScript의 다른 타입 관련 문법(interface, type)은 대부분 컴파일 시 완전히 제거되고 런타임에는 아무 흔적도 남기지 않습니다. enum은 예외입니다 — 컴파일되면 실제 JavaScript 객체(양방향 매핑을 포함)를 생성하는 런타임 코드가 만들어집니다. 사용하는 enum이 많아지면 이 코드가 번들 크기에 실제로 반영됩니다.
숫자 enum이 범위 밖 숫자를 받아들이는 것도 TypeScript의 알려진 특성입니다 — 숫자 enum은 내부적으로 일반 숫자와 상당히 느슨하게 호환되도록 설계되어 있어, 임의의 숫자를 그 enum 타입에 할당해도 타입 검사를 통과합니다. 이건 실수를 잡아주기를 기대하는 입장에서는 함정입니다.
해결 방안
대부분의 경우 문자열 리터럴 유니온 타입이 enum보다 안전하고 가벼운 대안입니다.
type Status = "idle" | "loading" | "success";
function setStatus(s: Status) { /* ... */ }
setStatus("unknown"); // 컴파일 에러 — 정확히 세 값 중 하나만 허용됨- 문자열 리터럴 유니온은
interface/type처럼 컴파일 후 런타임에 완전히 사라집니다 — 번들 크기에 영향을 주지 않습니다. - 정의된 값 외에는 절대 허용하지 않습니다 — 숫자 enum이 갖고 있던 "임의의 숫자 허용" 문제가 없습니다.
as const로 값 배열을 함께 관리하면, 실제 값 목록과 타입을 하나의 소스에서 파생시킬 수 있습니다.
const STATUSES = ["idle", "loading", "success"] as const;
type Status = (typeof STATUSES)[number]; // "idle" | "loading" | "success"- 정말로 런타임에 그 값 집합을 순회해야 하거나(반복문으로 전체 값을 돌아야 하는 경우), 다른 언어와의 상호운용(다른 시스템이 정수 값을 그대로 기대하는 경우)처럼 실제 런타임 객체가 필요한 명확한 이유가 있을 때만
enum을 고려합니다.const enum은 런타임 코드를 만들지 않지만, 프로젝트의 빌드 도구(특히 격리된 모듈 컴파일 환경)에 따라 제약이 있을 수 있어 사용 전 확인이 필요합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.