컴파일타임
: 소스코드가 컴파일 과정을 거쳐 컴퓨터가 인식할 수 있는 기계어코드(바이트 코드)로 변환되어 실행할 수 있는 프로그램이 되는 과정
런타임
: 컴파일 과정을 마친 응용 프로그램이 사용자에 의해 실행되는 과정
자바스크립트가 실행되는 환경을 의미하며, 대표적인 JS 런타임으로 인터넷 브라우저와, Node.js 등이 있다.
JS는 대표적인 인터프리터(interpreter) 언어로 별도의 컴파일 과정이 존재하지 않는다고 알려져 있다.
하지만 엄밀히 말해, JS에서도 컴파일 단계가 존재하며 JS를 해석하고 실행하는 역할을 가진 V8엔진은 때때로 JS코드를 최적화하기위해 컴파일단계를 거친다.
따라서 JS는 기본적으로 인터프리터 언어의 특징을 가지지만, 현대의 JS엔진은 컴파일 언어의 특성을 일부 통합하고 있다.
타입스크립트는 tsc 컴파일러를 통해 자바스크립트 코드로 변환된다.
소스 대 소스 컴파일러(source -to-source compiler)
라고 지칭하기도 한다.트랜스파일의 다른 예시로, JS ES6 이상의 문법을 ES5 이하의 문법으로 변환하는
바벨(Babel)
이 있다.
컴파일과 트랜스파일을 통틀어 컴파일이라고 부르기도 한다.
TS는 .ts 확장자가 붙은 파일을 찾아내 컴파일한 다음, .js 확장자가 붙은 JS파일을 만들어낸다.
AST(Abstract Syntax Tree)
: 컴파일러가 소스코드를 해석하는 과정에서 생성된 데이터 구조.
컴파일러는어휘적 분석(lexical analysis)
과구문 분석(syntax analysis)
을 통해 소스코드를노드 단위의 트리 구조
로 구성한다.
이때 TS 소스코드의 타입은 1 ~ 2단계에서만 사용된다. 3단계부턴 타입을 확인하지 않는다.
앞에서 TS 컴파일 과정의 전반적인 흐름을 살펴봤다. 이 절에선, TS 컴파일러의 주요 역할에 대해 알아본다.
TS는 정적으로 코드를 분석해 에러를 검출하며, 코드 실행 전에 JS 런타임에서 발생할 수 있는 에러를 사전에 알려준다.
const developer = {
work() {
console.log("working...");
},
};
developer.work(); // working...
developer.sleep(); // TypeError: developer.sleep is not a function
이 코드는 JS로 작성할 땐 에러가 발생하지 않지만, 런타임엔 에러가 발생한다.
그러나 같은 코드를 TS로 작성하면, 사전에 에러를 발견해 알려준다.
타입을 검사한 뒤, TS 코드를 각 런타임 환경에서 동작할 수 있도록 구버전의 JS로 트랜스파일한다.
type Fruit = "banana" | "watermelon" | "orange" | "apple" | "kiwi" | "mango";
const fruitBox: Fruit[] = ["banana", "apple", "mango"];
const welcome = (name: string) => {
console.log(`hi! ${name} :)`);
};
"use strict";
var fruitBox = ["banana", "apple", "mango"];
var welcome = function (name) {
console.log("hi! ".concat(name, " :)"));
};
TS 컴파일러가 TS 파일을 JS로 변환한 결과 예시 코드이다.
정리하면, 타입스크립트 컴파일러의 역할을 크게 2가지로 나눌 수 있다.
??? : 타입스크립트의 컴파일러랑 Babel과 다른게 무엇인가요??
같은 점
: 소스코드를 ES5 이하의 JS 코드로 컴파일한다.
다른 점
: tsc는 타입검사를 하지만, Babel은 하지 않는다. Babel은 최신 JS를 낮은 버전으로 컴파일하는 것이 주된 역할이다.
이제 타입스크립트 컴파일러의 구성 요소들을 훑어보며 컴파일러의 동작 방식을 이해하자.
TS 컴파일러는 tsc 명령어로 실행되며, tsconfig.json
에 명시된 컴파일 옵션을 기반으로 수행한다.
TS 소스를 JS로 변환하기 위한 첫 단계는 스캐너이다.
const woowa = "bros";
이렇게 스캐너에 의해 분석된다.
스캐너가 소스 파일을 토큰으로 나눠주면, 파서는 그 토큰을 활용해 AST를 생성한다.
AST
: 컴파일러가 동작하는 데 핵심 기반이 되는 자료구조. 소스코드의 구조를 트리형으로 표현function normalFunction() {
console.log("normalFunction");
}
normalFunction();
위 예제코드는 위와같은 구조로 AST를 구성한다.
바인더의 주요 역할은, 체커(checker) 단계에서 타입 검사를 할 수 있도록 기반을 마련하는 것
export interface Symbol {
flags: SymbolFlags; // Symbol flags
escapedName: string; // Name of symbol
declarations?: Declaration[]; // Declarations associated with this symbol
// 이하 생략...
}
심볼의 인터페이스 일부 코드이다.
flags 필드
는 AST에서 선언된 타입의 노드 정보를 저장하는 식별자이다.// src/compiler/types.ts
export const enum SymbolFlags {
None = 0,
FunctionScopedVariable = 1 << 0, // Variable (var) or parameter
BlockScopedVariable = 1 << 1, // A block-scoped variable (let or const)
Property = 1 << 2, // Property or enum member
EnumMember = 1 << 3, // Enum member
Function = 1 << 4, // Function
Class = 1 << 5, // Class
Interface = 1 << 6, // Interface
// ...
}
declarations 필드
는 AST 노드의 배열 형태를 보인다.위 사진은, 여러 선언 요소에 대한 심볼이다.
체커는 파서가 생성한 AST와 바인더가 생성한 심볼을 활용하여 타입 검사를 수행한다.
참고) 파서의 소스 크기 : 500 KB, 체커의 소스 크기 : 2.7 MB로 전체 컴파일 과정에서 타입 검사가 차지하는 비중이 크다는 것을 짐작할 수 있음
타입스크립트 소스 파일을 변환하는 역할을 한다.
즉, TS 소스를 자바스크립트(js) 파일과 타입 선언 파일(d.ts)로 생성한다.
글 잘 읽었습니다!
한가지 잘못된 점을 발견해서 제보드립니다.
"TS는 고수준 언어가 고수준 언어(JS)로 변환되는 것이기에, 컴파일이 아닌 트랜스파일(Transpile)이라고 부르기도 한다."
이 부분을 "TS는 고수준 언어가 고수준 언어(JS)로 변환되는 것이기에, 트랜스파일(Transpile)이 아닌 컴파일이라고 부르기도 한다."
으로 수정해야 할 것 같아요