[이펙티브 타입스크립트] 8장 타입스크립트로 마이그레이션하기

JIY00N·2023년 9월 13일
0

TypeScript

목록 보기
9/9
post-thumbnail
post-custom-banner

8장 타입스크립트로 마이그레이션하기

이번 장은..

자바스크립트의 프로젝트를 꾸준하게 타입스크립트로 마이그레이션하는 방법을 배운다.

아이템 58 모던 자바스크립트로 작성하기


  • 마이그레이션을 어디서부터 시작해야할지 막막하다면, 옛날 버전의 JS 코드를 최신 버전의 JS 코드로 바꾸는 작업부터 시작하자.
    → TS는 JS의 상위집합이기 때문에, 코드를 최신으로 바꾸다면 TS의 일부를 익힐 수 있다.
  • 모던 자바스크립트의 주요 기능 몇 가지를 살펴보자
  • 그 중에서도 타입스크립트를 도입할 때 가장 중요한 기능은 ECMAScript 모듈ES2015 클래스 이다.

ECMAScript 모듈 사용하기

  • ES2015 이전에는 코드를 개별 모듈로 분할하는 표준 방법이 없었지만, 지금은 개별 모듈로 분할하는 방법이 많아졌다.
  • ES2015부터는 import , export를 사용하는 ECMAScript 모듈(ES모듈)이 표준이다.
  • ES모듈 시스템TS에서도 잘 동작하며, 모듈 단위로 전환할 수 있게 해 주기 때문에 점진적 마이그레이션이 원활해진다.

프로토타입 대신 클래스 사용하기

  • 코드에서 단순한 객체를 다룰 때 프로토타입을 사용했다면, 클래스로 바꾸자
  • 프로토타입보다 클래스로 구현한 것이 문법이 간결하고 직관적이다.
  • 클래스 문법에 익숙하지 않더라도 TS 언어 서비스를 활용하면 클래스를 간단히 작성 할 수 있다→ 함수를 ES2015 클래스로 변환 사용

var 대신 let/const 사용하기

  • var 키워드의 스코프 규칙은 let, const 처럼 제대로 된 스코프 규칙이 아니여서, 일반적으로 기대하는 방식으로 동작하지 않는다.
  • 호이스팅도 발생한다.
  • 따라서, var 대신 let/const를 사용하자.

for(;;) 대신 for-of 또는 배열 메서드 사용하기

  • for-of 는 코드가 짧고 인덱스 변수를 사용하지 않기 때문에 실수를 줄일 수 있다.
  • 인덱스가 필요한 경우엔 forEach 메서드 사용
  • for-in 은 몇 가지 문제가 있기 때문에 사용하지 말자 (타입이 안정하지 않음)
// for(;;)
for (var i = 0; i < array.length; i++) {
  const el = array[i]
  // ...
}

// for of
for (const el of array) {
  // ...
}

// forEach
array.forEach((el, i) => {
  // ...
})

함수 표현식보다 화살표 함수 사용하기

  • 인라인(또는 콜백)에서는 일반 함수보다 화살표 함수가 더 직관적이며 코드도 간결해지기 때문에 가급적 화살표 함수를 하자.
  • 컴파일러 옵션 noImplicitThis 또는 strict 를 설정하면, this 바인딩 관련된 오류를 표시해준다.

단축 객체 표현과 구조 분해 할당 사용하기

  • 단축 객체 표현과 구조 분해 할당을 사용하면 문법이 간결해지고 변수를 사용할 때 실수를 줄일 수 있기 때문에 적극적으로 사용하자.
  • 단축 객체 표현
const x = 1,
  y = 2,
  z = 3
const pt = {
  x: x,
  y: y,
  z: z,
}

// 단축 객체 표현
const pt = { x, y, z }
  • 구조 분해 할당
const props = obj.props
const a = props.a
const b = props.b

// 구조 분해 할당
const { props } = obj
const { a, b } = props

함수 매개변수 기본값 사용하기

  • 모던 자바스크립트에서는 매개변수에 기본값을 직접 지정할 수 있다.
  • 코드가 간결해지고 base 가 선택적 매개변수라는 것을 명확히 나타내는 효과도 줌.
function parseNum(str, base = 10) {
  return parseInt(str, base)
}

저수준 프로미스나 콜백 대신 async/await 사용하기

  • asnyc/await 를 사용하면 코드가 간결해져서 버그나 실수를 방지할 수 있고, 비동기 코드에 타입 정보가 전달되어 타입 추론을 가능하게 함.
function getJSON(url: string) {
  return fetch(url).then(response => response.json())
}
function getJSONCallback(url: string, cb: (result: unknown) => void) {
  // ...
}

// async/await 사용
async function getJSON(url: string) {
  const response = await fetch(url)
  return response.json()
}

연관 배열에 객체 대신 Map과 Set 사용하기

  • 객체의 인덱스 시그니처보단 Map과 Set을 사용하자
  • 특정 식별자에 대해 오류가 발생하기 때문 (constructor)

타입스크립트에 use strict 넣지 않기

  • ES5에 도입된 strict mode보다 TS에서 수행되는 안전성 검사가 더 엄격해서 TS의 코드에서 use strict 는 무의미하다.
  • 대신, alwaysStrict 설정을 사용해야 한다.

🎯 요약

타입스크립트 개발 환경은 모던 자바스크립트도 실행할 수 있으므로 최신 기능을 활용하자.

아이템 59 타입스크립트 도입 전에 @ts-check와 JSDoc으로 시험해 보기


@ts-check

  • 타입 체커가 파일을 분석하고, 발견된 오류를 보고하도록 지시한다.
  • 매우 느슨하게 타입 체크를 수행한다.
  • 타입 불일치나 함수의 매개변수 개수 불일치 같은 간단한 오류를 보여준다.
  • 사용법
// @ts-check
const person = {first: 'Grace', last: 'Hopper'};
2*person.first 
// ~~~~~~~~~~~~ 산술 연산자 오른쪽은 'any', 'number', 'bigint'
//              또는 열거형 형식이어야 합니다.
  • 대표적 오류들
  1. 선언되지 않은 전역 변수
    types.d.ts 파일을 이용하여 변수 인식시키거나 **트리플 슬래시**로 명시적인 임포트
  2. 알 수 없는 라이브러리
    → 제이쿼리를 예시로 제이쿼리 타입 선언을 설치하는 방법으로 타입 체킹 가능
  3. DOM 문제
    → 타입스크립트가 아니어서 타입단언은 못하지만, JSDoc을 통한 타입 단언 대체
  4. 부정확한 JSDoc
    → JSDoc 자동 생성 기능은 타입 정보를 빠르게 추가할 수 있기 때문에 유용하지만, 잘 동작하지 않은 경우가 있어 유의해서 사용해야 한다.

🎯 요약

마이그레이션의 궁극적인 목표는 모든 코드를 TS 기반으로 전환되는 것이므로 JSDoc과 @ts-check 같은 중간단계를 너무 공들일 필요는 없다.

아이템 60 allowJs로 타입스크립트와 자바스크립트 같이 사용하기


  • allowJs 컴파일러 옵션은 TS와 JS가 공존하게 해준다.
  • TS 파일과 JS 파일을 서로 임포트할 수 있게 해주고, 기존 빌드 과정에 TS 컴파일러를 추가하기 위해 이 옵션이 필요하다.
  • 모듈단위에서 테스트하는 과정에서도 필요하다.
  • 적용하는 방법
  1. 번들러, 플러그인 방식으로 통합되어 있다면 간단히 적용가능 npm install —save-dev tsify
  1. 프레임워크 없이 빌드 체인 직접 구성한 경우 outDir 옵션을 사용한다.

🎯 요약

TS로 대규모 마이그레이션 함과 동시에 빌드와 테스트가 동작하게 하는 것이 힘들지만 제대로 마이그레이션을 하기 위해서는 반드시 필요하다.

아이템 61 의존성 관계에 따라 모듈 단위로 전환하기


  • 점진적 마이그레이션을 할 때는 모듈 단위로 해결하는 것이 이상적이다.
  • 다른 모듈에 의존하지 않는 최하단 모듈 → 의존성의 최상단에 있는 모듈 순으로 해결한다. ( madge 라는 도구를 이용하면 의존성 관계를 시각화 할 수 있다.)
  1. 서드파티 라이브러리 타입 정보를 가장 먼저 해결
  2. 외부 API, API에 대한 사양을 기반으로 타입 정보를 생성
  3. 마이그레이션을 할 때는, 타입 정보 추가만하고 리팩토링을 하면 안된다.
    → 코드 개선이 아니라 타입스크립트로 전환하는 것임을 명심해야 함.
  • 아이템 59에서 다루지 못한 또 다른 오류들
  1. 선언되지 않은 클래스 멤버

js와 달리 ts 클래스에선 클래스 멤버 변수를 선언해주어야하는데 빠른 수정 기능을 이용해서 추가하며, 정확하게 추론되지 않은 타입들을 수정해주어야한다.

  1. 타입이 바뀌는 값

당장의 마이그레이션이 중요하면 타입 단언문을 사용하고 이후 문제를 제대로 해결해야 한다. 또는 JSDoc과 같은 타입 정보를 추가한 상태이면 이를 이용한 빠른 수정이 가능하다.

최종적으로 테스트 코드는 의존성 관계도의 최상단에 위치하기 때문에 마이그레이션의 마지막 단계로 수정하면 된다.

🎯 요약

마이그레이션을 점진적으로 모듈 단위로 해결하고, 리팩토링은 추후의 문제이다.

아이템 62 마이그레이션의 완성을 위해 noImplicitAny 설정하기


  • 프로젝트 전체를 .ts 로 전환했다면, noImplicitAny 를 설정해야 한다.
  • 처음에는 noImplicitAny 로컬에만 설정하고 작업하는 것이 좋다.
    → 원격에서는 설정에 변화가 없기 때문에 빌드가 실패하지 않기 때문
  • 가장 강력한 설정은 "strict": true 이다.
  • 타입 체크의 강도는 팀 내의 모든 사람이 TS에 익숙해진 다음에 조금씩 높이자.

🎯 요약

프로젝트 전체를 .ts 로 전환했다면, 점진적으로 noImplicitAny 를 설정하자.

profile
블로그 이전 했습니다. https://yoon-log.vercel.app/
post-custom-banner

0개의 댓글