타입스크립트를 사용하는 3가지 방법
타입스크립트를 사용하는 방법
런타임
- 타입 가드
IDE
- 타입체커(tsserver)
컴파일
- 타입스크립트 컴파일러(tsc)
- Babel vs TypeScript
마치며
타입스크립트를 사용하는 방법
요즘 개발하는 대부분의 프로젝트는 타입스크립트로 진행된다. 타입스크립트의 강력한 장점은 자동 완성으로 개발에 편의를 더해주고, 런타임 오류를 잡아내 유저가 겪을 문제를 사전에 방지할 수 있도록 한다는 점이다. 타입스크립트를 처음 접할 때 자바스크립트에 타입이 추가된 언어라고 배우는데, 그렇다면 타입스크립트는 어떻게 타입을 이용해 자동 완성을 가능하게 하고, 에러를 잡아내도록 작동하는 것일까?
예전에 원티드 챌린지를 들으며 런타임, IDE, 컴파일 시점으로 나누어 타입스크립트를 사용하는 방법을 학습했다. 최근에 <이펙티브 타입스크립트>를 읽으면서 일전의 원티드 강의 내용을 바탕으로 타입스크립트 사용 방법을 정리해보고 싶어졌다. 타입스크립트는 개발하면서 3가지 단계에 걸쳐 이용된다. 런타임 시점, IDE 및 에디터를 사용하는 시점, 컴파일 시점 3가지로 나눌 수 있다. 이번 포스트에서는 해당 내용을 살펴보았다.
런타임
타입스크립트는 자바스크립트 런타임 동작을 모델링하는 타입 시스템을 가지고 있기 때문에, 런타임 오류가 발생하는 코드를 찾아내려고 한다. 그렇지만 타입스크립트를 사용해도 런타임에 오류가 발생할 수 있다. 런타임에 타입이 결정되는 경우가 많기 때문이다. 타입 체커를 통과하면서, 런타임 오류를 발생시키는 코드가 존재하기도 한다.
타입 가드
타입스크립트의 타입이 좁혀지는 방식을 이해하면 타입 추론의 개념을 잡을 수 있고, 오류의 원인을 발견할 수있으며, 타입 체커를 더 효율적으로 이용할 수 있다. 타입을 좁힐 때는 타입가드가 도움이 된다.
타입스크립트는 타입스크립트 컴파일러를 거쳐 js 파일과 d.ts 파일로 분리된다. d는 declare의 이니셜이다. d.ts 파일에는 선언부만 있고 구현부가 제외된다. 타입스크립트를 컴파일하면 타입 구문이 날아가기 때문에 타입 가드가 필요하다. js 파일에 // @ts-check를 추가하면 편집기에 오류가 표시된다.
typeof, instanceof, in 연산자를 사용해 타입 가드를 할 수 있다.
function example(value: string | number) {
if (typeof value === 'string') {
console.log(value);
}
if (typeof value === 'number') {
console.log(value);
}
}
그렇다면 타입스크립트는 왜 런타임에 타입이 결정되도록 허술하게 타입을 허용할까? 이는 타입스크립트의 건전성(Soundness) 때문이다. 타입스크립트는 특정 불건전(Unsoundness)한 상황을 허용한다. 불건전한 상황의 예시로는 any, as, ! 등이 있다.
건전성이란, 컴파일 타임에 알기 어려운 특정 타입을 안전하게 허용하는 것을 의미한다.
- 컴파일러가 런타임 시점 값의 타입을 보장할 수 있다는 개념
- 모든 JavaScript 코드를 지원하기 위함
- 건전한 언어는 데이터가 타입이 말하는 것과 일치하는지 확인하기 위해 때때로 런타임 검사 사용
- TypeScript는 변환된 코드가 런타임에 영향을 미치지 않는 것이 목표
IDE
타입 체커(tsserver)
tsserver는 언어 서비스를 제공한다. 언어 서비스에는 자동 완성, 명세, 검사, 검색, 오류 메시지 표시 등이 포함된다. tsserver는 실시간으로 코드 분석 정보를 제공한다. IDE를 이용해 개발하는 과정에서 코드를 지속적으로 분석하고 개발자에게 피드백을 준다. 특히 자동 완성은 타입스크립트를 사용하는 장점 중 하나이며 개발자 경험을 높여준다.
타입스크립트가 타입을 추론할 수 있다면 명시적인 타입 구문은 불필요하다. 비생산적이고, 개발자가 로직에 집중하는 것을 방해한다. 그럴 경우에는 타입 구문을 굳이 작성하지 않는 것이 좋다. eslint의 no-inferrable-types 룰을 사용하면 작성한 타입 구문이 정말 필요한지 확인할 수 있다.
타입스크립트를 이용하면 타입스크립트를 사용하면 발생하지 않았을 오류가 계속 생긴다. 타입스크립트를 처음 배웠을 당시에는 개발하면서 번거로워지는 부분이기도 했다. 하지만 '이 오류들이 런타임에 발생했다면?' 하는 아찔한 생각을 할 때마다 개발자가 고생할수록 이용자가 편리하다는 말을 떠올린다. 한편 타입스크립트의 강력함을 느낀 것이 자동 완성 기능이었다. 타입을 잘 잡아놓으면 코드를 잘못 입력하는 실수도 줄여주고, 자동 완성이 편리해진다.
컴파일
타입스크립트 컴파일러(tsc)
인터프리터 언어인 자바스크립트와 달리, 타입스크립트는 컴파일 언어다. 자바스크립트는 동적 타입 언어로 런타임에 타입이 결정된다. 타입스크립트는 정적 타입 언어이기 때문에 변수를 생성할 때 타입을 명시해주어야 한다.
타입스크립트 컴파일러는 빌드시에 사용되며 두 가지 역할을 수행한다.
1. 최신 타입스크립트/자바스크립트를 브라우저에서 동작할 수 있도록 구버전의 자바스크립트로 트랜스파일
2. 코드의 타입 오류를 체크
이 역할은 서로 독립적이기 때문에, 타입 오류가 있더라도 컴파일은 가능하다. 타입은 런타임에 사용할 수 없다. 이를 보안하기 위해 타입 가드 등의 방법을 사용한다. 타입스크립트 코드를 원하는 자바스크립트 코드로 컴파일하려면 구성 파일(tsconfig.json)이 필요하다.
라이브러리를 사용할 때 이름에 @types가 붙은 것을 발견할 수 있는데, 이는 기존 라이브러리가 자바스크립트로 되어있었기 때문에 DefinitelyTyped라 불리는 '타입을 정의한 모음'을 추가한 것이다. @types 라이브러리에는 타입 정보만 포함되어 있고 구현체는 포함되지 않는다. 비교적 최근에 만들어진 라이브러리는 자체적으로 타입스크립트를 지원하고 있기 때문에 모든 라이브러리가 @types를 필요로 하는 것은 아니다.
Babel vs TypeScript
바벨은 최신 자바스크립트 코드를 이전 브라우저 및 환경에서 호환되는 JavaScript 버전으로 변환해주는 자바스크립트 컴파일러다. 타입스크립트 코드를 동등한 자바스크립트 코드로 변환하는 트랜스파일러 역할을 한다. 트랜스파일은 번역(translate)과 컴파일(compile)의 합성어다. 컴파일이 특정 언어를 다른 언어로 변환하는 과정이라면, 트랜스파일은 특정 언어를 동일한 동작을 하는 소스코드(다른 버전 또는 다른 언어 등)로 변환하는 과정이다.
바벨에서는 타입을 체크하지 않는다. 아래의 예시 코드는 바벨에서는 오류나 경고 없이 컴파일되지만, 타입스크립트에서는 컴파일되지 않는다. 타입 체크가 되지 않지만 컴파일을 해야하는 빠른 작업에는 바벨이 좋은 선택이 될 수도 있다. 바벨은 타입스크립트를 지원하기 때문에 기존의 빌드 파이프라인으로 작업할 수 있고, 타입 체크를 하지 않기 때문에 자바스크립트 출력 시간이 더 빨라질 수 있다.
const str: string = 1;
@babel/preset-typescript를 사용해 자바스크립트 파일을 생성하고, 타입스크립트를 이용해 타입 체크와 d.ts 파일을 생성하는 방식도 있다.
마치며
타입스크립트 사용 방법은 각 시점별로 완전히 동떨어진 것이 아니라 서로 연관되어 있다. 런타임 단계의 오류를 줄이기 위해 타입 가드를 이용하고 컴파일 단계에서 tsc가 엄격한 타입 체크를 할 수 있도록 돕는다. IDE에서는 tsserver가 실시간으로 코드를 분석하며 자동 완성, 오류 메시지를 통해 개발자에게 피드백을 제공한다. 컴파일 단계에서는 tsc가 타입 오류를 체크하고 자바스크립트 코드로 변환한다.
각 과정 별로 타입스크립트를 사용하면서 일어나는 일을 학습했다. 타입스크립트는 동적 타입 언어인 자바스크립트에 타입 개념을 도입한 언어다. 정적 타입 언어인 타입스크립트가 어떻게 동작하는지 구체적으로 알아보는 계기가 되었다. 자바스크립트 코드에 타입을 덧붙이면서 세 단계에 걸쳐 서로 맞물리는, 개발자에게 높은 개발경험을 제공하는 타입스크립트의 생태계를 알아보았다.
참고 레퍼런스
TypeScript 공식문서 Soundness 샘플 코드
https://www.typescriptlang.org/docs/handbook/2/narrowing.html
https://github.com/DefinitelyTyped/DefinitelyTyped
https://www.typescriptlang.org/docs/handbook/babel-with-typescript.html