시작으로

TypeScript는 마이크로소프트가 발표한 자바스크립트를 기반으로 정적 타입 문법을 추가한 프로그래밍 언어이다. 요새 자바스크립트보다 타입스크립트를 사용하는 기업들이 많아지고 있다. 실제로 개발자 구인 시 우대사항 및 필수사항에 명시되어 있을 정도로 웹 프론트에서 핫한 언어이다.

TypeScript 등장 이전의 초창기 자바스크립트는 웹 페이지의 보조적인 기능을 수행하기 위해 한정적인 용도로 사용했다. 대부분 로직은 주로 웹 서버에서 실행되었고 브라우저는 서버로부터 전달받은 HTML과 CSS를 렌더링하는 수준이었다.

HTML5가 등장하기 이전에는 웹 애플리케이션을 플래시, 실버라이트, 액티브엑스와 같은 플러그인에 의존하여 인터렉티브한 웹페이지를 구축해왔다. HTML5가 등장함으로써 플로그인에 의존하던 구축 방식에서 자바스크립트로 대체되었다. 또한, AJAX의 활성화로 데스크탑 애플리케이션과 유사한 사용자 경험을 제공할 수 있는 SPA(Single Page Application)가 대세가 되었다. 이로써 과거 서버 측이 담당하던 업무의 많은 부분이 클라이언트 측으로 이동하게 되었고, 자바스크립트는 웹의 어셈블리 언어로 불릴 만큼 중요한 언어로 그 위상이 높아지게 되었다.

자바스크립트도 다른 언어와 마찬가지로 장단점이 존재한다. 자바스크립트는 언어가 잘 정제되기 이전에 서둘러 출시된 문제와 과거 웹페이지의 보조적인 기능을 수행하기 위해 한정적인 용도로 만들어진 태생적 한계로 장점과 단점이 많이 것이 사실이다.

자바스크립트는 C와 JAVA와 같은 C-family 언어와는 구별되는 특성이 있다.

  • 프로토타입 기반의 언어
  • 스코프와 this
  • 동적 타입 언어 혹은 느슨한 타입 언어

이러한 특성 때문에 클래스 기반의 객체지향 언어(C++, C#, JAVA)에 익숙한 개발자를 혼란스럽게 한다. 또한 코드가 복잡해질 수 있고 디버그와 테스트 공수가 증가하는 등의 문제를 일으킬 수 있어 큰 규모의 프로젝트에서는 주의해야 한다.

이같은 자바스크립트의 태생적 문제를 극복하고자 TypeScript가 등장하였다. TypeScript는 자바스크립트(ES5) 상위 확장이다. C#의 창시자인 덴마크 출신 소프트웨어 엔지니어 Anders Hejlsberg(아네르스 하일스베르)가 개발을 주도한 TypeScript는 Microsoft에서 2012년 발표한 오픈소스로, 정적 타이핑을 지원하며 ES6(ECMAScript 2015)의 클래스, 모듈 등과 ES7의 Decorator 등을 지원한다.

image


TypeScript 특징

1.컴파일 언어, 정적 타입 언어

자바스크립트는 동적 타입의 인터프리터 언어로 런타임에서 오류를 발견할 수 있다. 이에 반해 타입스크립트는 정적 타입의 컴파일 언어이며 타입스크립트 컴파일러 또는 바벨을 통해 자바스크립트 코드로 변환된다. 코드 작성 단계에서 타입을 체크해 오류를 확인할 수 있고 미리 타입을 결정하기 때문에 실행 속도가 매우 빠르다는 장점이 있다. 하지만 코드 작성 시 매번 타입을 결정해야 하기 때문에 번거롭고 코드량이 증가하며 컴파일 시간이 오래 걸린다는 단점이 있다.

2.자바스크립트 슈퍼셋(Superset)

타입스크립트는 자바스크립트의 슈퍼셋 언어다. 즉 자바스크립트 기본 문법에 타입스크립트의 문법을 추가한 언어입니다. 따라서 유효한 자바스크립트로 작성한 코드는 확장자를 .js에서 .ts로 변경하고 타입스크립트로 컴파일해 변환할 수 있다.

3.객체 지향 프로그래밍 지원

타입스크립트는 ES6(ECMAScript 6)에서 새롭게 사용된 문법을 포함하고 있으며 클래스, 인터페이스, 상속, 모듈 등과 같은 객체 지향 프로그래밍 패턴을 제공한다.


TypeScript를 사용해야 하는 이유

1.높은 수준의 코드 탐색과 디버깅

타입스크립트는 코드에 목적을 명시하고 목적에 맞지 않는 타입의 변수나 함수들에서 에러를 발생시켜 버그를 사전에 제거합니다. 또한 코드 자동완성이나 실행 전 피드백을 제공하여 작업과 동시에 디버깅이 가능해 생산성을 높일 수 있습니다. 실제로 한 연구에 따르면 모든 자바스크립트 버그의 15%가 사전에 타입스크립트로 감지할 수 있다고 한다.

2.자바스크립트 호환

타입스크립트는 자바스크립트와 100% 호환됩니다. 따라서 프론트엔드 또는 백엔드 어디든 자바스크립트를 사용할 수 있는 곳이라면 타입스크립트도 쓸 수 있습니다. 타입스크립트는 앱과 웹을 구현하는 자바스크립트와 동일한 용도로 사용 가능하며 서버 단에서 개발이 이루어지는 복잡한 대형 프로젝트에서도 빛을 발한다.

3.강력한 생태계

타입스크립트는 그리 오래되지 않은 언어임에도 불구하고 강력한 생태계를 가지고 있다. 대부분의 라이브러리들이 타입스크립트를 지원하며 마이크로소프트의 비주얼 스튜디오 코드(VSCode)를 비롯해 각종 에디터가 타입스크립트 관련 기능과 플러그인을 지원한다.

4.점진적 전환 가능

기존의 자바스크립트 프로젝트를 타입스크립트로 전환하는데 부담이 있다면 추가 기능이나 특정 기능에만 타입스크립트를 도입함으로써 프로젝트를 점진적으로 전환할 수 있다. 자바스크립트에 주석을 추가하는 것에서부터 시작해 시간이 지남에 따라 코드베이스가 완전이 바뀌도록 준비 기간을 가질 수 있다.

하지만, 새로운 프로그래밍 언어에 대한 러닝 커브(Learning Curve), 상대적으로 낮은 가독성, 코드량 증가 등의 이유로 타입스크립트 사용을 망설이는 개발자가 많다. 프로젝트 성격에 따라 타입스크립트를 사용할지 결정하면 된다. 프로젝트의 규모가 크고 복잡할수록, 유지보수가 중요한 장기 프로젝트일수록 타입스크립트의 이점이 부각될 것이다.


TypeScript 컴파일러 설치 및 사용

TypeScript 설치하는 방법은 크게 두 가지가 있다.

  • Node.js의 npm으로 설치하기
$ npm install -g typescript

설치 완료 후 TypeScript 설치 버전을 확인한다.

$ tsc -v
Version 4.6.3


  • TypeScript의 Visual Studio 플러그인 설치하기 Visual Studio 2017과 Visual Studio 2015 Update 3는 기본적으로 Typescript를 포함하고 있다.


TypeScript 컴파일러(tsc)는 TypeScript 파일(.ts)을 자바스크립트 파일로 트랜스파일링한다.

컴파일은 일반적으로 소스 코드를 바이트 코드로 변환하는 작업을 의미한다. TypeScript 컴파일러는 TypeScript 파일을 자바스크립트 파일로 변환하므로 컴파일보다는 트랜스파일링(Transpiling)이 보다 적절한 표현이다.


아래와 같이 TypeScript를 작성해서 javascirpt 파일로 컴파일 해보자. TypeScript 파일의 확장자는 .ts이다.

  • 웹 브라우저는 오직 자바스크립트 파일만 읽을 수 있으므로 컴파일 될 test.js 파일을 import 해논다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
</body>
<script src="./test.js"></script>
</html>
  • 아래와 같이 타입스크립트를 작성하고 test.ts로 파일을 생성한다.
const Person = (name: string) => {
    return `제 이름은 ${name}입니다.`;
};

let userName: string = '서성식';

console.log(Person(userName));


작성한 test.ts 타입스크립트 파일을 자바스크립트 파일로 트랜스파일링 해보자. 이때 확장자 .ts는 생략할 수 있다.

$ tsc test

실행 결과, 같은 디렉터리에 자바스크립트 파일(test.js)이 아래와 같이 생성된다.

var Person = function (name) {
    return "\uC81C \uC774\uB984\uC740 ".concat(name, "\uC785\uB2C8\uB2E4.");
};
var userName = '서성식';
console.log(Person(userName));

이때 트랜스파일링된 test.ts의 자바스크립트 버전은 ES3이다. 그 이유는 TypeScript 컴파일 타겟이 ES3로 기본셋팅 되어있기 때문이다. 만약, 자바스크립트 버전을 변경하려면 --target 또는 -t 옵션을 사용한다.


현재 TypeScript에서 제공하는 자바스크립트 버전은

  • ES3(기본), ES5, ES2015, ES2016, ES2017, ES2018, ES2019, ESNEXT

ES6 버전으로 트랜스파일링을 실행하려면 아래와 같이 옵션을 추가한다.

$ tsc test -t ES2015
const Person = (name) => {
    return `제 이름은 ${name}입니다.`;
};
let userName = '서성식';
console.log(Person(userName));


트랜스파일링이 성공하여 자바스크립트 파일이 되었을 것이다. Node.js REPL을 이용해 트랜스파일링된 test.js를 실행해보자.

$ node test
제 이름은 서성식입니다.


컴파일 설정 파일(tsconfig.json)

매번 TypeScript 옵션을 지정하는 것은 번거로운 일이다. 명령어를 매번 입력하지 않고 보다 쉽게 사용하려면 컴파일 설정 파일(tsconfig.json)을 생성하여 사용하는 것이 편리하다. tsconfig.json는 웹팩, 바벨, 린트 등의 설정 파일과 동일하게 프로젝트 루트 디렉토리에 생성해야 된다.

1.tsconfig.json 생성

직접 파일을 생성하는 방법도 있지만 명령어를 통해 생성하는 방법도 있다.

tsc --help를 실행해보면 tsc --init이 작업 디렉토리에 tsconfig.json를 생성한다고 되어 있다.

$ tsc --help
tsc: The TypeScript Compiler - Version 4.6.3                                                                            
                                                                                                                     TS 
COMMON COMMANDS

  tsc
  Compiles the current project (tsconfig.json in the working directory.)

  tsc app.ts util.ts
  Ignoring tsconfig.json, compiles the specified files with default compiler options.

  tsc -b
  Build a composite project in the working directory.

  tsc --init
  Creates a tsconfig.json with the recommended settings in the working directory.

  tsc -p ./path/to/tsconfig.json
  Compiles the TypeScript project located at the specified path.

  tsc --help --all
  An expanded version of this information, showing all possible compiler options

  tsc --noEmit
  tsc --target esnext
  Compiles the current project, with additional settings.

tsc --init 명령어를 아래와 같이 실행해보자.

$ tsc --init

Created a new tsconfig.json with:                                                                                       
                                                                                                                     TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig.json

명령어를 통해 생성된 tsconfig.json 설정 코드는 다음과 같다.
기본 값만 설정되어 있고 나머지 옵션들은 주석처리 되어 있다. 컴파일 옵션의 각 설정은 변역된 주석을 참고하길 바란다. 컴파일 설정 가능한 모든 옵션은 tsc CLI Options 에서 확인할 수 있다.

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Enable incremental compilation */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./",                          /* Specify the folder for .tsbuildinfo incremental compilation files. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "es2016",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
    // "reactNamespace": "",                             /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */

    /* Modules */
    "module": "commonjs",                                /* Specify what module code is generated. */
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    // "baseUrl": "./",                                  /* Specify the base directory to resolve non-relative module names. */
    // "paths": {},                                      /* Specify a set of entries that re-map imports to additional lookup locations. */
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like `./node_modules/@types`. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "resolveJsonModule": true,                        /* Enable importing .json files */
    // "noResolve": true,                                /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */

    /* Emit */
    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
    // "outDir": "./",                                   /* Specify an output folder for all emitted files. */
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like `__extends` in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing `const enum` declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true,                             /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true,            /* Ensure that casing is correct in imports. */

    /* Type Checking */
    "strict": true,                                      /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied `any` type.. */
    // "strictNullChecks": true,                         /* When type checking, take into account `null` and `undefined`. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when `this` is given the type `any`. */
    // "useUnknownInCatchVariables": true,               /* Type catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when a local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true                                 /* Skip type checking all .d.ts files. */
  }
}

2.주의

tsc 명령어 뒤에 파일명을 지정하면 tsconfig.json이 무시되므로 주의해야 한다.

$ tsc test # 파일을 지정했으므로 tsconfig.json이 무시된다.

tsconfig.json을 적용하려면 아래와 같이 실행한다. 단 파일명을 지정하지 않으면 프로젝트 폴더 내의 모든 TypeScript 파일을 대상으로 컴파일이 적용된다.

tsc

3.include/exclude 설정

컴파일에 포함할 디렉토리/파일 경로를 설정하거나 제외할 수 있다.
포함/제외할 각 항목에는 glob 패턴을 사용하여 표기할 수 있다.

글로브(glob) 패턴은 와일드카드 문자로 여러 파일 이름의 집합을 지정한다. 이를테면 유닉스 명령어 mv *.txt textfiles/은 현재 디렉터리의 .txt로 끝나는 이름의 모든 파일을 textfiles 디렉터리로 이동(mv)시킨다. 여기에서 *는 모든 문자열을 가리키는 와일드카드이고 *.txt는 글로브 패턴이다.

와일드카드 문자 설명 예시 일치 미일치
* 없는 것을 포함한 어떠한 수의 문자라도 일치 Law* / Law Law, Laws, 또는 Lawyer / Law, GrokLaw, 또는 Lawyer GrokLaw, La, 또는 aw / La, 또는 aw
? 어떠한 하나의 문자를 일치 ?at Cat, cat, Bat 또는 bat at
[abc] 대괄호 안의 하나의 문자를 일치 [CB]at Cat 또는 Bat cat 또는 bat
[a-z] 대괄호 안의 범위에 속하는 하나의 문자를 일치 (로케일에 따라 다름) Letter[0-9] Letter0, Letter1, Letter2 … Letter9 Letters, Letter 또는 Letter10


  • include 디렉토리안에 있는 타입스크립트 파일들만 컴파일한다.
{
  "include": ["include/*"],
  "compilerOptions": {...}
}
.
├── exclude                 ⨯
│   └── exclude.ts          ⨯
├── include                 ✓
│   ├── include.ts          ✓
│   └── test.ts             ✓
├── index.html
├── package.json
└── tsconfig.json

컴파일을 실행해보면 아래와 같이 include에 포함된 것들만 .js로 변환됐다.

$tsc

image

4.파일의 허용 목록을 직접 지정

만약 파일을 찾을 수 없으면 오류를 발생한다.

아래와 같이 include/include.ts 파일만 직접 지정하면 해당 파일만 컴파일 된다.

{
  "compilerOptions": {...},
  "files": ["include/include.ts"]
}

image