TL;DR


 국내 여러 교육, 채용 플랫폼에서 진행하는 토크 콘서트에서도 빠지지 않는 주제가 타입스크립트입니다. npm 상위 랭킹에 있는 라이브러리들은 대부분 타입스크립트를 지원하고 있고요. 그만큼 타입스크립트의 학습 및 사용 근거가 명확하기 때문에 많은 개발자와 기업에서도 사용하고 있고 튼튼한 생태계를 갖추고 있습니다. 공식문서를 살펴보며 간략한 타입스크립트 소개와 타입스크립트가 개발자와 사용자에게 가져오는 이점들에 대해 알아보겠습니다.

 타입스크립트를 설명하는 글을 볼 때 흔히 "타입스크립트는 자바스크립트의 슈퍼셋이다"라는 표현을 많이 보셨을 겁니다. 공식문서에서도 언급하듯 타입스크립트는 "JavaScript and More"입니다, 즉 코드 작성 단계에서 에러를 캐치할 수 있는, 자바스크립트에 추가적인 문법을 얹힌 상위 언어입니다.


typescriptlang.org

1.1 typescript, https://www.typescriptlang.org/


 그렇다면 왜 자바스크립트에 타입을 지정할 수 있는 문법을 추가했고 타입스크립트는 어떻게 코드 작성단계에서 에러를 잡을까요? 우선 자바스크립트는 동적 타입 언어입니다. 동적 타입 언어는 변수의 타입을 선언하지 않고도 동적으로 값을 할당하고 사용할 수 있는 특징을 가지고 있습니다. 이는 자바스크립트를 간결하고 유연한 언어로 만들어주지만 동적 타입 언어는 몇 가지 단점 아닌 단점들이 있습니다.


런타임 오류

 자바스크립트에서는 변수에 어떤 종류의 값이든 할당할 수 있습니다. 변수에 어떤 종류의 값이든 할당할 수 있다는 것은 코드 작성 단계를 지나서야 코드가 실행되는 런타임에 타입 관련 오류를 파악할 수 있다는 것입니다. 변수의 타입은 런타임에 결정되기 때문이죠. 예를 들어, 잘못된 변수 타입을 사용하거나 존재하지 않는 프로퍼티에 접근하려 할 때 오류가 발생할 수 있어요.

1function doubleQuantity(a) {
2  if (a > 0) {
3    return a * 2;
4  }
5}

함수에 매개변수 a를 전달하면 a의 두 배 값을 반환하는 함수입니다. 이 함수는 당연히 매개변수 a가 number 타입의 값을 전달받는다고 실행된다는 가정하에 작성됐겠죠. 자바스크립트는 매개변수가 어떤 타입의 값인지 제약을 걸지 않습니다. 하지만 함수의 사용법은 작성자만 숙지하고 다른 개발자들은 사용법을 추론만 할 뿐 최종 사용자는 모를 수도 있습니다. 문자열과 같은 다른 타입의 값으로 함수가 호출되면 사용법과 의도와 다른 결과를 반환하겠죠(함수 실행 결과가 NaN의 값을 의도한 게 아니라면).

1doubleQuantity(15); // 30
2doubleQuantity('15'); // 30
3doubleQuantity('15'); // NaN

타입 명시를 하지 않고 변수에 어떤 종류의 값이든 할당할 수 있는 자바스크립트에서 변수의 타입은 런타임에 결정됩니다. 따라서 함수가 호출되고 나서야 타입 오류를 파악할 수 있어요.


유지보수의 상대적 어려움

 동적 타입 언어에서는 변수의 타입이 명시되지 않기 때문에 코드의 예상되는 데이터 형식을 개발자의 예상과 경험에 의존해야 합니다. 따라서 코드가 개발자의 의도와 다르게 실행될 수 있고 그 책임에 대한 비용은 코드 작성자 또는 팀원과 같은 다른 개발자들이 부담해야 됩니다. 추후 유지보수할 때 생기는 비용은 덤이고요. 코드 베이스가 복잡해지고 상호작용하는 모듈이 많아질수록 서로 공유하는 값들을 명확하게 정의하지 않으면 오류가 발생했을 때 디버깅 빈도와 디버깅에 드는 시간이 오래 걸릴 수 있습니다.


타입스크립트

 타입스크립트는 자바스크립트에서 넘어오면서 동적 타입 언어의 단점을 보완하기 위해 타입 시스템을 도입하게 됩니다. 타입 시스템이란 컴파일러에게 사용하는 타입을 명시적으로 지정할 수 있는 시스템과 명시적으로 타입을 지정하지 않더라도 컴파일러가 자동으로 타입을 추론하는 시스템입니다.

타입스크립트 컴파일러는 타입 검사, 변환, 코드 생성 등의 과정을 거치며 타입스크립트 코드를 자바스크립트 코드로 변환합니다.

컴파일러 역할과 컴파일 과정 더 보기
  1. 소스 코드 읽기: 타입스크립트 컴파일러는 타입스크립트 소스 코드와 tsconfig를 기반으로 컴파일할 파일들과 규칙들을 토대로 파일들을 읽고 분석합니다.
  2. 구문 분석: 컴파일러는 읽어드린 소스 코드 구조를 분석하며 구문 트리(Abstract Syntax Tree)를 생성하고 문법적 유효성을 확인합니다. 구문 분석 단계에서는 소스 코드를 작은 단위로 토큰화(변수명, 문자, 숫자, 연산자 등 토큰으로 식별)하고 계층적인 구조로 표현합니다.
  3. 의미 분석: 구문 분석 단계에서 생성된 구문 트리를 활용하여 타입 검사를 수행합니다. 이 과정에서 변수, 함수 등의 타입을 분석해서 변수 할당, 함수 호출, 연산 등에서 발생할 수 있는 타입 오류를 컴파일 타임에서 찾아냅니다.
  4. 변환: 컴파일러는 타입스크립트 코드를 자바스크립트 코드로 변환합니다. 이 단계에서 자바스크립트 환경에서도 타입스크립트 기능을 유지하는 구문 트리로 수정하고 변환합니다.
  5. 코드 생성: 변환된 구문 트리를 기반으로 자바스크립트 코드를 생성합니다. 생성된 코드는 자바스크립트가 동작하는 브라우저나 Node.js 환경에서 실행됩니다.

타입스크립트의 타입 시스템은 코드가 오류를 발생시키지 않는 유효한 자바스크립트 코드일지라도 정적 타입 시스템에서 오류로 간주될 경우, 코드 작성 단계에서 알려줍니다.

js
1const group = {
2  name: 'BTS',
3  memberCount: '6',
4};
5group.album; // undefined

자바스크립트에서는 코드를 실행한 런타임에 실행 결과인 undefined를 알려줍니다.

ts
1const group = {
2  name: 'BTS',
3  memberCount: '6',
4};
5group.album; // Property 'album' does not exist on type { name: string; memberCount: number; }

타입스크립트에서는 객체에 존재하지 않는 프로퍼티에 접근하고자 할 때 코드 작성 단계에서 오류를 알려줍니다.

뿐만 아니라 타입스크립트는 명시적인 에러는 아니지만 에러로 간주되는 코드를 개발자에게 코드 작성단계에서 알려줍니다: 오타, 호출되지 않는 함수, 논리 오류 등

ts
1const announcement = 'Hello World!';
2announcement.toLowercase();
3// 'toLowercase' 속성이 '"Hello World!"' 형식에 없습니다. 'toLowerCase'을(를) 사용하시겠습니까?
ts
1function flipCoin() {
2  return Math.random < 0.5;
3}
4// '<' 연산자를 '() => number' 및 'number' 형식에 적용할 수 없습니다.
ts
1const value = Math.random() < 0.5 ? 'a' : 'b';
2if (value !== 'a') {
3  // ...
4} else if (value === 'b') {
5  // ...
6}
7// '"a"'이(가) '"b"'과(와) 겹치지 않으므로 이 비교는 의도하지 않은 것 같습니다.

 타입스크립트의 정적 타입 시스템에서 가져갈 수 있는 이점은 우리가 코드를 실행했을 때가 아닌 코드 상에서 실수를 저질렀을 시점에 오류를 잡아준다는 것이겠죠? 개발자가 코드를 실행하고 운 좋게 문제가 발견되지 않더라도 나중에 코드를 수정하거나 리팩토링을 거친 후 실행했을 때 미처 발견하지 못한 잠재적인 오류가 있을 수도 있습니다. 타입스크립트의 역할은 작성한 코드가 명시 또는 추론된 값의 형태대로 안전하게 동작할 수 있도록 컴파일 타임에 오류를 잡아줍니다.

 타입스크립트는 컴파일러가 보다 엄격하고 일관성 있게 잠재적 오류를 발견할 수 있도록 다양한 옵션들을 제공합니다. 타입스크립트의 타입 시스템은 매개변수의 타입을 명시적으로 지정하지 않을 경우 몇몇 경우에 타입을 any로 추론합니다. doubleQuantity 함수에서는 a를 any 타입으로, 리턴 타입을 number로 추론합니다(NaN도 number 타입).

ts
1function doubleQuantity(a) {
2  if (a > 0) {
3    return a * 2;
4  }
5}
6console.log(doubleQuantity(3)); // 6
7console.log(doubleQuantity('cup') + 2); // NaN

noImplicitAny 옵션은 타입이 any로 추론되는 변수에 대해 에러를 반환합니다. any 타입은 모든 종류의 값을 나타낼 수 있는 "관대한" 타입이기 때문에 any 타입으로 추론되는 변수에 대해서 타입 명시를 강제합니다.

ts
1// 매개변수 a의 타입이 any로 추론되기 때문에 타입 명시를 강제
2function doubleQuantity(a: number) {
3  if (a > 0) {
4    return a * 2;
5  }
6}

매개변수에 타입을 명시적으로 지정했습니다. 사용자는 규칙에 맞게 매개변수로 number 타입의 값을 전달하고 실행합니다.

1console.log(doubleQuantity(3)); // 6
2console.log(doubleQuantity(-5)); // undefined
3console.log(doubleQuantity(-5) + 2); // NaN

규칙에 맞게 number 타입의 값을 전달했으나 조건문에 부합하지 않은 값을 전달하고 실행했을 때는 추론된 리턴 타입인 number와 다른 undefined가 반환됩니다. 이는 런타임과 컴파일 타임의 결과가 다르다는 의미겠죠.

자바스크립트는 모든 타입이 nullable입니다. 즉, 변수에 어떠한 값이든 할당할 수 있고 필요한 경우에는 null 또는 undefined를 할당할 수 있습니다. 타입스크립트의 strictNullChecks 옵션은 null과 undefined 값에 대한 명시적인 처리를 강제하는 옵션입니다. 해당 옵션은 코드 작성 단계에서 작성자에게 잠재적인 null 또는 undefined 타입의 값을 오류 메시지로 알려줍니다.

1console.log(doubleQuantity(3)); // 6
2console.log(doubleQuantity(-5)); // error: Object is possibly 'undefined'.
3console.log(doubleQuantity(-5) + 2); // error: Object is possibly 'undefined'.

그 외 옵션과 타입스크립트가 제공하는 다양한 기능, 문법을 활용해서 프로젝트의 타입 안정성을 갖출 수 있습니다.

자바스크립트의 언어적 단점들을 보완해 주는 타입스크립트의 타입 시스템에 대해 간략히 알아봤는데 타입스크립트의 강점들을 다음과 같이 정리해 볼 수 있을 거 같아요.


컴파일 타임에 오류를 잡아요

 타입스크립트는 코드 작성 단계에서 타입 체크를 수행해요. 이때 컴파일러는 변수, 함수, 객체 등의 타입 정보를 추론하고, 타입 검사 규칙을 적용하여 코드 작성자와 사용자가 안전하게 코드를 작성하고 사용할 수 있는 환경을 제공합니다. 컴파일 타임에 오류를 발견하는 것은 런타임에 발생할 수 있는 오류를 사전에 방지할 수 있다는 것이고 예측이 가능한 코드로 만들어줘요. Airbnb는 타입스크립트를 도입하면서 런타임 에러가 40%나 감소했다고 해요. 코드 작성 단계에서 잠재적인 타입 오류를 찾을 수 있다는 점만으로도 큰 플러스 같습니다.


데이터를 설명하고 코드 이해도를 높여요

 타입스크립트는 소스코드의 객체와 함수가 어떤 값들을 공유하는지를 설명해요. 개발자들이 처음 타입스크립트를 접할 때는 개발에 드는 시간이 운 좋으면 2배, 때로는 3배, 4배, 5배(저처럼) 더 드는 경우가 많습니다. 타입스크립트 문법에 익숙지 않거나 값들이 어떤 타입을 공유하는지에 대해 상대적으로 깊게 생각해 볼 필요가 없었으니 당연한 현상 같습니다. 다만 타입스크립트를 적절히 사용하고 익숙해지면 타입스크립트는 다른 개발자들 뿐만 아니라 코드를 작성한 본인에게도 소스코드의 이해도를 높이는 선순환 효과를 가져오는 거 같아요. 어떤 타입의 값들이 공유되고 코드의 동작 흐름을 이해하고 예상할 수 있다는 것은 코드가 어떻게 동작해야 하는지, 어떻게 동작할지에 대한 이해도와 예측성이 생긴다는 의미일 테니까요. 소스코드 내 일종의 문서로도 참고하고 활용할 수도 있어요.


튼튼한 생태계

 NextJS, Vue, Angular 등 많은 프론트엔드 프레임워크는 타입스크립트와의 호환성이 좋고 리액트 공식 홈페이지에서도 타입스크립트를 리액트에서 사용할 수 있는 가이드를 제공하고 있습니다. 다양한 개발자 커뮤니티에서 타입스크립트는 지속적으로 성장하고 있는 언어입니다. 대부분의 라이브러리들은 타입스크립트를 지원하고 있고 각종 에디터들은 타입스크립트 관련 기능을 제공합니다.


typescriptlang.org

1.2 stackoverflow typescript, https://stackoverflow.blog/2021/05/05/getting-started-with-typescript/


개발자/사용자 경험

 명시한 타입 정보를 기반으로 코드를 작성할 때 자동완성 기능은 객체의 속성과 메소드뿐만 아니라 해당 속성과 메소드의 타입 정보도 함께 알려줘서 개발자가 가능한 동작을 빠르고 올바르게 사용할 수 있게 도와줘요. 개발의 생산성 뿐만 아니라 코드의 타입 안정성은 사용자가 마주할 수 있는 버그를 줄일 수 있어요. 타입 안정성을 개선해서 코드의 동작을 예측할 수 있다는 것은 코드의 신뢰성을 높일 수 있다는 것이고 앱이 안정적으로 실행된다는 의미이기도 해요. 사용자에게 좀 더 안정적인 경험을 제공하는 것이 좋은 코드의 조건이기도 하겠죠.

출처 : TypeScript: JavaScript With Syntax For Types