Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1,60 @@
<!-- 정리할 내용을 작성해주세요. -->
### 런타임과 컴파일타임
---
프로그래밍 언어는 일반적으로 `고수준`과 `저수준`으로 구분됨.
- 고수준 : 사람이 이해하기 쉬운 형식으로 작성된 언어
- 저수준 : 컴퓨터가 이해하기 쉬운 형식으로 작성된 언어

js는 대표적인 고수준 언이이며, 컴파일러에 의해서 저수준 언어로 변역되어서 실행됨.

개발자가 소스코드를 작성하고, 해당 코드는 컴파일러에 의해서 기계어 코드로 변환되어 실행이 가능한 프로그램이 되는데, 이를 `컴파일타임`이라고 함. <br />
> [!NOTE]
> **컴파일타임** <br />
> 소스코드가 컴파일 과정을 거쳐서 컴퓨터가 이해할 수 있는 기계어로 바뀌어 실행될 수 있는 프로그램이 되는 괴정.

소스코드가 컴파일이 되어 기계어 코드가 되면 프로그램이 메모리에 적재되어서 실행되는데, 이 시간을 `런타임`이라고 함.
> [!NOTE]
> **런타임** <br />
> 컴파일 과정을 마친 응용 프로그램이 사용자에 의해 실행되는 과정.

<br />

### js런타임
---
js 런타임은 js가 실행되는 환경을 말함.(대표적으로 `브라우저`, `nodeJS`가 있음)<br />
런타임은 다양한 구성요소로 이루어져 있음(js엔진, webAPI, 이벤트루프, 콜백큐 등등..)
<img src="../../assets/CH06/js_runtime.png" />
-> js런타임 구성요소

<br />

### 타입스크립트의 컴파일
---
일반적으로 **"컴파일"** 은 고수준 언어에서 저수준 언어로 변환되는 과정을 가리킴. <br />

타입스크립트는 tsc라는 컴파일러를 통해서 js코드로 변환됨. 하지만 ts는 고수준 언어에서 고수준 언어로(ts to js) 변환되는 것 이기 떄문에 컴파일이 아닌, `트랜스파일` 또는 `source-to-source compiler`라고도 함.

> [!NOTE]
> 트랜스파일의 다른 예시로 C/C++ 코드를 js로 변환하는 `Emscripten`과, `Babel`을 들수있음.

타입스크립트는 `.ts` 파일을 찾아내서 컴파일한 뒤 `.js`파일로 만들어냄.
이때 tsc는 소스코드를 해석해서 `AST(추상 구문 트리)`를 만들고 이후 타입 확인을 거친 뒤에 js코드인 `결과 코드`를 생성함. <br/>
tsc가 ts코드를 컴파일해서 프로그램이 실행되기 까지의 과정은 아래와 같음.

<ol style="font-weight: bold">
<li>타입스크립트 소스코드를 AST(추상 구문 트리)로 만듬 (tsc)</li>
<li>타입 검사기가 AST를 확인해서 타입을 확인 함 (tsc)</li>
<li>타입스크립트 AST를 JS소스코드로 변환함(tsc)</li>
<li>js소스코드를 AST로 만듦 (런타임)</li>
<li>AST가 바이트 코드로 변환 됨 (런타임)</li>
<li>런타임에서 바이트 코드가 실행됨 (런타임)</li>
</ol>

> [!NOTE]
> **AST(추상 구문 트리)** <br/>
> 컴파일러가 소스코드를 해석하는 과정에서 생성되는 데이터 구조. <br />
> 컴파일러는 `어휘적 분석`과, `구문 분석`을 통해 소스코드를 노드 단위의 트리 구조로 형성함. 이를 AST라고 함.

<br/>

타입스크립트의 타입은 최종적으로 만들어지는 프로그램에는 아무런 영향을 주지 않음. 또한 ts는 컴파일 타임에 타입을 검사하기 때문에 에러가 발생하면 프로그램이 실행되지 않음. <br />
이러한 특징 떄문에 ts를 `정적 타입 검사기`라고 부름.
Original file line number Diff line number Diff line change
@@ -1 +1,85 @@
<!-- 정리할 내용을 작성해주세요. -->
앞서 우리는 타입스크립트 컴파일 과정의 전반적인 흐름에 대해 살펴보았음. <br />
이번 절에서는 tsc의 주요 역할에 대해서 살펴보자

<br />

### 코드 검사기로서의 tsc
---
tsc는 코드 타입에 오류가 없는지를 확인함.<br />
ts는 컴파일타임에 코드 타입을 확인하기 때문에 코드를 실행하지 않고도, 에러가 있다는 것을 바로알 수 있음. 따라서 ts는 정적으로 코드를 분석하여 에러를 검출하며, 코드를 실행하기 전에 에러를 사전에 알려줌.<br />

아래 예시를 살펴보자

```js
// js
const dev = {
work() {
console.log('work');
}
}

dev.sleep(); // ✅
```
해당 코드는 js였다면 코드를 작성하는 시점에서는 에러가 발생하지 않음. 그러나, 실제 실행을 하면 에러가 발생함. <br/>
같은 코드를 ts로 작성하게 되면 코드를 실행하기 전에 에러를 발견해서 알려줌

```ts
// ts
const dev = {
work() {
console.log('work');
}
}

dev.sleep(); // Error! Property 'sleep' does not exist on type '{ work(): void; }'
```
이처럼 ts는 js였다면 런타임에서 발견할 수 있는 에러를 컴파일 타임에 발견해서 실행과정에서 발생할 수 있는 문제를 방지함.

tsc는 <a href="https://github.com/microsoft/TypeScript/blob/main/src/compiler/binder.ts">tsc binder</a>를 사용해서 타입 검사를 하며, 컴파일 타임에 오류를 발견함. <br/>
이후 타입 검사를 거쳐서 AST를 js코드로 변환함. (tsc binder는 ts깃헙에 정의되어져 있음)

<br/>

### 코드 변환기로서의 타입스크립트 컴파일러
---
tsc는 타입검사를 한 뒤에, 구버전의 js로 트랜스파일을 함. 이것이 tsc의 두번째 역할임. <br />
tsc의 `target`옵션을 사용해서 특정 버전의 js소스코드로 변환할 수 있음.
<img src="../../assets/CH06/transfile_target.png" />

tsc는 타입 검사를 수행한 후 코드 변환을 시작하는데, 이때 타입 오류가 있더라도 일단 컴파일을 진행함. <br/>
ts코드가 js코드로 변환되는 과정은 타입 검사와 독립적으로 이루어지기 때문임.
따라서 ts코드의 타이핑이 잘못되어서 발생하는 에러는 js를 실행하는 과정에서 런타임 에러로 처리 됨.
```ts
const naming: string = 'zig';
const age: number = 'zig';
```
-> 해당 코드는 타입 에러가 있지만 트랜스파일은 진행 됨.

```ts
// 컴파일 이후 js소스코드
"use strict";

const naming = 'zig';
const age = 'zig';
```

ts를 컴파일하게 되면 순수js만 남고, 런타임에서는 타입 검사를 할수없기 때문에 주의해야 하는 경우도 있음.
```ts
interface Name {
name: string;
}

function getName(name: Name) {
if (name instanceof Name) { // Error! 'Name' only refers to a type, but is being used as a value here
// ...
}
}
```
위 코드는 `instanceof`는 런타임에 실행되지만, `Name`은 타입이기 때문에 런타임은 해당 코드를 이해하지 못함.
정리하자면 tsc의 역할은 크게 2가지로 볼수 있음.
- **코드의 타입 오류검사**
- **최신버전의 ts나 js를 구버전의 js로 트랜스파일**

> [!TIP]
> ***그러면 바벨과는 뭐가 다른거죠?*** <br />
> 바벨은 타입 검사를 하지 않고, 최신버전의 js코드를 구버전의 js로 컴파일 하는것이 주된 역할임.
Original file line number Diff line number Diff line change
@@ -1 +1,112 @@
<!-- 정리할 내용을 작성해주세요. -->
여기서는 실제 tsc의 구성 요소를 훑어보며 tsc의 동작 방식을 톮아보자<br/>

컴파일러는 하나의 프로그램으로, 이를 구현한 소스파일이 존재함.
타입스크립트의 공식문서에서도 찾아볼 수 있음.<br />
<a href="https://github.com/microsoft/TypeScript/tree/main/src/compiler">-> 링크</a>

tsc는 아래와 같은 5가지 단계를 거쳐서 소스 변환을 진행함.
<img src="../../assets/CH06/tsc_complie_process.png" />

<br/>

### 0. 프로그램
---
컴파일러는 tsconfig에 명시된 컴파일 옵션을 기반으로 `'tsc'`라는 명령어로 실행됨. <br/>
우선적으로 컴파일 과정을 관리하는 컴파일 객체가 생성되고, 해당 객체는 컴파일을 할 `ts소스파일`과, 해당 파일에서 `import`하는 파일들을 불러오는데 이때 가장 첫번재로 불러온 파일을 기준으로 컴파일을 시작함.

<br/>

### 1. 스캐너
---
ts코드를 js로 변환하기 위한 첫번째 단계는 `스캐너`임.<br />
스캐너는 ts소스 파일을 어휘적으로 분석해서 `토큰`을 생성함. 즉, 소스코드를 작은 단위로 나누어서 의미있는 토큰으로 변환하는 작업을 수행함. <br />
```ts
const name = 'seongho';
```
예를 들어서 위와 같은 코드는 아래처럼 `토큰화` 됨.
<img src="../../assets/CH06/tokenizing.png" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미지 자료까지 만들어주셨군요 👍


<br />

### 2. 파서
---
스캐너가 소스파일을 토큰으로 나눠주면 파서는 해당 토큰을 이용해서 `AST`를 생성함. <br/>
`AST`는 컴파일러가 동작하는게 가장 핵심 기반이 되는 자료구조로써, 소스코드를 트리형태의 구조로 표현함. AST의 최상위 노드는 타입스크립트 소스파일이며, 마지막 리프노드는 `EOF 토큰`으로 구성됨. <br/>
파서는 토큰을 이용해서 `구분적 분석(Syntax analysis)`을 수행함. <br/>
AST의 각각의 노드는 `코드상의 위치`, `구문 종류`, `코드 내용`과 같은 정보를 담고있음.<br/>
예를 들어서 토큰에 `()`에 해당하는 토큰이 있을 경우, AST를 생성하는 과정에서 해당 토큰이 **함수의 호출**인지, **함수의 인자**인지 또는 **그룹 연산자**인지가 결정 됨.

예시를 봐보자

<img src="../../assets/CH06/typescript_ast.png" />

-> 해당 이미지는 `AST`와 함수의 이름인 `getName`에 해당하는 노드에 대한 정보를 보여주고 있음.

<br />

### 3. 바인더
---
바인더의 주요 역할은 ***체커 단계(4단계)에서 타입 검사를 할 수 있도록 기반을 마련하는 것*** 임. 바인더는 타입 검사를 위해서 `심볼`이라는 데이터 구조를 생성함.<br />
심볼은 이전 단계의 AST에서 선언된 타입의 노드 정보를 저장함. 심볼의 구성은 아래와 같음.
```ts
export interface Symbol {
flags: SymbolFlags;
escapeName: string;
declarations?: Declaration[];
// ...
}
```

<br />

여기서 `flags`는 AST에서 선언된 타입의 노드 정보를 저장하는 식별자임.
```ts
// flag에 해당하는 SymbolFlags타입\
export const enum SymbolFlags {
None = 0,
FunctionScopedVariable = 1 << 0, // Variable(var) or parameter
BlockScopedVariable = 1 << 1, // a block-scoped variable (let, 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
// ...
}
```

<br />

`declaration`필드는 AST노드의 배열 형태를 보임.<br />

<img src="../../assets/CH06/symbol.png" />

즉, 바인더는 `심볼`을 생성하고, 해당 심볼과 그에 대응하는 AST노드를 연결하는 역할을 수행함.

<br />

### 4. 체커와 이미터
---
- `check`: 확인하다
- `emit`: 내보내다 또는 방출하다

`체커`는 파서가 생성한 `AST`와 `바인더`가 생성한 `심볼`을 활용해서 타입 검사를 수행함.

체커의 주요 역할은 AST를 탐색하면서 심볼 정보를 불러와 주어진 소스 파일에 대해서 타입 검사를 진행하는 것임. 체커의 타입 검사는 다음 단계인 이미터 단계에서 실행 됨. <br/>
checker.ts의 getDiagnostics 함수를 사용해서 타입을 검증하고, 에러가 있다면 어떤 에러를 띄울지를 저장함.

이미터는 `타입스크립트 소스파일`을 `js소스파일`과, `타입 선언 파일d(d.ts)`로 나눠서 생성함. 이미터는 ts파일을 변환하는 과정에서 tsconfig파일을 불러오고, 체커를 통해서 코드에 대한 타입 검증 정보를 가져옴. <br />

이후 emit이라는 의미에 맞게 `emitter.ts` 내부의 `emitFiles()` 함수를 사용해서 코드 변환을 진행함.

tsc의 컴파일 과정을 정리하자면 아래와 같음.
<ol
style="font-weight: bold; font-style: italic;"
>
<li>'tsc'라는 명령어로 프로그램 객체를 생성해서 컴파일을 시작함</li>
<li>스캐너가 소스코드를 토큰으로 분해함</li>
<li>파서는 해당 토큰을 기반으로 AST를 만듦</li>
<li>바인더는 해당 AST를 기반으로 각각의 노드에 대한 심볼을 생성함</li>
<li>체커는 AST를 순회하며 각 노드에 상응하는 심볼 정보를 가지고 타입 검사를 진행함</li>
<li>이미터를 사용해서 js파일로 변환함</li>
</ol>
Loading