타입스크립트로 뚝~딱 마이그레이션 하기!

·2022년 6월 30일
3

타입스크립트

목록 보기
1/1

타입스크립트를 조각조각 접하다보니 모르는 부분이 많아, 전반적으로 이해하고 싶어 개인 프로젝트에 TS를 적용해보았습니다..!

React, webpack, babel로 구성된 작은 개인 프로젝트에 적용하였고, 과정을 이 포스트에 단계별로 정리했습니다. 100% 타입스크립트 짱짱 적용..! 까지는 아니고, 일단 마이그레이션에 필수적으로 필요한 정도의 내용이 담겨있습니다. 비슷한 작업을 앞둔 분들에게 도움이 되었으면 좋겠네요. ☘️

커밋과 함께 보시려면 이 PR을 확인해주세요. -> https://github.com/hon9g/Draggable/pull/3

🏞 오버뷰

우선 크게 보자면 다음과 같은 과정을 거치게됩니다.

  1. TypeScript 관련 패키지를 추가합니다.
  2. TypeScript 설정 파일을 추가하고, Webpack과 Babel 설정 일부를 수정합니다.
  3. React 구성요소의 인터페이스를 확인하고, 이에 맞춰 적절하게 타입을 지정합니다.

👮🏻 타입스크립트 셋업

1. 타입스크립트 관련 패키지 설치.

npm i -D typescript @types/node @types/react @types/react-dom @babel/preset-typescript

2. 타입스크립트 설정 파일 생성.

tsc cli 커맨드 사용을 위해,

brew install typescript

타입스크립트 설정 파일 tsconfig.json 생성을 위해,

tsc --init

3. 타입스크립트 설정 파일에 프로그램에 포함 할 폴더 위치 추가.

(저의 경우) 프로그램에 포함하고 싶은 파일이 root 기준으로 ./ src/ 에 위치하고 있으므로, tsconfig에 "include": ["./src/"], 을 추가합니다.
(tsconfig.json 가 위치하는 곳이 타입스크립트 Root로 취급 됨.)

🦕 webpack 설정 수정

.tsx, .ts 파일에 대한 rule을 추가해줍니다.
webpack.config.js

module: {
        rules: [
        {
            test: /\.(js|jsx|ts|tsx)$/,
            use: ['babel-loader'],
            exclude: /node_modules/,
        }, 
        ...
        ],
    },

🚜 babel 설정 수정

preset에 타입스크립트를 추가해줍니다.
.babelrc

{
    "presets": [
        ...,
        "@babel/preset-typescript"
    ]
  
}

🐻‍❄️ -> 🐼 기존 코드에 타입 지정하기

  1. 기존에 작성한 자바스크립트 파일에는 대부분 타입이 설정되어 있지 않아, noImplicitAny 컴파일 에러가 다수 발생할테니, 타입스크립트 설정파일에서 해당 옵션을 우선 off 해둡니다. "noImplicitAny": false
  2. 기존 .js, .jsx 파일을 하나씩 .tsx로 변환해가며 작업합니다.
  3. tsx 파일의 jsx 문법을 제대로 컴파일 할 수 있도록 jsx 관련 타입스크립트 설정을 추가합니다. 이 프로젝트에서는 jsx option 값을 "jsx": "preserve"로 주었습니다. (TS: JSX config)
  4. 전 각 컴포넌트의 props 인터페이스를 지정하는 것부터 시작했습니다.
  5. 어느정도 타입을 다 지정했다고 느꼈을 때 즈음 noImplicitAny 설정을 true로 바꾸었습니다. 그러고나면 타입 추론이 되지 않아, 암묵적으로 any 처리된 곳은 컴파일 에러가 발생합니다. 그런 부분들을 찾아 적절한 타입을 지정해줍니다.

React JSX ref & React.useRef 타입 지정

div객체의 optional한 인자인 ref는 LegacyRef<T> 타입의 객체를 받습니다.

type Ref<T> = RefCallback<T> | RefObject<T> | null;
type LegacyRef<T> = string | Ref<T>;

useRef는 전달하는 인자가 있는지 없는지, 또 null 값인지 아닌지에 따라 다른 타입을 리턴값을 돌려줍니다.

  • 인자가 없는 경우, 또는 undefined 를 넘기는 경우 -> void
  • null 값을 넘기는 경우 -> RefObject<T>
  • null 값이 아닌 초기 값을 넘기는 경우 -> MutableRefObject<T>

따라서 LegacyRef<T> 타입에 해당하지 않는 객체를 div ref에 전달해주면 에러가 발생합니다.
다시말해, useRef를 초기화 할 때, 인자를 넘기지 않는다면 void한 함수가 넘어가기 때문에 에러가 발생합니다.
이 에러를 해결하기 위해서는 아래와 같이 null 값으로 초기화를 해주면됩니다.
또한 리턴 타입인 RefObject<T>은 제네릭을 가지고 있으므로 적절한 타입을 넘겨주어야합니다.

const ref = React.useRef<HTMLDivElement>(null)
...
return (
    <div ref={ref} />
)

(삽질 노트 더보기: useRef 타입지정 어떻게하지?)

React EventHandler 타입 지정

React EventHandler에서 꼭 타입을 지정해줘야하는 부분은 EventHandler 함수의 인자 부분입니다.
이 인자들은 @types/react 패키지에서 React namespace 하위에 지정된 event 타입에 해당합니다.
적절한 event 타입을 지정해주면, 해당 함수가 그 이벤트에 대한 EventHandler 인 것으로 추론됩니다.
이 때, 제너릭으로 타겟이되는 element의 타입을 제너릭으로 넘겨주어야 합니다.

  • 핸들러 함수 인자 타입: React.MouseEvent<T> -> 핸들러 합수 타입: MouseEventHandler<T>
  • React.TouchEvent<T> -> TouchEventHandler<T>

(삽질 노트 더보기: React 이벤트 핸들러 타입은 어떻게 지정해줘야하지?)

Window EventHandler 타입 지정

window객체에 직접 이벤트리스너를 추가할 때, 이벤트 핸들러 함수의 타입 또한 지정해야합니다.
마찬가지로 인자들의 타입을 지정하는게 필수적이고, 인자는 event 타입에 해당합니다.
이 인자들은 @types/react 패키지에 정의되어 있습니다. 그런데 React namespace 하위의 이벤트 타입이 아니라 글로벌하게 정의되어있는 이벤트 타입을 사용해야합니다.

☘️☘️☘️☘️☘️☘️☘️☘️🍀 끝 🍀☘️☘️☘️☘️☘️☘️☘️☘️

🔗 reference

profile
프롱트엥드 엥지니어

0개의 댓글