아이템 28. 유효한 상태만 표현하는 타입을 지향하기

아이템 28. 유효한 상태만 표현하는 타입을 지향하기

타입 시스템의 큰 장점 중 하나는 데이터 타입을 명확히 알 수 있어 코드를 이해하기 쉽다는 것이다.
4장(타입 설계)에서는 타입 자체의 설계에 대해 다룬다.
대부분 다른 언어에서도 적용될 수 있는 아이디어다.


아이템 28. 유효한 상태만 표현하는 타입을 지향하기 #

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

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

위 타입이 페이지를 렌더링하는 상태를 나타낸다고 가정할 때, 이 타입은 애매하다. (모호하다.)

즉 잘못된 타입(설계)이다.
어떻게 사용하느냐에 따라 다를 수도 있을 것 같다.

예를 들어 다음과 같은 코드가 있다.

function renderPage(state: State) {
    if(state.error) {
        // 에러 ...
    } else if(state.isLoading) {
        // 로딩 중...
    } else {
        // 정상 ...
    }
}

위 코드에서 만약 ’error 값이 있고’, isLoading 이 true 라면?

error 분기를 타겠지만 이건 의도한건가?

이런 관점에서 이 타입은 명확하지 않다.


아래와 같이 개선해볼 수 있다.

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};
}

function renderPage(state: State) {
    const {currentPage} = state;
    const requestState = state.requests[currentPage];
    switch(requestState.state) {
        case 'pending':
            // pending ...
        case 'error':
            // error ...
        case 'ok':
            // ok ...
    }
}

요청에 대한 각각의 상태를 명시적으로 모델링하는 태그된 유니온(또는 구별된 유니온) 을 사용했다.

이 상태는 무효한 상태를 허용하지 않는다.
각각의 상태에서 발생할 수 있는 요청의 모든 상태를 표현한다.

이후 책에서 설명하는 에어프랑스 447 항공편 에어버스 이야기가 재밌다.



유효한 상태를 표현하는 값만 허용한다면 코드를 작성하기 쉬워지고 타입 체크가 용이해진다. #

타입을 설계할 때는 어떤 값들을 포함하고, 어떤 값들을 제외할지 신중하게 생각해야 한다.

유효한 상태를 표현하는 것에 집중하자.



요약 & 정리 #

  • 유효한 상태와 무효한 상태를 둘 다 표현하는 타입은 혼란을 초래하기 쉽다. (+ 오류를 유발하기 쉽다.)
  • 유효한 상태만 표현하는 타입을 지향해야 한다.
    • 코드가 길어지거나 표현하기 어려울 수 있다.
    • 하지만 결국에는 시간을 절약하고 고통을 줄일 수 있다.