본문 바로가기

IT

Effective Typescript - 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}`;
}

async function chagePage(state: State, newPage: string){
    state.isLoading = true;

    try{
        const res = await fetch(getUrlFroPage(newPage));
        if(!res.ok){
            throw new Error(`unable to load ${newPage} : ${res.statsText}`);
        }
        const text = await res.text();
        state.isLoading = false;
        state.pageText = text;
    }catch(e){
        state.error = '' + e;
    }
}
 
// 아래는 추천 방식
interface ReqPending {
    state: 'pending';
}

interface ReqError{
    state: 'error';
    error: string;
}

interface ReqSuccess{
    state: 'ok';
    PageText: string;
}

type ReqState = ReqPending | ReqError | ReqSuccess;

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

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

async function changePageFix(state: State, newPage: string){
    state.requests[newPage] = {state : 'pending'};
    state.currentPage = newPage;
    try{
        const res = await fetch(getUrlForPage(newPage));
        if(!res.ok){
            throw new Error(`Unable to load ${newPage}: ${res.statusText}`);
        }
        const pageText = await res.text();
        state.requests[newPage] = {state: 'ok', pageText};
    }catch(e){
        state.requests[newPage] = {state: 'error', error: '' + e};
    }
}

책에서는 인터페이스 설계시 '무효한 상태 설계' '유효한 상태 설계' 라고 표현하고 있다. 앞의 예제는 무효한 상태를 가지는 인터페이스로 인해 코드가 허점이 많게 작성되는 것을 보여준다. 다음 이어지는 예제는 유효한 상태를 가지는 인터페이스 설계로 앞의 예제에서 발생하는 오류들을 해결하는 것을 보여준다.

 

그렇다면 무효한 상태란 무엇인가?

-> 어떠한 상태를 완벽하게 설명하지 못하는 설계 구조?

-> 속성(property)에 optional한 상태를 가고 있고(예제에선 error? 속성) 이것을 활용한 코드 구현은 복잡해 진다.

 

유효한 상태란 무엇인가?

-> 어떠한 상태를 완벽하게 설명가능한 설계 구조?

-> 속성(property)에 optional한 상태를 가지는 것을 지양하는 설계 구조.

 

결론적으로, 저자는 "유효한 상태" 표현이 가능한 타입 설계를 하라고 강조하고 있다.