iBetter Books
수정

메뉴판에 없는 건 주문 못 합니다

카페에서 음료를 주문할 때, 메뉴판에 없는 걸 달라고 하면 어떻게 될까요? "그런 메뉴는 없습니다"라는 답이 돌아올 겁니다. 메뉴판이 있으면 주문 가능한 선택지가 명확히 제한됩니다.

TypeScript의 리터럴 타입(Literal Type)이 딱 이런 역할을 합니다. 변수에 담을 수 있는 값을 딱 몇 가지로 고정하는 것입니다.

리터럴 타입이란

이전 챕터에서 const로 변수를 선언하면 타입이 좁아진다고 잠깐 언급했습니다.

const direction = "left"; // 타입: "left" (string이 아님!)

"left"라는 값 자체가 타입이 됩니다. 이것이 리터럴 타입입니다.

이걸 활용하면 특정 값만 허용하는 타입을 만들 수 있습니다.

let align: "left" | "right" | "center";align = "left";   // OKalign = "center"; // OKalign = "top";    // 에러! Type '"top"' is not assignable to type '"left" | "right" | "center"'.

"left", "right", "center" 세 가지 값만 허용합니다. 다른 문자열은 들어올 수 없습니다.

유니언 타입과 함께 쓰는 리터럴

| 기호는 "또는"을 의미합니다. 이것을 유니언 타입이라고 합니다.

type Direction = "left" | "right" | "up" | "down";type Status = "pending" | "success" | "error";type Coin = 10 | 50 | 100 | 500;

리터럴 타입은 문자열뿐 아니라 숫자에도 쓸 수 있습니다.

function move(direction: Direction) {  console.log(`${direction} 방향으로 이동합니다.`);}move("left");   // OKmove("up");     // OKmove("jump");   // 에러! Argument of type '"jump"' is not assignable to parameter of type 'Direction'.

함수 매개변수에 리터럴 타입을 쓰면 잘못된 값이 들어오는 것을 컴파일 시점에 막을 수 있습니다.

타입 별칭으로 이름 붙이기

type 키워드를 사용하면 복잡한 타입에 이름을 붙일 수 있습니다.

type Align = "left" | "right" | "center";type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";type DayOfWeek = "월" | "화" | "수" | "목" | "금" | "토" | "일";

이름을 붙여두면 여러 곳에서 재사용할 수 있습니다.

function setTextAlign(element: HTMLElement, align: Align) {  element.style.textAlign = align;}function fetchData(url: string, method: HttpMethod) {  // ...}

열거형 — 상수를 하나로 묶기

리터럴 타입과 비슷한 용도로 쓰이지만 다른 문법이 있습니다. 바로 열거형(Enum)입니다.

enum Direction {  Left,  Right,  Up,  Down,}

Direction.Left, Direction.Right처럼 점으로 접근합니다.

function move(direction: Direction) {  console.log(direction);}move(Direction.Left);  // OKmove(Direction.Up);    // OKmove(0);               // OK (숫자 열거형은 숫자도 허용됨 — 주의!)

기본적으로 enum은 숫자 값을 가집니다. Left는 0, Right는 1, Up은 2, Down은 3입니다. 직접 값을 지정할 수도 있습니다.

enum Direction {  Left = "LEFT",  Right = "RIGHT",  Up = "UP",  Down = "DOWN",}move(Direction.Left);   // OKmove("LEFT");           // 에러! 문자열 열거형은 값으로 직접 접근 불가

문자열 열거형은 숫자 열거형보다 안전합니다. 숫자 열거형은 숫자를 직접 넘겨도 에러가 나지 않기 때문입니다.

const enum — 더 가볍게

일반 enum은 컴파일 후 JavaScript 코드에 객체가 생성됩니다. const enum을 사용하면 컴파일 시 값으로 인라인 처리되어 런타임 객체가 생기지 않습니다.

const enum Direction {  Left = "LEFT",  Right = "RIGHT",  Up = "UP",  Down = "DOWN",}const dir = Direction.Left;// 컴파일 후: const dir = "LEFT";

번들 크기를 줄이고 싶을 때 const enum을 사용합니다. 다만 외부 모듈에서 가져다 쓰거나 동적으로 접근할 때는 제한이 있습니다.

리터럴 타입 vs enum 비교

구분 리터럴 타입 enum
문법 "left" | "right" enum Direction { Left, Right }
값 접근 "left" 직접 사용 Direction.Left
JS 변환 타입만 있고 코드 없음 객체가 생성됨
자동완성 지원 지원
추천 상황 간단한 선택지 명확한 도메인 상수 그룹

실무에서는 enum보다 리터럴 타입과 type 별칭 조합을 더 많이 사용하는 추세입니다. enum은 컴파일 결과에 JavaScript 코드가 추가되기 때문입니다. 하지만 enum은 가독성이 좋고 의미가 명확해서 팀에 따라 여전히 많이 씁니다.

실전 예제

실제 코드에서 어떻게 쓰이는지 확인해보겠습니다.

type ButtonVariant = "primary" | "secondary" | "danger";type ButtonSize = "small" | "medium" | "large";interface ButtonProps {  variant: ButtonVariant;  size: ButtonSize;  label: string;}function renderButton(props: ButtonProps) {  const { variant, size, label } = props;  return `<button class="${variant} ${size}">${label}</button>`;}renderButton({ variant: "primary", size: "medium", label: "확인" }); // OKrenderButton({ variant: "big", size: "medium", label: "확인" });     // 에러!

"big"ButtonVariant에 없는 값이므로 에러가 납니다. 오타나 실수를 컴파일 시점에 잡아주는 것입니다.

다음 챕터에서는 타입 시스템에서 특수한 역할을 하는 세 가지 타입, any, unknown, never를 살펴보겠습니다. 이 세 가지를 이해하면 TypeScript가 진짜 왜 any를 싫어하는지 알게 됩니다.

Ch 04. 선택지를 제한하는 힘 — 리터럴 타입과 열거형 — 소설처럼 읽는 TypeScript | iBetter Books