[Mere] 타입 가드를 활용해 컴파일러 적극 활용하기

BangDori·2023년 12월 20일
0

Mere

목록 보기
2/5

타입스크립트를 갓 배우기 시작한 개발자가 기존의 자바스크립트 프로젝트에서 타입스크립트로 마이그레이션을 하면서 타입 좁히기를 어떻게 활용했는지, 그리고 로그인 양식 컴포넌트에서 어떻게 적용하여 IDE를 더 적극적으로 활용할 수 있었는지 후기를 공유해보겠습니다!

📕 목차

  1. 기존의 로그인 컴포넌트
  2. 객체의 Key값으로 로그인 모드 분리하기
  3. 인덱스 시그니처를 활용하여 문제 해결하기
  4. 타입 가드를 활용해 컴파일러 적극 활용하기
  5. 후기

기존의 로그인 컴포넌트

// 기존의 LoginForm.jsx

const LoginForm = ({ mode }) => {
  // ...

  return (
    <form className='login-form' method='post'>
      <input
        type='text'
        placeholder={mode === 'admin' ? '관리자번호' : '아이디'}
      />
      <p className='id-save-message'>
        {mode === 'admin' ? '관리자번호 저장' : '아이디 저장'}
      </p>
        
      {/* ... */}
      <button type='submit' className='login-submit-btn'>
        로그인
      </button>
    </form>
  );
};

기존의 로그인 양식 컴포넌트에서는 위 코드처럼 로그인 모드에 따라 삼항 연산자로 구성되어 있었습니다.

isError ? errorMessage : null 과 같은 삼항 연산자가 아니고 사용자의 로그인 모드에 따라 분기 처리해줘야 할 경우 위와 같이 코드가 작성되었는데 위 코드는 깔끔해 보이지 않을 뿐더러, 첫 조건문으로 무엇을 먼저 해주는것이 더욱 높은 가독성을 향상해줄 수 있을지에 대해 의문을 품고 있었습니다.

ChatGPT와 함께 해당 코드의 가독성을 어떻게 더 높일 수 있을지에 대해 토론하던 중 GPT가 해당 문자열들을 로그인 모드에 대한 객체로 분리하는 방법에 대해 제시해주었습니다.

객체의 key 값으로 로그인 모드 분리하기

우선 로그인 모드에 따라 달라지는 FORM_PLACEHOLDERS 객체를 생성하고, 로그인 모드(owner, admin)에 따라 다른 프로퍼티를 가지도록 아래와 같이 생성하였습니다.

const FORM_PLACEHOLDERS = Object.freeze({
  owner: {
    id: '아이디',
    save: '아이디 저장',
  },
  admin: {
    id: '관리자번호',
    save: '관리자번호 저장',
  },
});

Object.freeze()란 객체를 동결하여, 속성의 불변성을 유지하기 위한 메서드입니다. 동결된 객체는 새로운 속성을 추가하거나 존재하는 속성을 제거하는 것을 방지해주기 때문에, 추후 변경될 여지가 없는 경우 Object.freeze() 메서드를 활용하여 속성의 불변성을 유지해주는 것이 좋은 선택이 될 수 있습니다. MDN Object.freeze()

FORM_PLACEHOLDERS 활용하여 아래와 같이 로그인 폼을 수정하였습니다.

// 객체를 활용한 LoginForm.tsx

const LoginForm: React.FC<{ mode: string }> = ({ mode }) => {
  // ...

  return (
    <form className='login-form' method='post'>
      <input
        type='text'
        placeholder={FORM_PLACEHOLDERS[mode].id}
      />
      <p className='id-save-message'>
        {FORM_PLACEHOLDERS[mode].save}
      </p>
      
      {/* ... */}
      <button type='submit' className='login-submit-btn'>
        로그인
      </button>
    </form>
  );
};

하지만 위 코드에서는 아래와 같은 에러가 발생하는 것을 확인할 수 있었습니다.

해당 에러메시지는 modeowneradmin 뿐만 아니라, string 타입의 어떠한 값도 가져올 수 있기 때문에 암묵적으로 값이 any 타입을 가질 수 있다고 알려주는 컴파일 에러라는 것을 확인할 수 있었습니다. (타입스크립트 너 정말 매력적이구나..)

인덱스 시그니처를 활용하여 문제 해결하기

인덱스 시그니처를 활용하면 Object에 들어있는 다른 객체를 문자열로 액세스할 수 있기에, 인덱스 시그니처를 통해 이에 대처하였습니다.

interface LoginPlaceholders {
  id: string;
  save: string;
}

interface FormPlaceholders {
  [key: string]: LoginPlaceholders; // ✅
  admin: LoginPlaceholders;
  owner: LoginPlaceholders;
}

기존에 FormPlaceholders 타입에 대해 key를 string 타입으로 액세스 가능하게 설정해준 후에 에러가 발생하지 않는 것을 확인할 수 있었습니다.

하지만 인덱스 시그니처를 통해 접근하는 것은 결국 또 다른 에러를 낳을 수 있다는 사실을 코드 리뷰를 통해 이해할 수 있었습니다.

이는 FORM_PLACEHOLDER에 접근하는 key에게 owneradmin 값을 가질 것이라고 알려준 것이 아닌, string 타입을 가질 것이라고 알려준 것이기 때문에 발생한 에러였습니다.

실제로 mode를 임의로 user로 설정하고 테스트해 본 결과 어떠한 에러도 없이 undefined를 가지는 것을 확인할 수 있었습니다.

타입 가드를 활용해 컴파일러 적극 활용하기

mode 타입을 string 타입을 가져오는 것이 아닌, 유저의 타입을 가지는 변수를 가져오도록 타입을 좁히자고 생각하였습니다.

지금까지는 mode 타입을 searchParams를 이용해서 받아오고 있었기 때문에, 아래와 같이 string 혹은 null 타입을 가지고 있었습니다.

그래서 기존의 코드에서 || 연산자를 활용하여 string 타입을 받아오도록 설정해주고, 타입 가드를 통해 modeowner 혹은 admin 만을 가지도록 설정해주었습니다.

export function loadUserLoginData({ request }: { request: Request }) {
  const params = new URL(request.url);
  const { searchParams } = params;
  const mode: string = searchParams.get('mode') | USER_MODE.owner;

  if (assertUserMode(mode)) {
    return { mode };
  }

  // 해당 모드를 임의로 조작하는 경우 / 이동
  return redirect('/');
}

function assertUserMode(mode: string): mode is UserMode {
  return mode === 'owner' || mode === 'admin';
}

위 코드를 통해 LoginForm 컴포넌트에서 props로 받아오는 mode가 UserMode 중 하나라는 것을 확정해줄 수 있었습니다.

물론! 타입 가드를 통해 받아온 타입이 FORM_PLACEHOLDERS 에 정확히 존재하기 때문에, 컴파일 에러가 발생하지 않았습니다.

후기

타입스크립트를 사용하는 것은 정말 좋은 선택이지만, 잘 사용하는 것이 정말 중요하다는 생각을 가지게 되었어요.

타입 시스템을 가진다는 의미가 단순 string, number 타입을 가진다는 것에 그치지 않고, 특정 타입으로 좁혀서 컴파일러에게 더 명확한 정보를 알려주고 IDE를 적극활용한다는 것이 협업의 관점에서 정말 큰 도움이 될 수 있으며 코드를 더 안전하게 잘 보호해준다고 느끼게 되었습니다.

너무너무 매력적인 언어인 타입스크립트에 대해 앞으로도 천천히 알아가보겠습니다 !

긴 글 읽어주셔서 감사합니다.

profile
Happy Day 😊❣️

0개의 댓글