TS. TypeScript를 알아보자

momo
13 min readAug 21, 2022
영롱한 타스에대해 알아보자

TypeScript

  • ms에서 구현한 JavaScript의 Superset 프로그래밍 언어
    (슈퍼셋이란 컴퓨터 과학에서 특정한 언어의 모든 기능을 포함하면서 다른 기능까지 포함하도록 향상 또는 확장되는 것을 의미한다는 개념입니다)
  • 타입스크립트는 이름답게 정적 타입을 “명시” 할 수 있다는 것이 순수 자바스크립트와의 가장 큰 차이인데, 이 때문에 개발 도구(IDE or Compiler) 에 개발자가 의도한 변수나 함수 등의 목적을 더욱 명확히 전달할 수 있습니다.

이를 통해 개발자는 풍부한 피드백을 얻고 디버깅에 용이한 개발을 할 수 있게 되는 것이죠. 순수한 자바스크립트에 비해 어마어마한 생산성 향상을 꾀할 수 있게 되는 것입니다.

즉, ‘자바스크립트를 실제로 사용하기 전에 있을만한 타입 에러들을 미리 잡는 것’ 이 타입스크립트의 사용 목적입니다.

개발자와 도구 간의 상호작용 뿐만이 아닌, 개발자 간의 상호작용에도 다양한 이점이 있는데요. 대표적으로 API를 구현하고 사용함에 있어 해당 API의 req, res가 무엇인지 명확하게 표현할 수 있으므로 API 하나를 사용함에도 자바스크립트에 비해 보다 효율적이라는 것이죠. 어느정도인지 체감이 되시나요!?

자바스크립트와의 차이

  • 타입 선언 기능의 존재

TS는 버그가 일어나기 쉬운 요소의 타입을 선언하여 버그가 일어나는 것을 방지.

// Javascriptconst a = 3;
const b = '5';
console.log(a*b); //return 15
// Typescriptconst a:number = 3;
const b:string = '5';
console.log(a*b) // return Error
컴파일 전에 오류 메시지를 띄우게 함으로 개발에 용이.
  • DOM 조작 용이
// Javascriptvar alist = document.querySelectorAll('a');
for (var i = 0; i < alist.length; i++) {
alist[i].style.color = '#333'
};

var example = document.querySelector('.exampleClass');
example.style.backgroundColor = "#ccc";

document.addEventListener('mousemove', function () {
var x = event.clientX;
document.querySelector('h1').style.fontSize = (x / 50) + 'px'
});
// Typescriptconst alist = document.querySelectorAll('a');
for (let i: number = 0; i < alist.length; i++) {
alist[i].style.color = '#333'
};

const example: HTMLElement = document.querySelector('.exampleClass');
example.style.backgroundColor = "#ccc";

document.addEventListener('mousemove', function (event: MouseEvent) {
const x = event.clientX;
(document.querySelector('h1') as HTMLElement).style.fontSize = String(x / 50) + 'px';
});

as로 Casting을 해주는 경우도 좋은 사례이지만, 애초에 저런 Casting없이 컴파일러의 타입추론만으로 함수나 변수, 클래스를 사용할 수 있게끔 타입을 정의하는 것이 더 중요하다. 타입정의에 시간이 오래 걸리는 것 같아도 결국에는 시간을 몇십배 절약할 수 있기 때문이죠.

[사용사례]

  1. 백엔드와 프론트엔드 통합

타입스크립트는 Node.js 런타임 뿐만 아니라, 원래 자바스크립트의 고향인 프론트엔드 개발에서도 상당히 유용합니다.

특히 프론트와 백엔드를 모두 TS로 구현한다면 종전과는 비교할 수 없을 정도의 개발 안정성과 편의성을 확보할 수 있습니다.

프론트와 백엔드의 상호 간 데이터 통신을 위해서는 일반적으로 JSON형식의 REST API를 구현하게 되는데, 이렇게 사용되는 데이터 포맷은 일반적으로 개발 과정 중에 수없이 변경되고 또 변경됩니다.. 😫

이로 인해 프론트엔드와 백엔드 개발자 사이에서는 수도 없이 커뮤니케이션 로스가 발생하고 이는 고스란히 개발자의 피로도 상승 및 개발 기간 증가, 유지보수성의 저하로 이어지겠습니다..ㅠㅠ

또한 이러한 문제점들은 실제로 프로덕션 레벨에서 확인해야만 드러나는 경우도 발생하기 때문에 최악의 경우 검증되지 않은 버그가 남은 채 프로젝트를 퍼블리싱할 수도 있습니다 😢

Typescript가 이러한 사태를 미연에 방지할 수 있는 이유는, 프론트엔드와 백엔드간의 데이터 포맷을 타입으로 정의하여, 이를 양측에서 공통으로 참조하도록 구현하고, 데이터 포맷의 변경 사항이 발생할 경우 이렇게 공용화된 타입의 정의부를 수정함에 따라 프론트엔드와 백엔드 코드에 컴파일 에러를 발생시킴으로써 데이터 포맷의 통일을 강제하기 때문입니다.

2. 프론트엔드 단독 사용

물론 프론트엔드에서도 단독으로 사용이 가능합니다. 바닐라 자바스크립트로 컴파일 되는 것은 물론! Google Angular의 경우는 2.0부터 TS만 지원하고 있습니다. (그때문에 사용자들이 많이 떨어져 나갔다는..)

Facebook의 리액트는 React.js , React Native를 가리지 않고 타입스크립트 사용이 가능합니다. 현재는 JS와 TS간의 교류도 굉장히 활발하고, DefinitelyTyped등의 기여로부터 React에서 TS를 사용하는 것은 정석 중의 하나로 자리잡게 되었습니다. React의 경우는 특히 Function Component, Hooks와 함께 타입스크립트를 사용한다면 이전에 클래스 컴포넌트를 사용할 때보다 훨씬 쉬운 개발이 가능하죠 ㅎㅎ

[특장점]

  1. 기존자바스크립트 소스와의 호환

타입스크립트를 사용할 때, 타입스크립트가 런타임에서 그대로 돌아가는 경우는 매우 드뭅니다. 타스는 “컴파일”을 통해 자바스크립트로 변환되는 것이기 때문에 기존의 자바스크립트와 호환성이 우수하며 자바스크립트 런타임은 그 컴파일된 자바스크립트를 기존의 자바스크립트 파일과 실행가능하게 만드는 것이죠.
기존의 자바스크립트 프로그래머들은 타입스크립트를 도입할 때 처음 고려해봐야할 것이 ‘**.d.ts’ 파일 작성입니다.
클래스,또는 어떤 자바스크립트 객체이든지 타입스크립트로 정의 가능한데
기존의 자바스크립트 소스를 일절 건드리지 않고 *.d.ts 파일 작성만으로 타입스크립트 도입에 큰 도움을 줄 수 있습니다. DefinitelyTyped repo에서 다운받는 @types/<패키지이름> 패키지가 그것입니다.

이 레포는 기존 자바스크립트 npm 패키지들이 기존 소스를 건드리지 않고 타입스크립트 진영에 자연스럽게 녹아들 수 있도록 도움을 주는데요. JS 언어 차원에서 지원하는 API들의 입출력 타입들도 친절하게 정의돼 있기 때문에 이를 더 안정적으로 사용할 수 있게 되기도 합니다.

또한 TS는 const, let과 같은 ES5 이상의 버전에서 사용되는 구문을 지원하는데, 이를 컴파일하면 ES3 버전으로 컴파일을 시켜주어 구버전 브라우저에서도 작동할 수 있게 됩니다. 상위 버전의 자바스크립트 구문을 사용하고도 IE 등의 구버전을 지원할 수 있다는 것이죠.

2. 다른 언어와의 문법 유사성

애초에 JS가 C, C++, Java, C# 같은 언어들 처럼 (), {}, ; 등을 사용하는 C-family 언어이기 때문에 문법적으로 유사함을 내세우는 타입스크립트 역시 C-family 언어입니다. 오히려 그 정도가 특히 더 가깝죠. 예를 들면 Generic에 <> 기호를 쓰는 것을 보며 TS가 자바, C#, C++와는 본질적으로 비슷하지도 않지만, 타입스크립트를 처음 입문하는 기존 개발자들은 제네릭이라고 빠르게 이해할 수 있을 겁니다. 거기에 interfacce, const 같은 지시문 역시 기존 언어 사용자들이 익숙하다고 느낄만한 사용방법을 가지고 있습니다.

3. IDE와의 궁합

타입 개념의 도입을 통해 TypeScript 개발을 지원하는 IDE로부터 풍부한 피드백을 받을 수 있습니다. 일반적인 여느 정적 타입 언어들처럼 자동 완성을 지원하거나, 해당 타입이 지원하지 않는 연산의 시도나 잘못된 인자를 사용한 함수 호출 등을 코딩 중에 즉석에서 검출할 수 있는 것은 기본. Symbol의 이름을 변경하면 해당 심볼을 참조하는 모든 코드들을 자동으로 수정해주는 리팩토링 기능 역시 JS가 아닌 TS이기 때문에 가능한 기능들입니다. 대표적인 IDE로는 WebStorm이 있으며, 같은 회사에서 만든 VSC와의 궁합이 엄청납니다. 플러그인을 통해 TypeScript로 상상할 수 있는 모든 개발환경을 너무나 쉽게 구축할 수 있습니다.

4. 과감한 신규문법 도입

Node.js를 포함해서 기존 자바스크립트 생태계에는 언어의 버전이 올라가도 어느정도 하위호환성을 위해 빠르게 새로운 문법을 추가하거나 기존 문법을 변경하지 못합니다.

babel과 같은 트랜스파일러를 통해 ES6+ 문법을 ES5 문법으로 컴파일하여 사용하는 등 호환성 문제를 해결하기 위한 여러 노력들은 있었으나.. 마음대로 트랜스파일 환경을 구성하는 건 쉬운일이 결코 아니고, 자바스크립트 문법이 아무리 급진적으로 발전하고 있다 하더라도 신규문법이 제안되어서 거절되는 경우가 허다하기 때문에 채용이 결정되더라도 실제로 사용까지의 레벨에는 최소 1~2년은 걸린다고 생각해야합니다..

반면 TS는 마이크로소프트가 언어의 발전을 주도적으로 이끌고 있으며, 사용하기 위해선 컴파일 과정을 반드시 거쳐야하기 때문에 하위호환성을 보다 유연적으로 고려해 ECMA보다도 더 빠르게 신규문법들을 채용하고 있습니다.

ECMA에서 반려되어 자바스크립트에서 채용될 가능성이 사라진 몇가지 문법들 중 타입스크립트 개발자들이 보기에 유용하다고 생각된 문법들도 일부 채용되었는데 대표적으로 decorator, abstract class가 있습니다. 이런 문법들은 엔터프라이즈급 프로젝트를 개발해야 할 수록 유용한 것이며 기업에서 프론트엔드 개발 또는 Node.js 기반 백엔드 개발시 TS를 적극 고려하는 가장 큰 이유가 되기도 하는 것이죠.

[주의 사항]

  1. 결국 Javascript 로 컴파일

타입스크립트는 결국 자바스크립트로 컴파일되어 동작하므로 ‘런타임에는 약타입'이라는 약점이 있습니다. 가령 타입스크립트로 작성한 Node.js 서버가 HTTP 요청을 받았을 때 클라이언트가 ‘username’이라는 값을 보내주었다고 선언할 수는 있지만, 이는 가정이 맞았을 때 자신의 코드가 잘 동작함을 보장할 뿐이지 실제 사용자가 어떤 값을 보냈는지 검사하지는 않습니다. 이는 타스 사용시 반드시 고려해야할 문제이기도 하죠.

실행 시점에 자료형을 검사하려면 type guard와 같은 함수를 직접 작성하거나, io-ts, runtypes 등의 라이브러리를 사용해 자료형 검사를 부분적으로 자동화해야 합니다.

하지만 언어 차원에서 이를 지원하는 것이 아니므로 결국 사용자가 불편을 감수해야한다는 것이죠.

다른 강타입 언어들 또한 대체로 컴파일 시점에 자료형을 확인하는 편입니다. 실행 시점에는 타입을 검사하지 않는다는 것이죠. 가령 C++ 컴파일러는 형변환이 불가능한 자료형 간의 대입을 금지하지만, 컴파일된 기계어에는 자료형을 검사하는 명령이 없습니다.

중요한 차이점이라면 다른 강타입 언어는 다른 라이브러리를 사용할 때 컴파일러를 거쳐야 하니 타입 규칙을 지켜야 하지만, 타입스크립트 코드를 컴파일해 나온 자바스크립트 코드를 직접 호출할 경우 컴파일러를 거치지 않으므로 예상치 못한 자료형을 전달하는 것을 막을 수 없습니다. 따라서 자신의 프로그램이 외부로부터 신뢰할 수 없는 데이터를 받아오는 경계선을 정확히 파악하고 필요한 경우 런타임 타입 검사 코드를 추가해야하는 것이죠.

2. 학습 곡선 및 Trade-off

자바스크립트는 객체가 갖는 속성을 어떻게든 개발자가 파악해서 어떻게든 에러 없이 동작하도록 설계하는 것이 기본적인 개발 플로우입니다. 반면 타입스크립트는 이토록 지나치게 자유로운 변수/객체 활용에 의도적으로 제약을 걸어 개발의 안정성 및 편의성을 증대시키는 것이 그 존재의의인 것이죠.

특히 복잡한 객체를 안전하게 활용할 수 있도록 제공되는 TS의 대표적인 도구가 Advanced Type과 Utility Type인데, 이 기능들은 자바스크립트 뿐만이 아니라 다른 언어에서도 생소한 개념이기 때문에 개발자들에게 익숙하지 않을 가능성이 상당히 높습니다.

모든 개발자가 문서를 한번 보고 응용 가능할 정도가 결코 아니기 때문에, 첫 도입시 이 부분을 간과하면 오히려 도입 전보다 생산성이 심각하게 안 좋아질 수도 있다는 점에 유의해야합니다.

또한 위와 같은 도구를 활용해서 기껏 엄밀하게 typing을 해놨더니 요구 사항 및 기능 변경, 리팩토링 작업 등으로 인해 기능 구현에 더해 typing까지 덤으로 다시 해야 하는 불상사가 생길 수도 있습니다. 경우에 따라서는 아예 any Type으로 도배하는 것만도 못한 결과가 초래될 수도 있다는 것.. 따라서 적재적소에 타스를 도입하는 안목을 기르는 것도 중요한 역량이라고 볼 수 있겠네요.

[JS라이브러리와의 호환성]

다른 사람이 만든 라이브러리를 TS에서 쓰려면 해당 라이브러리가 TS로 작성되었거나, 별도로 .d.ts 형식의 type definition 파일을 제공해야 합니다. 간혹 직접 타입 정의를 제공하는 라이브러리도 있지만, 대부분은 DefinitelyTyped라는 공개된 프로젝트를 통해 타입 정의를 제공합니다. 이러한 경우 some-js-library 패키지를 설치할 때 @types/some-js-library 패키지를 함께 설치하면 TS에서 사용할 수 있게 됩니다.

이용자가 많고 유명한 라이브러리들은 대부분 다른 개발자들의 재능기부를 통해 타입정의를 제공하고 있지만, 일부 라이브러리의 경우 타입 정의가 없어서 TS에서 사용할 수 없는 경우도 존재합니다. 그럼에도 불구하고 반드시 특정 라이브러리를 사용해야 하는 경우라면 2가지 안이 존재하는데요.

첫째로는 //@ts-ignore 주석을 import 문 위에 붙여서 컴파일러를 침묵시킵니다. require()를 사용하여 컴파일 오류 없이 라이브러리를 사용할 수도 있습니다. 물론 이렇게 불러온 라이브러리의 심볼들에 대해서는 타입 검사가 이루어지지 않기 때문에 TS의 장점을 활용하기에 어렵습니다.

두번째로는 직접 타입 정의 파일을 만드는 것인데요. TypeScript 컴파일러는 소스 코드가 있는 디렉토리의 모든 .d.ts 파일을 불러오므로, 다음과 같은 타입 정의 파일을 적당한 곳에 던져놓으면 됩니다. 물론 규모가 좀 있는 프로젝트를 꾸린다면 타입 정의 파일을 별도의 디렉토리에 모아놓는 것이 편합니다.

// some-js-library.d.ts
declare module "some-js-library" {
function someFunction(a: string, b: number): void;
class SomeClass {
/* .... */
}
}

위와 같이 특정한 패키지에 대한 타입 정의를 만들면, 자신의 코드에서
import { someFunction } from ‘some-js-library’; 와 같은 식으로 불러와 사용할 수 있습니다.

저도 현업에서 TS와 JS를 전부 경험해봤었는데, 시간이 지날수록 TS의 장점이 명확히 보이더라구요. 오늘은 본격 TS 포스팅에 앞서 TS 특징과 장단점에 대해 알아보았는데요. TS와 NEXTJS를 자유자재로 쓰는 순간까지 열심히 달려보겠습니다! 그럼 다음주도 행코하세요 😉

--

--