본문 바로가기

Language/TypeScript

<TS> 태그드 유니온 패턴

1. 소개

 이펙티브 타입스크립트에서 다음과 같이 Tagged Union Pattern을 시작하고 있다.

 

효과적으로 타입을 설계하려면, 유효한 상태만 표현할 수 있는 타입을 만들어 내는 것이 가장 중요합니다.

- 아이템28, p156 -

 

 태그드 유니온 패턴은 타입에 태그를 추가하여 효율적으로 타입을 설계할 수 있는 방식이다. 이를 어디서 활용하면 좋을지 크게 두 가지로 나누어 정리해 보았다.

 

2. 사용

* 각 필드가 연관되어 특정될 경우

- 특정 페이지의 상태를 설계한다고 생각해보자. 아마 다음과 같은 타입의 상태를 정의할 수 있을 것이다

interface State {
  pageText: string;
  isLoading: boolean;
  error?: string;
}

 

 - 단순히 봤을 때는 문제가 없어 보인다. 그러나 페이지를 직접 렌더링하는 함수를 작성하게 되면, 우리는 에러가 있는지 없는지 체크를 해야하며, isLoadingtrue인지 false인지를 체크해야 한다. 이렇게만 하더라도 2*2 가지의 상황이 생긴다. isLoadingtrue일때 에러는 없음에도 불구하고 말이다.

 

 - 만약 페이지를 전환하는 함수까지 추가된다면 이전 페이지의 상태까지 확인해야 하므로 더 많은 정보를 신경써야 한다. 따라서 명확하게 상태를 표현하려면 아래와 같이 각 상태를 연관이 있는 방향으로 특정되도록 설계할 필요가 있다.

 

interface RequestPending {
  state: 'pending'
}
interface RequestError {
  state: 'error';
  error: string;
}
interface RequestSuccess {
  state: 'ok'
  pageText: string;
}
type RequestState = RequestPending | RequestError | RequestSuccess;

interface State {
  currentPage: string;
  requests: {[page: string]: RequestState};
}

 

 - 요청 과정의 상태를 태그드유니온을 사용하여 명시적으로 표현하였다. 이 때 태그는 state가 될 것이며, 유니온 타입으로 요청시의 상태(RequestState)라는 것을 명확하게 표현하였다.

 

 - 이로써 렌더링 하는 함수는 훨씬 짧고 명확하게 switch문으로 구현할 수 있게되었다. 뿐만 아니라 State를 좀 더 상위의 개념으로 표현하면서 requests를 감싸서 표현하였다. 이를 통해 페이지를 전환하더라도 각 페이지를 구분지어 분별할 수 있게 된다.

 

유니온 타입의 속성을 가지는 인터페이스를 작성 중이라면, 혹시 인터페이스의 유니온 타입을 사용하는 게 더 알맞지는 않을지 검토해 봐야 합니다.

- 아이템32, p173 -

 

 - 이번에는 조금 더 복잡하게 그림을 그리는 프로그램의 인터페이스를 생각해보자. 이는 유니온 타입의 속성을 여러 개 갖게된다.

interface Layer {
  layout: FillLayout | LineLayout | PointLayout;
  paint: FillPaint | LinePaint | PointPaint;
}

 

 - 각 필드가 연관이 있다. 예를 들어 LineLayoutLinePaint만 필요할 것이다. 따라서 이 경우도 위에서 봤던 예시처럼 각 상태를 필드가 연관이 있는 방향으로 특정되도록 설계하는 것이 추후 함수를 작성할 때 효과적이다.

 

 - 태그를 사용한 태그드 유니온패턴을 사용한 코드는 다음과 같다.

interface FillLayer {
  type: 'fill';
  layout: FillLayout;
  paint: FillPaint
}
interface LineLayer {
  type: 'line';
  layout: LineLayout;
  paint: LinePaint;
}
interface PointLayer {
  type: 'point';
  layout: PointLayout;
  paint: PointPaint;
}
type Layer = FillLayer | LineLayer | PointLayer;

 

 - type 이라는 태그를 주었고, 이는 함수를 작성할 때 이 레이어가 어떤 레이어인지를 구분할 수 있게 해줄 것이다. 자연스럽게 타입스크립트는 타입에 맞는 레이아웃과 페인트를 유추할 수 있게 된다.

 

* 여러 개의 선택적 필드가 함께 있거나 없을 경우

- 위에서는 각 필드가 연관이 있었다면, 이번엔 각 필드가 동시에 존재하거나 없어야 하는 경우이다.

 

여러 개의 선택적 필드가 동시에 값이 있거나 동시에 undefined인 경우도 태그드 유니온 패턴이 잘 맞습니다.

- 아이템32, p176 -

 

 - 간단한 예시로 사람을 나타내는 타입을 살펴보자.

interface Person {
  name: string;
  birth: Date;
  school?: string;
  studentId?: number;
}

 

 - 위의 경우 schoolstudentId는 함께 존재하거나 함께 없는 필드이다. 그럼에도 불구하고 이러한 정보를 알 수 없으며, 이는 코드를 작성할 때 실수를 할 여지를 남긴다. 이를 태그드 유니온 패턴을 사용한다면 어떻게 작성할 수 있을까

interface Person {
  name: string;
  birth: Date;
  school?: {
    name: string;
    studentId: number;
  }
}

 

 - 위는 school을 마치 태그처럼 사용한 것이다. 이제 school필드가 있는지만 체크하더라도 학교이름과 학번을 가지는 객체라는 것이 명확해 진다.

 

 - 하지만 위는 객체의 구조가 바뀌어야 하는 경우이다. 만약 객체의 구조를 손댈 수 없다면 어떨까. 이럴 때에는 인터페이스의 유니온을 사용할 수 있다.

interface Human {
  name: string;
  birth: Date;
}
interface Student extends Human {
  school: string;
  studentId: number;
}
type Person = Human | Student;

 

 - 위처럼 작성하면 타입 코드만 읽더라도 관계가 명확해 진다. 코드를 작성할 때에도 school만을 태그로 판별하면 해당 객체가 Human인지 Student인지 체크할 수 있다.

 


참고

 

 해당 포스팅은 이펙티브 타입스크립트를 읽고 개인적으로 필요한 내용을 추가 및 정리한 글입니다.

 

 

 

이펙티브 타입스크립트 - YES24

타입스크립트는 타입 정보를 지닌 자바스크립트의 상위 집합으로, 자바스크립트의 골치 아픈 문제점들을 해결해 준다. 이 책은 《이펙티브 C++》와 《이펙티브 자바》의 형식을 차용해 타입스

www.yes24.com

 

 

'Language > TypeScript' 카테고리의 다른 글

<TS> 타입가드  (0) 2022.09.03
<TS> 효과적으로 타입 명시하기  (0) 2022.07.16
<TS> 유틸리티 타입으로 반복 줄이기  (0) 2022.07.02