- Published on
코드를 작성하고 실행하기 ~ 타입스크립트로 마이그레이션하기 - Item 57 ~ 59
코드를 작성하고 실행하기 ~ 타입스크립트로 마이그레이션하기 - Item 57 ~ 59
Item 57) 소스맵을 사용하여 타입스크립트 디버깅하기
타입스크립트 코드는 독립적으로 실행할 수 없고 엄밀히 따지면 타입스크립트 컴파일러가 생성한 자바스크립트 코드를 실행하는 것
- 컴파일러뿐 아니라 압축기나 전처리기처럼 기존 코들르 다른 형태의 코드로 변환하는 도구들도 모두 해당됨
디버거는 런타임에 동작하며, 현재 동작하는 코드가 어떤 과정을 거쳐 만들어진 것인지 알지 못하기때문에 디버깅 문제를 해결하기 위해 브라우저 제조사들은 서로 협력하여 소스맵(
source map
)이라는 해결책을 내놓음대부분의 브라우저와 많은 IDE는 소스맵을 지원함
오래된 브라우저에서
async
와await
를 지원하기 위해, 타입스크립트는 이벤트 핸들러를 상태 머신으로 재작성하하므로 원본 코드와 동일하게 동작하지만 코드의 형태는 매우 다른 모습을 띠게 됨이와 같이 변환된 코드를 읽기위해서는 소스맵이 필요한데, 이 설정은
tsconfig.json
에서sourceMap
옵션으로 제어할 수 있음{ "compilerOptions": { "sourceMap": true } }
- 해당 옵션을 활성화한 후 컴파일을 실행하면 각
.ts
파일에 대해서.js
와.js.map
두개의 파일을 생성함 - 소스맵이
.js
파일과 함께 있으면 브라우저의 디버거에서 새로운index.ts
파일이 나타나고 원하는 대로 브레이크 포인트를 설정해서 변수를 확인할 수 있음
- 해당 옵션을 활성화한 후 컴파일을 실행하면 각
소스맵에 대해 알아야할 사항
- 타입스크립트와 함께 번들러나 압축기를 사용하고 있다면, 각자의 소스맵을 생성하게 됨
- 이상적인 디버거 환경을 위해 원본 타입스크립트 소스로 매핑되도록해야함
- 상용 환경(
production
)에 소스맵이 유출되고 있는지 꼭 확인해야 함- 소스맵에 원본 코드 인라인 복사본이 포함되어 있다면 공개해서는 안 될 내용이 들어 있을 수 있음
- 타입 체커가 코드를 실행하기 전에 많은 오류는 잡을 수 있지만, 디버거를 대체할 수 없으므로 제대로 된 타입스크립트 디버깅 환경을 구축하는 것이 좋음
Item58) 모던 자바스크립트로 작성하기
- 타입스크립트는 타입 체크 기능 외에, 자바스크립트로 컴파일하는 기능도 가지고 있음
- 즉, 타입스크립트 컴파일러를 자바스크립트 트랜스 파일러로 사용할 수 있는데 타입스크립트는 자바스크립트의 상위 집합이므로 최신 버전의 자바스크립트 코드를 옛날 버전의 자바스크립트로 변환할 수 있음
- 따라서 마이그레이션 작업 시, 자바스크립트 프로젝트를 타입스크립트로 바로 바꾸기보다는 옛날의 자바스크립트를 모던 자바스크립트로 작성한 후 도입하는 것을 권장
ECMAScript 모듈 사용하기
ES2015
이전에는 코드를 개별 모듈로 분할하는 표준 방법이 없지만, 지금은 개별 모듈로 분할하는 방법이 많아짐- 여러 개의
<script>
태그를 사용하기, 직접 갖다 붙이기,Makefile
기법,require
구문,define
콜백, 타입스크립트의 모듈시스템 등등
- 여러 개의
ES2015
부터는import
와export
키워드를 사용하는 것이 표준이 되어 있음- 마이그레이션 대상인 자바스크립트 코드가 단일 파일이거나 비표준 모듈 시스템이라면 ES 모듈로 전환하는 것이 좋음
- ES 모듈 시스템을 사용하기 위해 프로젝트에 따라
webpack
이나ts-node
같은 도구가 필요한 경우도 있음
- ES 모듈 시스템을 사용하기 위해 프로젝트에 따라
프로토타입 대신 클래스 사용하기
- 과거에는 프로토타입 기반의 객체 모델을 사용했으나 견고하게 설계된 클래스 기반 모델을 선호했기때문에 결국 ES2015에 class 키워드를 사용하는 클래스 기반 모델이 도입됨
- 프로토타입으로 구현한 객체보다 클래스로 구현한 객체가 문법이 더 간결하고 직관적이므로 클래스 문법에 익숙하지 않더라도, 타입스크립트 언어 서비스를 활용하면 클래스를 간단히 작성할 수 있음
var 대신 let/const 사용하기
- 자바스크립트
var
키워드의 스코프 규칙에 문제가 있으므로let
과const
를 사용해 스코프 문제를 피할 수 있음 var
키워드 대신let
,const
를 사용해 제대로된 블록 스코프 규칙을 가지고, 일반적으로 인지 가능한 방식으로 구현할 수 있음
for(;;) 대신 for-of 또는 배열 메서드 사용
- 과거에는 자바스크립트에서 배열을 순회할 때 C 스타일의
for
루프를 사용했지만 모던 자바스크립트에서는for-of
루프를 사용하여 실수를 줄일 수 있으며, 인덱스 변수가 필요한 경우엔forEach
메서드를 사용
함수 표현식보다 화살표 함수 사용하기
this
키워드는 일반적인 변수들과는 다른 스코프 규칙을 가지므로, 화살표 함수를 사용해 상위 스코프의this
를 참조할 수 있게 의미를 통일할 수 있으며 인라인 또는 콜백함수에서는 화살표 함수가 더 직관적인 장점이 있음- 컴파일러 옵션에
noImplicitThis
(또는 strict)를 설정하면this
바인딩 관련된 오류를 표시해 주므로 설정하는 것이 좋음
단축 객체 표현과 구조 분해 할당 사용
const x = 1,
y = 2,
z = 3
const pt = {
x: x,
y: y,
z: z,
}
변수와 객체 속성의 이름이 같다는 전제하에 아래와 같이 코드를 작성할 수 있음
const x = 1, y = 2, z = 3 const pt = { x, y, z }
앞의 두 예제 중 후자의 코드가 더 간결하고 중복된 이름을 생략하기 때문에 가독성이 좋고 실수가 적음
화살표 함수 내에서 객체를 반환할 때는 소괄호로 감싸야 함
;['A', 'B', 'C'].map((char, idx) => ({ char, idx })) // [ { char: 'A', idx: 0 }, { char: 'B', idx: 1 }, { char: 'C', idx: 2 } ]
객체의 속성 중 함수를 축약해서 표현하는 방법은 다음과 같음
const obj = { onClickLong: function (e) { // ... }, onClickCompact(e) { // ... }, }
객체 구조 분해를 사용해 아래와 같이 작성할 수 있음
// const props = obj.props // const a = props.a // const b = props.b // 줄여서 작성 // const { props } = obj // const { a, b } = props // 극단적인 예시 const { props: { a, b }, } = obj
구조 분해 문법을 통해 기본값 설정
const { a = 'default' } = obj.props
배열에서도 구조 분해 문법을 사용해 튜플처럼 사용할 수 있음
const point = [1, 2, 3] const [x, y, z] = point const [, a, b] = point // 첫번째 요소 무시
함수 매개변수에서도 구조 분해 문법을 사용할 수 있음
const points = [ [1, 2, 3], [4, 5, 6], ] points.forEach(([x, y, z]) => console.log(x + y + z)) // 6, 15을 출력함
함수 매개변수 기본값 사용
자바스크립트에서 함수의 모든 매개변수는 선택적이며, 매개변수를 지정하지 않으면
undefined
로 간주됨모던 자바스크립트에서는 매개변수에 기본값을 직접 지정할 수 있음
function parseNum(str, base = 10) { return parseInt(str, base) }
- 매개변수에 기본값을 지정하면 base가 선택적 매개변수라는 것을 명확히 나타낼 수 있으며 기본값을 기반으로 타입 추론이 가능하므로 타입스크립트로 마이그레이션할 때 매개변수에 타입 구문을 쓰지 않아도 됨(⭐)
저수준 프로미스나 콜백 대신 async/await 사용하기
async와 await를 사용하면 코드가 간결해져서 버그나 실수를 방지할 수 있으며 비동기 코드에 타입 정보가 전달되어 타입 추론을 가능하게 함
async function getJSON(url: string) { const response = await fetch(url) return response.json() }
타입스크립트에 use strict 넣지 않기
ES5
에서는 버그가 될 수 있는 코드 패턴에 오류를 표시해주는 엄격 모드가 도입됐지만 타입스크립트에서 수행되는 안전성 검사가 엄격 모드보다 훨씬 더 엄격한 체크를 하므로 타입스크립트에서서use strict
는 무의미 함- 타입스크립트 코드에
use strict
를 쓰지 않고, 대신alwaysStrict
설정을 사용해야 함
- 타입스크립트 코드에
자바스크립트 표준 단체
TC39
는 매년 새로운 기능을 발표하고, 타입스크립트 개발팀은 자바스크립트 표준화 4단계 중 3단계 이상의 기능들을 타입스크립트 내에 구현하고 있으므로 표준화 완성 여부에 상관없이 표준화 3단계 이상의 기능들은 타입스크립트 내에서 사용할 수 있음
Item59) 타입스크립트 도입 전에 @ts-check와 JSDoc으로 시험해 보기
@ts-check
지시자를 사용하면 타입스크립트 전환시에 어떤 문제가 발생하는지 미리 시험해 볼 수 있음- 타입 체커가 파일을 분석하고, 발견된 오류를 보고하도록 지시하지만 매우 느슨한 수준의 타입 체커이므로 컴파일러 설정 옵션에서
noImplicitAny
설정을 해제한 것보다 헐거운 체크를 함
- 타입 체커가 파일을 분석하고, 발견된 오류를 보고하도록 지시하지만 매우 느슨한 수준의 타입 체커이므로 컴파일러 설정 옵션에서
// @ts-check
const person = { first: 'Grace', last: 'Hopper' }
2 * person.first
// 산술 연산 오른쪽은 'any', 'number', 'bigint'
// 또는 열거형 형식이어야 합니다.
person.firs
t의 타입은string
으로 추론되었고,2 * person.first
는 타입 불일치 오류가 되었음- 지시자 덕분에 자바스크립트임에도 불구하고 타입 체크가 동작함
선언되지 않은 전역 변수
변수를 선언할 때 보통
let
이나const
를 사용하지만 어딘가에 숨어있는 변수라면, 변수를 제대로 인식할 수 있게 별도로 타입 선언 파일을 만들어야 함// @ts-check // console.log(user.firstName); // 'user' 이름을 찾을 수 없습니다.
user
선언을 위해types.d.ts
파일을 만듦interface UserData { firstName: string lastName: string } declare let user: UserData
타입 선언 파일을 만들면 오류가 해결됨
선언 파일을 찾지 못하는 경우는 트리플 슬래시 참조를 사용하여 명시적으로 임포트 할 수 있음
// @ts-check /// <reference path="./types.d.ts" /> console.log(user.firstName) // 정상
DOM 문제
웹브라우저에서 동작하는 코드라면, 타입스크립트는
DOM
엘리먼트 관련된 부분에 수많은 오류를 표시하게 됨// @ts-check const ageEl = document.getElementById('age') ageEl.value = '12' // 'HTMLElement' 유형에 'value' 속성이 없습니다.
HTMLInputElement
타입에는value
속성이 있지만,document.getElementById
는 더 상위 개념인HTMLElement
타입을 반환하는 것이 오류의 원인이므로 확실한 엘리먼트에 대해서 타입 단언문으로 명시해야 함// @ts-check const ageEl = /** @type {HTMLInputElemnt} */ document.getElementById('age') ageEl.value = '12' // 정상
JSDoc
의@type
구문을 사용할 때는 타입을 감싸는 중괄호가 필요함
부정확한 JSDoc
- 자바스크립트 환경에서도
@ts-check
지시자와JSDoc
주석이라면 타입스크립트와 비슷한 경험을 작업이 가능함 - 특별한 작업이 필요없으므로 점진적 마이그레이션 과정 중에는 유용하지만, 장기간 사용하지 않는 것이 좋음
- 주석이 코드 분량을 늘려서 로지글 해석하는 데 방해가 될 수 있음
- 타입스크립트는
ts
파일에서 가장 잘 동작하며, 마이그레이션의 궁극적인 목표는 자바스크립트에JSDoc
주석이 있는 형태가 아니라 모든 코드가 타입스크립트 기반으로 전환되는 것- 다만, 이미
JSDoc
주석으로 타입 정보가 많이 담겨 있는 프로젝트라면@ts-check
지시자만 간단히 추가해서 기존 코드에 타입 체크를 실해하고 초기 오류를 빨리 잡아낼 수 있다는 점은 기억해야 함
- 다만, 이미
Referenced
- 댄 밴더캄, 『이펙티브 타입스크립트』, 인사이트(2021.11.4), 281 ~ 306p