0496

FE

[TS] 리터럴 유니온(Literal Union)으로 더 안전한 코드를 작성하는 방법

| 서론

안녕하세요, 팡일입니다.

TypeScript를 사용하다 보면 "loading", "success", "error" 같은 상태값을 자주 다루게 됩니다. 처음에는 단순히 string 타입으로 선언하고 사용하지만, 프로젝트 규모가 커질수록 오타나 잘못된 값으로 인해 예상치 못한 버그가 발생하기도 합니다.

이럴 때 유용하게 사용할 수 있는 기능이 바로 Literal Union입니다. 최근 코드를 작성하면서 다시 한 번 정리해볼 필요성을 느껴 간단하게 기록해보려고 합니다.

| Literal Union이란?

Literal Union은 특정 변수나 속성이 가질 수 있는 정확한 값들의 집합을 정의하는 타입입니다.

쉽게 말하면 자바스크립트의 || 연산자처럼 “A이거나 B이거나 C”라는 의미를 타입으로 표현하는 방식입니다.

예를 들어 버튼 크기가 small, medium, large 세 가지 중 하나만 가능하다고 가정해보겠습니다.

code
type ButtonSize = "small" | "medium" | "large";

const size: ButtonSize = "medium";

이 경우 TypeScript는 small, medium, large 외의 값이 들어오는 것을 허용하지 않습니다.

code
const size: ButtonSize = "mega"; // Error

컴파일 단계에서 바로 오류를 확인할 수 있기 때문에 런타임 버그를 예방하는 데 도움이 됩니다.

| Literal Type과 Union Type

Literal Union은 사실 두 가지 개념의 조합입니다.

1) 첫 번째는 Literal Type입니다.

code
type Status = "loading";

: 여기서 "loading" 자체가 타입이 됩니다. 일반적인 string 타입이 아니라 "loading"이라는 특정 값만 허용하는 타입인 것입니다.

2) 두 번째는 Union Type입니다.

code
type Status = "loading" | "success" | "error";

: 파이프(|) 기호를 사용하여 여러 타입 중 하나를 선택할 수 있도록 만듭니다.

-> 이 두 개념을 결합하면 “정해진 값들 중 하나만 허용하는 타입”을 만들 수 있습니다.

| 숫자도 제한할 수 있다

문자열뿐만 아니라 숫자도 동일하게 사용할 수 있습니다.

주사위를 표현한다면 다음과 같이 작성할 수 있습니다.

code
type Dice = 1 | 2 | 3 | 4 | 5 | 6;

또는 특정 등급 체계가 존재한다면 아래와 같이 정의할 수도 있습니다.

code
type Grade = 1 | 2 | 3 | 4 | 5 | 6;

이처럼 허용 가능한 숫자를 명확하게 표현할 수 있어 비즈니스 규칙을 타입 수준에서 관리할 수 있습니다.

| 왜 사용할까?

Literal Union의 가장 큰 장점은 타입 안정성입니다.

IDE에서는 자동 완성을 제공해주고, 허용되지 않은 값을 입력하면 즉시 오류를 표시해줍니다.

code
type RequestStatus =
  | "idle"
  | "loading"
  | "success"
  | "error";

위와 같이 상태를 정의해두면 상태값을 사용할 때 실수로 "sucess"와 같은 오타를 작성하더라도 컴파일 단계에서 바로 확인할 수 있습니다.

또한 TypeScript의 타입 좁히기(Type Narrowing)와도 자연스럽게 연결됩니다.

code
if (status === "success") {
  // TypeScript가 status를 success 타입으로 추론
}

조건문 안에서는 TypeScript가 현재 가능한 타입을 자동으로 추론해주기 때문에 더 안전하고 읽기 쉬운 코드를 작성할 수 있습니다.

| 마무리

Literal Union은 단순히 타입을 엄격하게 만드는 기능이 아닙니다. “이 값은 반드시 이것들 중 하나여야 한다”는 도메인 규칙을 코드에 그대로 표현할 수 있게 해주는 도구라고 생각합니다.

특히 상태 관리, 컴포넌트 Props, API 응답 상태와 같이 허용 가능한 값이 명확한 경우 적극적으로 활용하면 코드의 가독성과 안정성을 모두 높일 수 있습니다.

TypeScript를 사용하고 있다면 string이나 number로 넓게 선언하기 전에, 실제로 허용되는 값의 범위를 Literal Union으로 표현할 수는 없는지 한 번 고민해보는 것도 좋은 습관인 것 같습니다.