최근 프로젝트에서 드래그 앤 드롭 기능을 구현하면서 Exclude를 활용해볼 수 있었다. 당시의 exclude 로직은 아래와 같다.
export type LogicDragPayload =
| { type: Exclude<LogicPaletteComponentId, typeof BoardNodeType.TABLE.type> }
| { type: typeof BoardNodeType.TABLE.type; tableId: string; name: string };
이 코드가 등장한 이유부터 이야기해보려 한다.
하나만 다르게 처리해야 한다
내가 진행하고 있는 프로젝트에서는 여러 종류의 컴포넌트를 드래그해 캔버스에 노드를 생성할 수 있다. 각 컴포넌트는 드래그 시 특정 데이터(Node type)를 동반하며, 대부분의 컴포넌트는 동일한 구조를 가지고 있다.
하지만 TABLE 타입은 예외였다. 이 노드는 일반적인 데이터(Node type) 외에도 추가 정보(tableId, name)를 반드시 포함해야 했다. 즉, 하나의 타입만 별도의 데이터 구조를 가져야 하는 상황이었다.
프로젝트 구조 설명
모든 노드 타입은 아래처럼 BoardNodeType 상수 객체로 정의되어 있다.
export const BoardNodeType = {
EX_TYPE_1: { type: "ExType1", label: "Ex Type 1" },
EX_TYPE_2: { type: "ExType1", label: "Ex Type 2" },
TABLE: { type: "Table", label: "Table" }
} as const;
LogicPaletteComponentId는 드래그 가능한 모든 컴포넌트의 type을 모은 유니온 타입이다.
export type LogicPaletteComponentId =
| typeof BoardNodeType.EX_TYPE_1.type
| typeof BoardNodeType.EX_TYPE_2.type
| typeof BoardNodeType.TABLE.type
문제의 핵심
대부분의 노드는 { type: LogicPaletteComponentId } 형태로 충분했지만, TABLE만은 추가 데이터가 필요했다.
이때, Exclude를 사용해서 TABLE을 제외한 나머지 타입들만 묶어주면, LogicDragPayload를 안전하게 분기할 수 있다.
export type LogicDragPayload =
| { type: Exclude<LogicPaletteComponentId, typeof BoardNodeType.TABLE.type> }
| { type: typeof BoardNodeType.TABLE.type; tableId: string; name: string };
결과적으로, "Table" 타입일 때만 tableId, name이 반드시 필요하고 그 외 타입은 오직 type만 존재한다.
실무 예시
Exclude는 단순히 타입을 하나 빼는 용도로 동작하는 것처럼 보이지만, 도메인 규칙을 타입 레벨에서 강제할 때 자주 쓰인다.
예를 들어, 특정 상태(loading, error, success) 중 하나만 별도 데이터를 가질 때나 API 응답 상태에서 deprecated 상태만 제거할 때 등이 그 예이다.
아래 코드처럼 정의해두면, 분기 구조를 타입으로 보장할 수 있다.
type Status = "loading" | "error" | "success";
type NormalStatus = Exclude<Status, "error">;
// 결과: "loading" | "success"
type ApiResponse =
| { status: "ok"; data: string }
| { status: "error"; message: string }
| { status: "deprecated" };
type ValidResponse = Exclude<ApiResponse, { status: "deprecated" }>;
'Typescript' 카테고리의 다른 글
| const assertions 이해하기 (0) | 2025.10.24 |
|---|---|
| React Flow에서 제네릭(Generic) 이해하기 (0) | 2025.10.19 |
| Union vs Enum vs Const enum 타입 중 어떤 걸 사용해야 할까? (0) | 2025.09.29 |
| unknown 타입과 any의 차이 (0) | 2025.02.06 |
| 왜 타입스크립트를 쓰나요? (1) | 2024.12.26 |