[인프런 실전 프로젝트로 배우는 타입스크립트] - 최종 프로젝트

Lee Jeong Min·2022년 1월 22일
2

TypeScript

목록 보기
10/18
post-thumbnail

이 글은 실전 프로젝트로 배우는 타입스크립트의 최종 프로젝트를 보고 정리한 글입니다.

JS에 TS를 적용할 때 주의해야 할 점

참고 사이트: https://joshua1988.github.io/ts/etc/convert-js-to-ts.html

  • 기능적인 변경은 절대 하지 않을 것
    → 타입 시스템 적용 후 진행

  • 테스트 커버리지가 낮을 땐 함부로 타입스크립트를 적용하지 않을 것

  • 처음부터 타입을 엄격하게 적용하지 않을 것 (점진적으로 strict 레벨을 증가)

프로젝트에 적용

타입스크립트 프로젝트 환경 구성

  1. npm 초기화
   npm init -y
  1. 타입스크립트 라이브러리 설치
   yarn add -D typescript
  1. 타입스크립트 설정 파일 생성 및 기본 값 추가
{
  "compilerOptions": {
    // 자바스크립트 파일을 그대로 인식해서 사용하겠다.
    "allowJs": true,
    // 타입스크립트 파일을 특정 버전의 js로 변환해주는 것
    "target": "ES5",
    // tsc 이후의 결과물이 어디에 저장될 지 알려주는 것
    "outDir": "./built",
    // Promise 관련 설정
    "moduleResolution": "Node"
  },
  "include": ["./src/**/*"]
}
  1. 자바스크립트 파일을 타입스크립트 파일로 변환

app.jsapp.ts

  1. tsc 명령어로 타입스크립트 컴파일하기

package.json

  "scripts": {
    "build": "tsc"
  },

위와 같은 명령어 추가 후에 npm run build 명령어 입력하면 타입스크립트로 컴파일 됨

점진적인 적용

{
  "compilerOptions": {
    // 자바스크립트 파일을 그대로 인식해서 사용하겠다.
    "allowJs": true,
    ...
}

tsconfig.json의 파일에서 allowJs와 같은 설정을 true로 만들어 두면 app.ts 혹은 app.js를 tsc로 컴파일하였을 때, JS코드로도 빌드가 가능하기 때문에 점진적으로 코드에 타입스크립트 적용을 할 수 있다.

엄격하지 않은 타입 환경에서 프로젝트 돌려보기

  • 프로젝트에 테스트 코드가 있다면 테스트 코드 동작 확인

  • 프로젝트의 js 파일을 모두 ts 파일로 변경하기

  • 타입스크립트 컴파일 에러가 나는 것 위주로만 먼저 에러가 나지 않게 수정

    • 기능을 사소하게 변경하지 않도록 주의!
  • 테스트 코드 성공하는 지 확인

명시적인 any 선언하기

tscofnig.json 에 다음의 설정을 추가한다.

{
  ...
    "noImplicitAny": true
  ...
}

이후 엄청나게 많은 에러를 확인할 수 있다.
모든 타입에 대해 명시적으로 최소한의 any 타입이라도 작성해주어야 함.

app.ts 안에 있는 모든 함수의 파라미터(에러가 발생하는 부분)에 any 타입을 적용하여 실습!

DOM 관련 타입 구체화

// utils
function $(selector: string) {
  return document.querySelector(selector);
}
function getUnixTimestamp(date: Date) {
  return new Date(date).getTime();
}

위의 쿼리셀렉터로 dom 요소를 가져오는 selector의 경우 문자열이 들어오기 때문에 string, Date를 받아 time을 리턴하는 함수의 파라미터는 Date 타입을 명시해준다.

// DOM
const confirmedTotal = $('.confirmed-total') as HTMLSpanElement;
const deathsTotal = $('.deaths') as HTMLParagraphElement;
const recoveredTotal = $('.recovered') as HTMLParagraphElement;
const lastUpdatedTime = $('.last-updated-time') as HTMLParagraphElement;

위와 같이 DOM을 가져오는 경우 오른쪽의 값의 결과의 타입이 무엇인가를 정의해야함. 따라서 타입 단언을 사용해야 한다.
변수에 타입을 정의해주는 것이 아닌 타입 단언을 사용하여 뒤에다가 타입을 명시해준다.

API 함수 타입 구체화

중간에서 api를 호출하는 함수가 있었는데 이 함수의 파라미터는 api docs 에서 확인을 하여 어떤 파라미터를 전달받는 지 확인하여 타입을 명시해준다.

enum CovidStatus {
  Confirmed = ' confirmed',
  Recovered = 'recovered',
  Deaths = 'deaths',
}

function fetchCountryInfo(countryCode: string, status: CovidStatus) {
  // params: confirmed, recovered, deaths
  const url = `https://api.covid19api.com/country/${countryCode}/status/${status}`;
  return axios.get(url);
}

status와 같은 상태의 경우 이넘을 활용하여 타입을 정의하면 가독성 좋게 사용할 수 있다.

모듈화 진행을 위한 환경 구성

라이브러리 설치

npm i -D typescript @babel/core @babel/preset-env @babel/preset-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint prettier eslint-plugin-prettier

devDependencies → 개발용 라이브러리
dependencies → 배포용 라이브러리

바벨, eslint, prettier 설정 → 강의 자료 참고

외부 라이브러리 모듈화

현재 위와 같이 cdn으로 갖고오고 있는데 npm 으로 설치하여 import 하여 app.ts 에 적용시켜야 한다.

axios 라이브러리 설치

npm i axios

chart 라이브러리 설치

npm i chart.js

chart.js가 3버전으로 올라가면서 별도의 타입 라이브러리를 설치하지 않아도 됨

app.ts

import axios from 'axios';
import { Chart } from 'chart.js';

타입 선언 라이브러리가 제공되지 않은 외부 라이브러리 대처 방법

타입스크립트에 외부 라이브러리 타입을 알려주기 위해 아래와 같은 작업을 수행해야 한다.

tsconfig.json

{
  "compilerOptions": {
    ...
    "typesRoots": ["./node_modules/@types", "./types"]
    ...
  }
}

이 설정을 하게 되면 작성한 폴더 위치로 외부 라이브러리들의 타입을 찾도록 만들어 준다.

이후

types/chart.js/index.d.ts

declare module 'chart.js';

위와 같은 경로로 index.d.ts 라는 타입이 선언된 코드를 모아둔 파일을 만들어 명시해주면 에러를 해결할 수 있다.

→ 이후 최종프로젝트 실습 진행하면서 타입 적용

엄격한 타입 적용

tsconfig.json

{
  "compilerOptions": {
    ...
    "strict": true
    ...
  },
}

엄격한 타입 적용을 하기 위해 위와 같은 설정을 적어주어야 한다.

타입 assertion 주의해야할점

// deathsList는 DOM 요소
deathsList!.appendChild(li);

다음과 같은 타입 assertion(non-null assertion) 코드를 작성하면 eslint에서 경고를 하는데 이것이 왜 위험한지 알아보자.

interface Hero {
  name: string;
  skill: string;
}

// const Capt: Hero = {
//   name: 'capt',
//   skill: 'shield',
// };

// const capt: Hero = {};
const capt = {} as Hero;
// capt.name = 'capt';
// capt.skill = 'shield';

이와 같은 코드에서 capt라는 변수는 Hero 인터페이스를 따르는 변수인데 빈 객체로 선언하여 Hero를 타입 단언을 해버리면 타입스크립트 에러가 발생하지 않는다.
이러한 상황에서 name, skill과 같은 내부 프로퍼티들을 적용하지 않아도 에러를 발생시키지 않기 때문에 코드를 위험하게 작성하게 되는 것이다.

따라서 아래의 non-null assertion또한 사용 시 보이지 않는 사각지대에서 에러를 발생시킬 수 있기 때무네 유의해서 사용해야 한다!

const a: string | null;
a!;

DOM 엘리먼트의 타입 단언

DOM 엘리먼트를 가져오는 부분에서 보통 as와 타입을 이용하여 타입 단언을 통해 null 에러를 발생시키지 않도록 한다.

// utils
function $(selector: string) {
  return document.querySelector(selector);
}

const confirmedTotal = $('.confirmed-total') as HTMLSpanElement;
const deathsTotal = $('.deaths') as HTMLParagraphElement;

DOM 유틸 함수의 활용성 높이는 타입 정의

// utils
function $<T extends HTMLElement>(selector: string) {
  const element = document.querySelector(selector);
  return element as T;
}

const confirmedTotal = $<HTMLSpanElement>('.confirmed-total');
const deathsTotal = $<HTMLParagraphElement>('.deaths');

위와 같이 작성하여 타입 단언을 위한 인수를 제네릭으로 전달하여 사용하면 더 활용성을 높인 함수를 만들 수 있다.

마무리

추후 공부 및 참고하면 좋을 자료들

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글