Typescript의 역사와 소개
TypeScript의 등장 배경
JavaScript는 1995년 Brendan Eich에 의해 웹 페이지에 간단한 상호작용을 추가하기 위한 스크립트 언어로 탄생했다.
당시에는 폼 유효성 검사나 간단한 애니메이션과 같은 작업을 위한 코드를 작성하는 것이 일반적이었다.
// 1990년대 JavaScript의 일반적인 용례
function validateForm() {
var name = document.forms['myForm']['name'].value;
if (name == '') {
alert('이름을 입력해주세요');
return false;
}
}
그러나 2000년대 후반부터 웹은 급격한 변화를 겪게 됐다. AJAX의 등장, 단일 페이지 애플리케이션(SPA)의 부상, 그리고 Node.js를 통한 서버 사이드 JavaScript의 확산은 JavaScript 코드량과 복잡도를 기하급수적으로 올리게 된 계기가 되었다.
- 런타임 오류의 빈번한 발생: undefined is not a function, cannot read property of undefined
- 코드 탐색과 리팩터링의 어려움: IDE가 타입 정보 없이는 정확한 자동 완성이나 리팩토링 지원을 제공할 수 없음
- 팀 협업의 복잡성: 대규모 팀에서 API 계약과 인터페이스 문서화의 어려움
- 코드 품질 유지의 어려움: 코드 베이스가 커질수록 변경의 영향 범위를 예측하기 어려움
이러한 문제는 JavaScript의 동적 타입 특성에서 비롯되었다.
JavaScript는 유연함을 제공했지만, 그 유연함이 대규모 애플리케이션에서는 오히려 발목을 잡는 요소가 되기도 했다.
TypeScript의 탄생
Microsoft의 Anders Hejlsberg가 이끄는 팀은 2012년 TypeScript 프로젝트를 시작했다. Anders Hejlsberg는 이미 Turbo Pascal, Delphi, C#과 같은 프로그래밍 언어를 설계한 경험이 있는 베테랑 언어 창시자였다.
TypeScript는 본질적으로 JavaScript를 모두 포함하는 확장판(Superset)입니다. 그렇기 때문에 JavaScript는 곧 TypeScript에 속해 있다고 볼 수 있습니다. 이렇게 확장 구조를 갖추고 있기에, TypeScript 툴체인은 JavaScript 파일도 오류 없이 받아들이고, 컴파일이나 코드 분석 같은 추가 기능을 제공할 수 있습니다. 결과적으로 개발자 입장에서는 JS 혹은 TS 어느 쪽을 쓰더라도 TypeScript의 이점들을 활용할 수 있게 됩니다. - Anders Hejlsberg
Because TypeScript is a superset of JavaScript, you could also think of JavaScript as a subset of TypeScript. And that means that our entire TypeScript toolchain is perfectly happy to process JavaScript and provide all the services on top of JavaScript - Anders Hejlsberg (원문)
- JavaScript와의 완벽한 호환성: 모든 JavaScript 코드는 유효한 TypeScript 코드여야 함
- 점진적 타입 시스템(Gradual Typing): 개발자가 필요에 따라 타입을 점진적으로 도입할 수 있어야 함
- 구조적 타이핑(Structural Typing): JavaScript의 덕 타이핑(duck typing) 특성을 존중하는 타입 시스템
- 컴파일 시간 검사, 런타임 영향 없음: 타입 검사는 개발 단계에서만 수행되고, 실행 코드에는 영향을 미치지 않음
이러한 설계 특성은 TypeScript가 '타입이 있는 JavaScript'뿐만 아니라 JavaScript 생태계의 특성을 계승한 언어로 자리매김하는 데 중요한 역할을 했다.
정적 타입 시스템의 이해
동적 타입 vs 정적 타입
프로그래밍에서 타입이란 무엇일까? 타입은 값(value)의 종류와 그 값에 수행할 수 있는 연산을 정의한다.
예를 들어, 숫자 타입의 값에는 연산(2+2=4)을 할 수 있고, 문자열 타입에는 연결(concatenation; '2'+'2'='22') 할 수 있다.
프로그래밍 언어의 타입 시스템은 크게 동적 타입과 정적 타입으로 나눌 수 있다.
동적 타입 시스템 (Dynamic Typing)
- 변수의 타입이 런타임(이하 프로그램 실행 시점)에 결정된다.
- 같은 변수에 다른 타입의 값을 할당할 수 있다.
- 타입 오류가 런타임에 발견된다.
- 예: JavaScript, Python, Ruby
// JavaScript (동적 타입)
let x = 10; // 숫자
x = 'hello'; // 문자열로 변경 가능
x = { prop: 42 }; // 객체로 변경 가능
정적 타입 시스템 (Static Typing)
- 변수의 타입이 컴파일 시간(이하 프로그램 실행 전)에 결정된다.
- 변수는 선언된 타입의 값만 가질 수 있다.
- 타입 오류가 컴파일 시간에 발견된다.
- 예: Java, C++, Rust
// TypeScript (정적 타입)
let x: number = 10;
x = "hello"; // 오류: 'string' 형식은 'number' 형식에 할당할 수 없습니다.
동적 타입 | 정적 타입 |
---|---|
✅ 유연성과 빠른 프로토타이핑 | ✅ 컴파일 시간에 오류 발견 |
✅ 적은 의식적 구조화(boilerplate) | ✅ 더 나은 IDE 지원과 코드 탐색 |
✅ 메타프로그래밍 용이 | ✅ 더 안전한 리팩터링 |
❌ 런타임 잠재 오류 가능성 | ❌ 더 많은 선행 설계 필요 |
❌ IDE 지원 제한적 | ❌ 때로는 유연성 제한적 |
TypeScript의 타입 시스템 철학
TypeScript의 타입 시스템은 몇 가지 중요한 철학적 원칙이 있다.
구조적 타이핑 (Structural Typing)
TypeScript는 이름이 아닌 구조(structure)에 기반한 타입 시스템을 채택했다.
이는 JavaScript의 덕 타이핑(duck typing) 특성과 일치한다.
interface Point {
x: number;
y: number;
}
function printPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}
// 명시적으로 Point 타입이 아니어도, 구조가 일치하면 사용 가능
const myPoint = { x: 10, y: 20 };
printPoint(myPoint); // 정상 작동
이러한 구조적 타이핑은 JavaScript의 유연함을 유지하면서도 타입 안전성을 제공할 수 있다.
3. JavaScript와 TypeScript의 관계
3.1. JavaScript의 상위집합으로서의 TypeScript
TypeScript는 JavaScript의 상위집합(superset)으로 설계되었다.
이는 모든 유효한 JavaScript 코드가 TypeScript 코드로도 유효하다는 것을 의미한다.
TypeScript 컴파일러(tsc)는 TypeScript 코드를 JavaScript로 변환한다.
이 과정에서 타입 정보는 제거되며(타입 소거, Type Erasure), 출력된 JavaScript 코드는 어떤 런타임에서도 실행될 수 있다.
Typescript 코드
function greet(name: string): string {
return `Hello, ${name}!`;
}
Type이 제거된 Javascript 코드
function greet(name) {
return `Hello, ${name}!`;
}
컴파일 과정을 거쳐 타입이 완전히 제거되었다. 이는 TypeScript는 개발 단계에서만 쓰이는 도구라는 점을 방증한다.