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

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

📍예제

🔗 웹 애플리케이션

애플리케이션에서 페이지를 선택하면, 페이지의 내용을 로드하고 화면에 표시한다. 페이지의 상태는 다음과 같이 설계한다.

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

// 상태 객체의 필드를 전부 고려해서 상태 표시를 분기
function renderPage(state: State) {
  if (state.error) {
    return `Error! Unable to load ${currentPage}: ${state.error}`;
  } else if (state.isLoading) {
    return `Loading ${currentPage}...`;
  }
  return `<h1>${currentPage}</h1>\n${state.pageText}`;
}

코드를 살펴보면 조건이 명확히 분리되어 있지 않다. isLoading이 true이고 동시에 error 값이 존재하면 로딩 중인 상태인지 오류가 발생한 상태인지 명확히 구분할 수 없다. 필요한 정보가 부족하기 때문이다.

❓ changePage에는 많은 문제점이 있다.

☑️ 오류가 발생했을 때 state.isLoading을 false로 설정하는 로직이 빠졌다.

☑️ state.error를 초기화하지 않았기 때문에, 페이지 전환 중에 로딩 메시지 대신 과거의 오류 메시지를 보여주게 된다.

☑️ 페이지 로딩 중에 사용자가 페이지를 바꿔 버리면 어떤 일이 벌어질지 예상하기 어렵다. 새 페이지에 오류가 뜨거나, 응답이 오는 순서에 따라 두 번째 페이지가 아닌 첫 번재 페이지로 전환 될 수도 있다.

→ 바로 상태 값의 두 가지 속성이 동시에 정보가 부족하거나, 두 가지 속성이 충돌할 수 있다.

State 타입은 isLoading이 true이면서 동시에 error 값이 설정되는 무효한 상태를 허용한다. 무효한 상태가 존재하면 render()와 changePage() 둘 다 제대로 구현할 수 없다.

⭐️ 애플리케이션의 상태를 좀 더 제대로 표현하기

네트워크 요청 과정 각각의 상태를 명시적으로 모델링하는 태그된 유니온이 사용되었다. 코드가 길어지긴 했지만, 무효한 상태를 허용하지 않도록 개선되었다.

renderPage와 changePage의 모호함은 완전히 사라졌다. 현재 페이지가 무엇인지 정확히 하나의 상태로 맞아 떨어진다. 무효가 된 요청이 실행되긴 하겠지만 UI에는 영향을 미치치 않는다.

🔗 비행기 추락 사례

타입스크립트로 이중 입력 모드 상태를 모델링해보자.

이 데이터 구조가 주어진 상태에서 현재 스틱의 설정을 계산하는 getStickSeting 함수를 작성해보자.

→ 기장이든 부기장이든 둘 중 하나의 스틱 값 중에서 중립이 아닌 값을 사용해야 한다.

❓ 왼쪽 스틱의 로직과 동일하게 오른쪽 스틱이 중립일 때 왼쪽 스틱 값을 사용해야 한다. 오른쪽 스틱에 대한 체크를 코드에 추가해야 한다.

두 스틱이 모두 중립이 아닌 경우, 두 스틱이 비슷한 값이라면 스틱의 각도를 평균해서 계산할 수 있다.

두 스틱의 각도가 매우 다른 경우는 해결하기 어렵다. 조종사에게 오류를 띄울 수는 없다.

→ 대부분의 비행기는 두 개의 스틱이 기계적으로 연결되어 있다. 부기장이 뒤로 당긴다면, 기장의 스틱도 뒤로 당겨진다.

⭐️ 기계적으로 연결된 스틱의 상태 표현

순서도(코드의 로직)이 분명해졌다. getStickSetting 함수는 전혀 필요 없다.

타입을 설계할 때는 어떤 값들을 포함하고 어떤 값들을 제외할 지 신중하게 생각해야 한다. 유효한 상태를 표현하는 값만 허용한다면 코드를 작성하기 쉬워지고, 타입 체크가 용이하다. 유효한 상태만 허용하는 것은 매우 일반적인 원칙이다.

📍 요약

  • 유효한 상태와 무효한 상태를 둘 다 표현하는 타입은 혼란을 초래하기 쉽고 오류를 유발하게 된다.

  • 유효한 상태만 표현하는 타입을 지향해야 한다. 코드가 길어지거나 표현하기 어렵지만 결국은 시간을 절약할 수 있다.

Last updated