타입스크립트 기초와 타입 시스템

타입스크립트와 친해지기

왜 타입스크립트를 써야할까?

인기있는 자바스크립트 프레임워크 중에 하나인 Angular는 타입스크립트를 사용하고, 주요 업그레이드가 진행되고 있는 Vue 3.0도 타입스크립트로 제공된다고 합니다. 그런데 왜 Angular나 Vue 같은 자바스크립트 프레임워크는 자바스크립트가 아닌 타입스크립트를 사용하는 걸까요?

타입스크립트는 Apache 라이센스가 부여되어 있는 마이크로소프트에서 개발한 오픈 소스 언어입니다. 물론 유지 및 관리도 마이크로소프트에서 하고 있죠. 타입스크립트는 일반 자바스크립트로 컴파일이 되는 자바스크립트의 상위 호환언어라고 할 수 있는데, ‘타입’이라는 이름에서도 알 수 있듯이 강한 타입 시스템을 사용합니다.

typescript.org

C#, Java와 같은 체계적이고 정제된 언어들에서 사용하는 강한 타입 시스템은 높은 가독성코드 품질 등을 제공할 수 있고, 치명적인 오류는 런타임이 아닌 컴파일 환경에서 발생되기 때문에 타입과 관련한 종류의 에러를 더 쉽게 잡아낼 수 있습니다.

반면에 자바스크립트는 타입 시스템이 없는 동적 프로그래밍 언어이기 때문에 변수에 타입을 지정하지 않아도 문자열, 숫자, 불린 등 여러 타입의 값을 가질 수 있습니다. 즉 ‘약한 타입 시스템’을 가진 언어라고 할 수 있는데, 비교적 유연하게 개발할 수 있는 환경을 제공한다는 장점이 있지만 런타임 환경에서 쉽게 에러가 발생할 수 있다는 단점도 가지고 있습니다.

강한 타입 시스템을 사용하는 타입스크립트는 이런 자바스크립트의 단점을 보완한 언어로 대부분의 에러를 컴파일 환경에서 코드를 입력하는 동안 체크할 수 있습니다.

대표적인 프레임워크인 Angular는 타입스크립트로 작성되었는데, Angular가 타입스크립트를 사용하는 이유는 생산성 때문이라고 할 수 있습니다. 또 업데이트가 진행되고 있는 Vue의 경우에도 Angular과 같은 이유로 타입스크립트를 지원하는 것으로 추측됩니다.

컴파일과 에러 체크

만약, 어떤 함수가 ‘문자열’을 인자로 받아야 하는데 개발자가 이 함수에 실수로 ‘숫자’를 인자로 전달한다면 어떻게 될까요? ‘문자열’을 인자로 받아야 하는 함수가 ‘숫자’로 인자를 전달받게 되면 당연히 에러가 발생하게 됩니다.

자바스크립트에서는 이런 상황이 생각보다 자주 발생되는데, 자바스크립트는 컴파일 과정이 없기 때문에 런타임에서 에러가 발생하게 됩니다. 즉 사소한 실수 하나로 운영 중인 서비스에 에러가 발생하여 애플리케이션이 종료될 수도 있다는 뜻이죠. 그런데 이런 사소한 실수가 큰 규모로 운영 중인 서비스에서 발생한다면 정상적인 서비스 제공하지 못해 입을 수 있는 손실이 무척 클 수 밖에 없을 겁니다.

하지만 컴파일 과정이 있다면 어떨까요? 앞의 사례와 같은 사소한 실수는 컴파일 과정에서 미리 에러를 발생시키기 때문에 문제를 미리 발견하고 처리할 수 있습니다. 대표적인 컴파일 언어라고 할 수 있는 JavaC#에서는 ‘타입’이 맞지 않으면 컴파일 자체가 되지 않죠. 하지만 자바스크립트는 동적으로 타입이 결정되고 별도의 컴파일 과정이 없는 스크립트 언어이기 때문에 이런 사소한 에러에 대한 대책을 세우기가 어렵습니다.

‘타입’에 대한 문제는 작은 규모의 프로그램에서는 큰 문제가 아닐 수도 있습니다. 하지만 애플리케이션의 크기가 커지고, 구조가 점점 복잡해지고, 사용해야 할 API가 많아지면 개발자의 생산성은 크게 떨어질 수 밖에 없습니다. 그래서 큰 프로젝트일수록 코드를 리팩토링할 때 IDE가 문맥에 맞는 도움말을 제공하거나 오류가 발생할 수 있는 코드를 미리 검사해주는 기능이 있는지 여부가 중요한데, 타입을 지원하지 않는 자바스크립트는 이런 기능을 사용할 수가 없는 것이죠.

타입스크립트의 장점

타입스크립트의 장점을 정리하면 다음과 같습니다.

  • 타입스크립트는 타입을 지원하여 에러가 발생할 수 있는 코드는 컴파일 단계에서 미리 검출할 수 있고, 런타임에서 발생할 수 있는 에러를 방지할 수 있다.
  • 타입스크립트는 인자의 개수가 잘못되거나 다른 타입의 인자 전달 시 에러 표시, 코드 수정 시 컨텍스트에 어울리는 도움말 제공, 코드 리팩토링과 같이 더 높은 생산성을 발휘할 수 있는 유용한 기능을 IDE에서 지원해 줄 수 있다.
  • 타입스크립트는 ECMAScript 6과 7 표준을 따르고, 여기에 타입, 인터페이스, 데코레이터, 클래스 멤버 변수, 제네릭, public, private과 같은 키워드를 추가로 제공한다.
  • 타입스크립트에서 제공하는 인터페이스 기능을 사용하면 애플리케이션에 적합한 커스텀 타입을 직접 정의할 수 있어 애플리케이션을 구조적으로 더 견고하게 만들 수 있다.
  • 타입스크립트로 코드를 작성하고 컴파일을 한 자바스크립트 코드는 더 좋은 가독성을 제공한다.

타입스크립트시작하기

타입스크립트는 마이크로소프트가 오픈 소스로 공개한 후 GitHub 저장소를 통해 소스를 관리하고 있는데, 타입스크립트를 사용하기 위해서는 전용 컴파일러가 필요합니다.

타입스크립트의 컴파일러는 NPM을 통해 설치하거나 타입스크립트 웹사이트에서 내려받을 수 있는데, 타입스크립트 웹사이트에서는 웹에서 직접 컴파일을 할 수 있도록 타입스크립트 트래스파일러도 제공합니다. 만약 간단한 타입스크립트 코드를 테스트해 보고 싶다면 이 서비스를 이용하면 되겠죠?

typescript.org 웹 트랜스파일러
typescript.org 웹 트랜스파일러

컴파일러 설치 및 설정

타입스크립트 컴파일러 자체도 타입스크립트를 이용해서 작성되어있는데, NPM으로 간단하게 설치할 수 있습니다. 아직 Node.js를 설치하지 않았거나 NPM 사용법이 궁금하다면 이전의 포스트를 참고해 보세요.

npm 패키지 관리자로 Node.js 패키지를 설치할 때 -g옵션을 붙여주면 전역 환경으로 설치가 되는데, 이렇게 전역 환경으로 설치하면 로컬에 있는 모든 프로젝트에서 해당 패키지를 사용할 수 있습니다.

타입스크립트 컴파일러도 모든 프로젝트에서 사용할 수 있도록 전역으로 설치해주는 것이 좋은데, 다음의 명령어로 설치할 수 있고, 설치하고 난 후에는 tsc -v라는 명령어로 설치 버전을 확인할 수 있습니다.

타입스크립트 컴파일러 설치하기
npm i -g typescript
컴파일러 버전 확인
tsc -v

컴파일

타입스크립트 코드를 작성하고 저장하면 .ts라는 확장자로 저장이 되는데, 이 .ts라는 확장자로 저장된 파일은 웹 브라우저에서 실행될 수 있도록 컴파일러를 통해 자바스크립트 코드로 변환해주어야 합니다.

타입스크립트 코드를 컴파일하는 방법은 tsc {파일명}.ts와 같이 tsc 명령어 다음에 컴파일을 원하는 파일명을 적어주면 되는데, 타입스크립트에서 지정한 모든 타입의 정보와 인터페이스 그리고 타입스크립트에서만 사용할 수 있는 키워드들은 타입스크립트 코드가 컴파일되고 난 후 모두 제거됩니다.

타입스크립트 컴파일하기
tsc typescript_file.ts

소스맵 생성

타입스크립트를 사용한다면 에러가 발생했을 때 컴파일되기 전의 소스 코드를 디버깅할 필요가 있습니다. 디버깅을 위해서는 당연히 컴파일된 자바스크립트가 아닌 원래의 소스 코드에서 문제를 찾아야 하는데, 이를 위해서는 소스맵이란 것을 생성해야 합니다.

소스맵을 생성하면 브라우저에서 자바스크립트 코드를 실행하더라도, 타입스크립트 코드의 중단점을 설정하고, 원하는 시점에서 코드의 실행을 멈추고 디버깅을 실시할 수도 있습니다. 생성된 소스맵은 {파일명}.map과 같이 .map이라는 확장자로 저장됩니다.

소스맵 생성하기
tsc typescript_file.ts --sourcemap

소스맵 디버깅은 개발자 도구로 할 수 있습니다. 개발자 도구를 열고 소스탭을 선택하면 타입스크립트로 작성한 코드를 확인 할 수 있고 코드의 원하는 곳에 중단점을 지정해 편리하게 디버깅을 할 수 있습니다.

크롬 개발자도구 타입스크립트 디버깅

자바스크립트 버전 지정

타입스크립트 파일을 컴파일 할 때 자바스크립트 코드의 버전을 지정할 수도 있습니다. 기본 값은 ES3이지만 다음과 같이 ES5나 ES6로도 지정할 수 있습니다.

자바스크립트 버전 지정
tsc type.ts --t ES6

파일 변경 자동 감지

–watch 또는 -w 옵션은 타입스크립트 컴파일러를 watch 모드로 실행하는 옵션입니다. watch 모드로 실행을 하게 되면 코드를 변경하고 저장할 때 컴파일러가 파일의 변경을 자동으로 감지하고 컴파일하게 됩니다.

다음과 같은 명령어로 프로젝트의 모든 .ts 파일에 대해 watch 모드를 적용할 수 있습니다.

watch 모드 설정하기
tsc --watch *.ts

타입스크립트 설정파일

Node.js 프로젝트에서는 타입스크립트의 컴파일 옵션을 미리 지정하여 파일로 만들어 둘 수 있습니다. 타입스크립트의 컴파일 옵션을 설정하는 파일은 보통 프로젝트의 루트 폴더에 tsconfig.json이라는 이름으로 저장이 되는데, 이 파일이 있다면 커맨드라인에서 tsc 명령어를 실행할 때 이 파일에 있는 설정을 반영합니다.

tsconfig.json
{
    "compilerOptions" : {
        "target" : "es6",
        "module" : "commonjs",
        "emitDecoratorMetadata" : true,
        "experimentalDecorators" : true,
        "rootDir" : ".",
        "outDir" : "./build"
    }
}

tsconfig.json 파일이 프로젝트의 루트에 있다면, tsc는 루트 폴더로 지정된 폴더의 모든 ts 파일을 컴파일하여 ./build 폴더에 컴파일된 자바스크립트 파일을 생성하여 저장합니다.

만약 대상 파일을 따로 지정하고 싶은 경우에는 files라는 프로퍼티를, 특정한 파일을 컴파일 대상에서 제외하고 싶은 경우에는 exclude라는 프로퍼티를 사용하면 됩니다. 이 외에도 더 많은 옵션이 있는데, 해당 옵션들은 타입스크립트의 웹사이트에 있는 컴파일러 옵션 문서에서 확인할 수 있습니다.

상위 집합

타입스크립트는 ES5, ES6 문법 대부분을 지원하기 때문에 순수한 자바스크립트 파일의 확장자를 .js에서 .ts로 바꾸기만해도 그 파일은 정상적으로 컴파일이 될 수 있습니다.

이렇게 순수한 자바스크립트 파일 자체로도 타입스크립트 컴파일이 되는 이유는 타입스크립트가 자바스크립트의 상위 집합이기 때문입니다. 즉 타입스크립트는 기본적인 자바스크립트의 기능 위에 생산성을 위한 몇 가지 유용한 기능을 추가한 상위 집합 언어라고 할 수 있습니다.

결국 타입스크립트는 완전히 새로운 언어가 아니며, 기존의 자바스크립트 문법에 익숙한 개발자라면 쉽게 익힐 수 있는 언어입니다.

js에서 ts로 변환하기

만약 프로젝트의 기본 작업 언어를 자바스크립트에서 타입스크립트로 바꿔야 한다면 컴파일러를 실행할 때 –allowJs 옵션을 사용하면 됩니다.

–allowJs 옵션은 –target 옵션과 –module 옵션을 함께 사용할 수 있는데, 이 옵션들에 따라 js 파일의 문법 에러를 검사하고 최종 결과물을 만들 수 있습니다. 이렇게 만든 자바스크립트 파일은 타입스크립트 파일을 변환한 자바스크립트 파일과 같이 사용해도 에러가 발생하지 않습니다.

타입 지정하기

타입스크립트는 변수를 선언할 때 타입을 지정할 수 있는데, 다음 처럼 기존의 자바스크립트에 타입을 지정하지 않아도 에러가 발생되지는 않습니다.

let firstName = '길동';
let lastName:string = '홍';

그렇다면 왜 굳이 변수의 타입을 지정해서 사용할까요? 그 이유는 타입을 지정하면 타입스크립트의 컴파일러가 컴파일을 할 때 잘못된 코드를 잡아낼 수 있고, IDE와 같은 프로그램에서는 자동 완성 기능이나 리팩토링과 같은 편리한 기능을 제공해 줄 수 있기 때문입니다. 즉, 생산성을 높일 수 있는 것이죠.

타입스크립트는 컴파일러가 타입을 지정하지 않은 변수에 할당되는 값을 기준으로 타입을 예측하여 자동으로 타입 체크를 수행하는 타입 추론이라는 기능을 적용하기 때문에, 동일한 변수에 지정된 타입이 아닌 다른 타입의 값을 할당하면 컴파일시에 에러를 발생하게 됩니다.

타입스크립트에서는 string 타입으로 지정한 변수는 string 타입의 값만을 할당할 수 있고, number 타입이라면 number 타입으로만 값을 할당해야 합니다. 그리고 타입을 지정하지 않은 경우라도 해당 변수에 처음 할당된 값을 기준으로 타입을 인식하기 때문에 이후에는 해당 변수에 다른 타입의 값을 할당할 수 없습니다.

이렇게 타입스크립트는 타입에 대해 강한 규칙을 적용하기 때문에 다음과 같은 경우에는 모두 에러가 발생합니다.

타입을 지정하지 않을 경우
let name1 = '홍길동';
name1 = 12345;
타입을 지정한 경우
let name2:string = '임꺽정';
name2 = 12345;

타입스크립트는 변수뿐만 아니라 함수의 인자, 함수의 반환값에도 타입을 지정할 수 있는데, 타입스크립트에서 제공하는 자료형은 매우 다양합니다. 타입스크립트에서 제공하는 자료형에 대해 더 자세히 알고 싶다면 타입스크립트 웹사이트의 Basic Types 문서를 참고하기 바랍니다.

  • Boolean
  • Number
  • String
  • Array
  • Tuple
  • Enum
  • Unknown
  • Any
  • Void
  • Null and Undefined
  • Never
  • Object

타입을 지정해 변수를 선언하려면, 변수명 뒤에 콜론:을 붙이고 사용할 타입을 적어주면 됩니다. 만약 변수나 함수의 인자에 타입을 명시하지 않은 경우에는 컴파일러가 해당 변수나 인자에 any 타입을 지정한 것으로 간주하고 처음에 할당되는 값에 모든 타입을 허용합니다.

타입을 지정하지 않은 경우 간주되는 타입
let account:any;
let name:any = '홍길동';
let isOpen:any;
let whoAreYou:any = null;

다음과 같이 변수에 적용한 number, string, boolean 타입은 모두 any 타입의 하위 타입입니다.

타입 지정
let account:number;
let name:string = '홍길동';
let isOpen:boolean;
let whoAreYou:string = null;

그런데 아무 타입도 지정하지 않은 경우와 any 타입으로 지정한 경우에는 결정적인 차이가 있습니다. 바로 처음 값을 할당한 이후에 다른 타입의 값을 할당 할 수 있는지 여부입니다.

즉 아무 타입도 지정하지 않은 경우에는 처음 값을 할당한 후에는 타입 추론 기능에 의해 하나의 타입으로 고정되지만, any 타입을 지정한 경우에는 처음 값을 할당한 이후에도 다른 타입의 값을 자유롭게 할당할 수 있는 것이죠.

타입을 지정하지 않은 경우
let name = '홍길동';
name = 12345; // 에러 발생
any 타입을 지정한 경우
let name:any = '홍길동';
name = 12345; // 정상 컴파일

이렇게 타입스크립트는 변수에 명시적으로 타입을 지정하면 컴파일러가 변수의 타입을 체크하고 오류를 잡아낼 수 있는데, 이 외에도 웹 브라우저에서 사용하는 HTML ElementDocument와 같은 타입도 지원하고, 클래스나 인터페이스도 새롭게 정의하여 커스텀 타입으로 변수의 선언에 사용할 수 있습니다.

지금까지 살펴본 것 처럼, 타입스크립트에서 다양한 타입을 지정하여 사용하는 것은 강력한 디버깅을 할 수 있게 해주고, 쉽게 오류를 찾아낼 수 있게 해줍니다.

답글 남기기