우아한 타입스크립트 스터디 6장 - 타입스크립트 컴파일

우아한 타입스크립트 with 리액트 6장 - 타입스크립트 컴파일

개요

6장은 타입스크립트 컴파일러의 동작 원리와 컴파일 과정을 상세히 다룹니다. 타입스크립트가 어떻게 자바스크립트로 변환되는지, 언제 타입 검사가 이루어지는지, 그리고 컴파일러의 내부 구조를 이해할 수 있는 내용으로 구성되어 있습니다.


6.1 자바스크립트의 런타임과 타입스크립트 컴파일

6.1.1 런타임과 컴파일 타임

언어 분류

  • 고수준 언어: 사람이 이해하기 쉬운 형식 (JavaScript, TypeScript)
  • 저수준 언어: 컴퓨터가 이해하기 쉬운 형식 (기계어, 바이트코드)

컴파일 과정

개발자 소스코드 작성 → 컴파일러에 의해 기계어 코드로 변환 → 실행 가능한 프로그램
  • 컴파일 타임: 소스코드가 컴파일 과정을 통해 기계어 코드로 변환되어 실행할 수 있는 프로그램이 되는 과정
  • 런타임: 컴파일이 완료된 프로그램이 메모리에 적재되어 실행되는 시간

6.1.2 자바스크립트의 런타임

자바스크립트 런타임 특징

  • 자바스크립트 실행 환경을 의미 (브라우저, Node.js)
  • 대표적인 인터프리터 언어이지만 내부적으로 컴파일 단계 존재
  • V8 엔진은 성능 최적화를 위해 컴파일 단계를 거쳐 코드를 캐싱

런타임 에러

  • 자바스크립트는 실행 도중 에러가 발생할 수 있음
  • 런타임에서만 타입 오류를 발견할 수 있음

6.2 타입스크립트 컴파일 과정

6.2.1 컴파일과 트랜스파일

타입스크립트 컴파일의 특징

  • 고수준 언어(TS)에서 고수준 언어(JS)로 변환
  • 일반적인 컴파일과 구분하여 **트랜스파일(Transpile)**이라고도 함
  • 하지만 넓은 의미에서 컴파일이라고 통용됨

6.2.2 타입스크립트 컴파일러 동작 과정

전체 컴파일 과정

컴파일 타임 (tsc)

  1. TypeScript 소스코드 → TypeScript AST 변환
  2. 타입 검사기가 AST를 확인하여 타입 검사
  3. TypeScript AST → JavaScript 소스 변환

런타임 4. JavaScript 소스 → JavaScript AST 변환 5. AST → 바이트 코드 변환 6. 런타임에서 바이트 코드 실행

6.2.3 타입 정보의 생명주기

중요한 특징

  • 타입스크립트 소스코드의 타입은 1~2단계에서만 사용됨
  • 타입 정보는 타입 검사에만 사용되고 최종 프로그램에는 영향 없음
  • 컴파일 후에는 타입이 제거되어 순수한 JavaScript만 남음

런타임에서의 타입 제한

interface Square {
  width: number
}

interface Rectangle extends Square {
  height: number
}

type Shape = Square | Rectangle

function calculateArea(shape: Shape) {
  // ❌ 에러: 'Rectangle'은 타입이므로 값으로 사용 불가
  if (shape instanceof Rectangle) {
    return shape.width * shape.height
  } else {
    return shape.width * shape.width
  }
}

6.3 타입스크립트 컴파일러의 구조

6.3.1 컴파일러의 주요 역할

2가지 핵심 기능

  1. 정적 분석을 통해 코드의 타입 오류 검사
  2. 최신 버전의 TS/JS 코드를 구버전 JavaScript로 트랜스파일

6.3.2 컴파일러 5단계

1. 스캐너(Scanner)

  • 타입스크립트 소스 파일을 어휘적으로 분석(lexical analysis)
  • 소스코드를 의미 있는 토큰(token) 단위로 분리
  • 예: let, name, =, "John" 등으로 분리

2. 파서(Parser)

  • 토큰 정보를 이용하여 AST(Abstract Syntax Tree) 생성
  • 구문적 분석(syntax analysis) 수행
  • 실질적인 구조를 노드 단위의 트리 형태로 표현

3. 바인더(Binder)

  • 타입 검사를 위한 기반 마련
  • 심볼(Symbol) 데이터 구조 생성
  • AST의 각 노드에 대응하는 선언된 타입의 노드 정보 저장
export interface Symbol {
  flags: SymbolFlags // Symbol flags
  escapedName: string // Name of symbol
  declarations?: Declaration[] // Declarations associated with this symbol
  // ...
}

4. 체커(Checker)

  • AST 노드를 탐색하면서 심볼 정보를 활용
  • 주어진 소스 파일에 대해 타입 검사 진행
  • 타입 오류 발견 및 보고

5. 이미터(Emitter)

  • 타입스크립트 소스 파일을 **JavaScript 소스 파일(.js)**과 **타입 선언 파일(.d.ts)**로 변환
  • 개발자가 설정한 tsconfig.json 읽어와 설정 적용
  • emitFiles 함수를 사용하여 소스 변환 수행

6.3.3 컴파일 과정 요약

1. tsc 명령어 실행 → 프로그램 객체가 컴파일 과정 시작
2. 스캐너 → 소스 파일을 토큰 단위로 분리
3. 파서 → 토큰을 이용하여 AST 생성
4. 바인더 → AST의 각 노드에 대응하는 심볼 생성
5. 체커 → AST를 탐색하면서 심볼 정보를 활용하여 타입 검사
6. 이미터 → 타입 검사 결과 에러가 없다면 JavaScript 소스로 변환

6.4 AST(Abstract Syntax Tree)

6.4.1 AST의 개념

AST란?

  • 컴파일러가 소스코드를 해석하는 과정에서 생성된 데이터 구조
  • 어휘적 분석과 구문 분석을 통해 소스코드를 노드 단위의 트리 구조로 구성
  • 각 노드는 코드상의 위치, 구문 종류, 코드 내용 등의 정보를 포함

6.4.2 심볼(Symbol) 시스템

심볼의 역할

  • AST의 각 노드에 대응하는 선언된 타입의 노드 정보 저장
  • 타입 검사를 위한 기반 데이터 제공

SymbolFlags 예시

export const enum SymbolFlags {
  None = 0,
  FunctionScopedVariable = 1 << 0,
  BlockScopedVariable = 1 << 1,
  Property = 1 << 2,
  EnumMember = 1 << 3,
  Function = 1 << 4,
  Class = 1 << 5,
  Interface = 1 << 6,
  // ...
}

6.5 타입 검사와 코드 변환의 독립성

6.5.1 독립적인 두 과정

중요한 특징

  • 타입 검사와 코드 변환은 독립적으로 동작
  • 타입 오류가 있어도 컴파일은 진행됨
  • 이는 개발 과정에서 점진적 마이그레이션을 가능하게 함

6.5.2 정적 타입 검사기

타입스크립트의 특성

  • 컴파일 타임에 타입 검사 수행
  • 에러 발생 시 프로그램 실행 방지
  • 정적 타입 검사기(static type checker) 역할

6.6 실무 활용 팁

개발 도구 활용

  • TypeScript Playground: 스캐너, 파서 플러그인으로 컴파일 과정 시각화
  • TypeScript AST Viewer: AST 구조 확인
  • Symbol 확인 플러그인: 심볼 정보 탐색

컴파일 설정 최적화

  • tsconfig.json에서 적절한 target 옵션 설정
  • 타입 검사와 빌드 성능 균형 맞추기
  • 점진적 마이그레이션 전략 수립

핵심 포인트

  1. 타입스크립트는 컴파일 타임에만 타입 정보 활용
  2. 런타임에서는 타입 정보 접근 불가
  3. 컴파일러는 5단계(스캐너→파서→바인더→체커→이미터)로 동작
  4. 타입 검사와 코드 변환은 독립적 과정
  5. AST와 심볼을 통한 정적 분석 수행