본문 바로가기
Book Review/Effective Typescript

Day9_Effective Typescript 스터디

by 예 강 2023. 6. 16.

2022년 12월 12일 글입니다.

Item 27. 함수형 기법과 라이브러리로 타입 흐름 유지하기

요약 : 타입스크립트의 타입정보가 그대로 유지되는 타입흐름(flow)을 계속 전달되로록 하는 라이브러리들과 함수형 기법을 적극 사용하자.
> 로대시(Lodash)나 람다(Ramda)같은 함수형 프로그래밍 개념이 자바 스크립트에도 도입되고 있다.

  • lodash 관련자료

기본적인 lodash의 사용방법을 적어놓겠다. 지금 익히기엔 너무 과한 것 같고, 나중에 개발할때 사용해야 겠다.

  • 함수 내부적으로는 문자열 리터럴 타입과 인덱스 타입의 조합으로만 이루어져 있기 때문에, 타입이 자연스럽게 도출된다고 한다.
  • 내장된 함수형 기법들과 로대시 같은 라이브러리에 타입 정보가 잘 유지되는데, 이는 함수 호출 시, 전달된 매개변수 값을 건드리지 않고 매번 새로운 값을 반환함 으로써, 새로운 타입으로 안저하게 반환할 수 있기 때문이다.
  • 로대시나 내장된 함수형 기법들(map, filter 등등)을 쓰면 굳이 하나하나 타입구문이나 단언문을 쓰지 않아도 되서 적극 활용하는게 가독성 면과 효율성 면으로도 좋다고 한다.

[개발상식] lodash 알고 쓰자.


4장. 타입설계

  • 타입스크립트의 장점 : 데이터 타입을 명확히 알 수 있어 코드를 이해하기 쉽다.
  • 그러니 그 중요한 타입을 설계하는 방법에 대해 알아보자!

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

요약 1 : 유효한 상태만 표현하는 타입을 지향해야 한다. 코드가 길어지거나 표현이 어려워질 수 있지만 결국은 시간을 절약하고 고통을 줄일 수 있다.
요약 2 : 유효, 무효한 상태를 둘 다 표현하는 타입은 혼란을 초래하기 쉽고 오류를 유발한다.

interface State {
  pageText: string;
  isLoading: boolean;
  error?: string;      //error가 optional로 선택된 것 부터 잘못됐다.
}

//이러면 isLoading이면서 error일때의 처리가 불가능해진다 == 무효한 상태


declare let currentPage: string;


//renderPage에는 etrror이면서 isLoading일때의 처리가 안되어있다.
//즉 무효한 상태를 표현하고 있다.
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}`;
}



// 1. isLoading중에 에러가 발생했다면 isLoading을 false로 만들어야 하는데, 그런 코드가 없어서 에러가 던져져도 isLoading이 트루인 괴상망측한 상태가 된다.
//2 . state.e를 초기화하는 구문이 없기 때문에 과거의 에러메세지가 계속 추가된다.
//3. 페이지 로딩중에 사용자가 페이지를 바꿨을 시 , 어떤일이 일어날지 예상 하기 어렵다. text는 먼저 도착하는 순서대로 text를 출력할 것이기 때문이다.

async function changePage(state: State, newPage: string) {
  state.isLoading = true;
  try {
    const response = await fetch(getUrlForPage(newPage));
    if (!response.ok) {
      throw new Error(`Unable to load ${newPage}: ${response.statusText}`);
    }
    const text = await response.text();
    state.isLoading = false;
    state.pageText = text;
  } catch (e) {
    state.error = '' + e;
  }

그래서 위와 같은 코드를 아래처럼 개선해보자

//각각의 상태마다 타입을 지정한다. 
//이렇게 되면 동시성을 띄는 상태가 없어지므로 무효한 상태를 표현할 수 없게 된다.
//태그된 유니온 기법을 사용했다. 
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 getUrlForPage(p: string) { return ''; }


//renderPage가 명확해졌다. 현재의 페이지를 받아와서 현재의 페이지의 응답을 기준으로 상태를 처리한다. 위 코드에서 봤던 loading이면서 error인 상태 없이 명확하게 분리되어 있다.

function renderPage(state: State) {
  const {currentPage} = state;
  const requestState = state.requests[currentPage];
  switch (requestState.state) {
    case 'pending':
      return `Loading ${currentPage}...`;
    case 'error':
      return `Error! Unable to load ${currentPage}: ${requestState.error}`;
    case 'ok':
      return `<h1>${currentPage}</h1>\n${requestState.pageText}`;
  }
}



// 1,2 . 로딩중에 에러가 발생한다면 바로 try~ catch문을 통해 에러를 던져준다. 에러는 {state:"error" , error:""+e}로 설정되기 때문에 에러메세지의 초기화와 상태의 설정이 동시에 된다.

//3. 페이지 로딩중에 사용자가 페이지를 바꿨을 시 , newPage로 인자가 들어오고 상태를 pending으로 변경한다. state.requests[newPage] newPagr를 인자로 받기때문에, 키값 설정에 따른 상태를 설정한다.즉 계속적으로 currentPage를 갱신해주기 때문에 명확한 것 같다.

async function changePage(state: State, newPage: string) {
  state.requests[newPage] = {state: 'pending'};
  state.currentPage = newPage;
  try {
    const response = await fetch(getUrlForPage(newPage));
    if (!response.ok) {
      throw new Error(`Unable to load ${newPage}: ${response.statusText}`);
    }
    const pageText = await response.text();
    state.requests[newPage] = {state: 'ok', pageText};
  } catch (e) {
    state.requests[newPage] = {state: 'error', error: '' + e};
  }
}
  • 유효한 상태, 무효한 상태라는 말이 조금 헷갈리는데,
    저자는 결국 active한 현재 상태만 제대로 표현하라고 하는 것 같다. 아직 살짝 이해하기 어렵지만 저자 말대로 case문으로 명확하게 하고, currentPage를 destructing으로 받아 오는 로직은 기억해 둘만 하다.