처음부터 배우는 TypeScript

처음부터 배우는 TypeScript (TypeScript from Scratch)

첫 프로그래밍 언어로 TypeScript를 골랐다니 축하해요 — 이미 좋은 선택을 하고 있는 거예요! 아마 TypeScript가 JavaScript의 "맛(favor)" 또는 "변형(variant)"이라는 얘기를 들어봤을 거예요. TypeScript(TS)와 JavaScript(JS)의 관계는 현대 프로그래밍 언어들 사이에서도 꽤 독특하니, 이 관계를 이해하는 게 TypeScript가 JavaScript에 무엇을 더하는지 이해하는 데 큰 도움이 될 거예요.

출처: TypeScript 핸드북

JavaScript란 무엇인가? 간단한 역사

JavaScript(ECMAScript라고도 불러요)는 브라우저용 간단한 스크립트 언어로 시작했어요. 만들어진 당시에는 웹 페이지에 삽입된 짧은 코드 조각에 쓰일 것으로 예상됐고, 수십 줄만 넘게 코드를 쓰는 것도 좀 이례적이었죠. 그래서 초기 웹 브라우저는 그런 코드를 꽤 느리게 실행했어요. 하지만 시간이 흐르면서 JS는 점점 더 인기를 얻었고, 웹 개발자들은 JS로 인터랙티브한 경험을 만들기 시작했어요.

웹 브라우저 개발자들은 이렇게 늘어난 JS 사용에 대응해 실행 엔진을 최적화하고(동적 컴파일) 할 수 있는 일을 확장했으며(API 추가), 이는 다시 웹 개발자들이 JS를 더 많이 쓰게 만들었어요. 현대 웹사이트에서 여러분의 브라우저는 수십만 줄에 달하는 애플리케이션을 자주 실행하고 있어요. 이게 바로 "웹"의 길고 점진적인 성장이에요. 단순한 정적 페이지의 네트워크에서 시작해, 온갖 종류의 풍부한 애플리케이션 플랫폼으로 진화한 거죠.

여기에 더해 JS는 브라우저 밖에서도 쓰일 만큼 인기를 얻었어요. 예를 들어 node.js로 JS 서버를 구현하죠. JS의 "어디서나 실행되는" 성격은 크로스 플랫폼 개발에 매력적인 선택지가 돼요. 요즘엔 전체 스택을 오직 JavaScript만으로 프로그래밍하는 개발자도 많아요!

요약하면, 빠른 사용을 위해 설계된 언어가, 수백만 줄의 애플리케이션을 작성하는 완전한 도구로 성장한 거예요. 모든 언어에는 저마다의 괴팍함 — 이상하고 놀라운 점들 — 이 있는데, JavaScript의 미천한 시작 덕분에 그런 게 아주 많아요. 몇 가지 예를 볼게요:

  • JavaScript의 동등 연산자(==)는 피연산자를 강제 변환(coerce) 해서 예상 밖의 동작을 만들곤 해요:

    if ("" == 0) {
      // It is! But why??
    }
    if (1 < x < 3) {
      // True for *any* value of x!
    }
    
  • JavaScript는 존재하지 않는 프로퍼티에 접근하는 것도 허용해요:

    const obj = { width: 10, height: 15 };
    // Why is this NaN? Spelling is hard!
    const area = obj.width * obj.heigth;
    

대부분의 프로그래밍 언어는 이런 종류의 오류가 발생하면 에러를 던져요. 어떤 언어는 코드가 실행되기 전, 컴파일 단계에서 에러를 내기도 해요. 작은 프로그램을 작성할 때는 이런 괴팍함이 짜증 나지만 감당할 만한 수준이에요. 하지만 수백 줄, 수천 줄의 애플리케이션을 작성할 때는 이런 끊임없는 놀라움이 심각한 문제가 돼요.

TypeScript: 정적 타입 검사기

앞서 어떤 언어들은 그런 버그가 있는 프로그램을 아예 실행하지 못하게 한다고 했죠. 코드를 실행하지 않고 오류를 감지하는 걸 정적 검사(static checking) 라고 불러요. 그리고 다루는 값의 종류 를 바탕으로 무엇이 오류이고 아닌지 판단하는 걸 정적 타입 검사라고 해요.

TypeScript는 실행 전에 프로그램의 오류를 검사하고, 그 기준이 값의 종류 이기 때문에 정적 타입 검사기 가 돼요. 예를 들어 위 마지막 예시는 obj타입 때문에 오류가 있어요. TypeScript가 찾아낸 오류는 다음과 같아요:

// @errors: 2551
const obj = { width: 10, height: 15 };
const area = obj.width * obj.heigth;

JavaScript의 타입이 있는 슈퍼셋

그렇다면 TypeScript는 JavaScript와 어떤 관계일까요?

문법 (Syntax)

TypeScript는 JavaScript의 슈퍼셋 인 언어예요. 그래서 JS 문법은 곧 유효한 TS 문법이에요. 문법이란 프로그램을 만들기 위해 텍스트를 적는 방식을 말해요. 예를 들어 다음 코드는 )가 빠졌기 때문에 문법 오류가 있어요:

// @errors: 1005
let a = (4

TypeScript는 어떤 JavaScript 코드도 문법 때문에 오류로 간주하지 않아요. 이 말은 작동하는 JavaScript 코드라면 무엇이든, 정확히 어떻게 쓰여 있든 신경 쓸 필요 없이 TypeScript 파일에 넣을 수 있다는 뜻이에요.

타입 (Types)

하지만 TypeScript는 타입이 있는 슈퍼셋이에요. 즉 서로 다른 종류의 값이 어떻게 사용될 수 있는지에 대한 규칙을 덧붙인다는 뜻이죠. 앞서 나온 obj.heigth 오류는 문법 오류가 아니에요. 어떤 종류의 값(타입)을 잘못된 방식으로 사용하는 오류인 거예요.

또 다른 예로, 다음은 브라우저에서 실행할 수 있고 실제로 값을 로그로 출력하는 JavaScript 코드예요:

console.log(4 / []);

이 문법적으로 유효한 프로그램은 Infinity를 로그로 출력해요. 하지만 TypeScript는 숫자를 배열로 나누는 걸 말이 안 되는 연산으로 보고 에러를 내요:

// @errors: 2363
console.log(4 / []);

혹시 정말로 숫자를 배열로 나누려 했을 수도 있겠죠. 그냥 무슨 일이 일어나는지 보려고요. 하지만 대부분의 경우 이건 프로그래밍 실수예요. TypeScript의 타입 검사기는 올바른 프로그램은 통과시키면서도 가능한 한 많은 흔한 오류를 잡아내도록 설계됐어요. (나중에 TypeScript가 코드를 얼마나 엄격하게 검사할지 설정하는 옵션들에 대해서도 배울 거예요.)

JavaScript 파일에서 TypeScript 파일로 코드를 옮기면, 코드가 어떻게 쓰였는지에 따라 타입 오류 를 보게 될 수도 있어요. 이런 오류는 코드의 진짜 문제일 수도 있고, TypeScript가 지나치게 보수적이어서 생긴 것일 수도 있어요. 이 가이드 전반에서 이런 오류를 없애기 위해 다양한 TypeScript 문법을 추가하는 방법을 보여줄게요.

런타임 동작 (Runtime Behavior)

TypeScript는 JavaScript의 런타임 동작 을 보존하는 프로그래밍 언어이기도 해요. 예를 들어 JavaScript에서 0으로 나누기는 런타임 예외를 던지는 대신 Infinity를 만들어요. 원칙적으로 TypeScript는 JavaScript 코드의 런타임 동작을 절대 바꾸지 않아요.

이는 JavaScript에서 TypeScript로 코드를 옮겨도, TypeScript가 그 코드에 타입 오류가 있다고 생각하더라도 보장된 방식으로 동일하게 실행된다는 뜻이에요.

JavaScript와 동일한 런타임 동작을 유지하는 것은 TypeScript의 근본적인 약속이에요. 두 언어 사이를 프로그램이 멈추게 할 수 있는 미묘한 차이를 걱정하지 않고 쉽게 오갈 수 있기 때문이죠.

지워지는 타입 (Erased Types)

대략적으로 말하면, TypeScript 컴파일러가 코드를 검사하고 나면 타입을 지워서(erase) 결과적으로 "컴파일된" 코드를 만들어요. 이 말은 코드가 컴파일되면 결과물인 순수 JS 코드에는 타입 정보가 없다는 뜻이에요.

이 말은 또한 TypeScript가 추론한 타입을 바탕으로 프로그램의 동작 을 절대 바꾸지 않는다는 뜻이기도 해요. 결론적으로, 컴파일하는 동안 타입 오류를 볼 수는 있지만, 타입 시스템 자체는 프로그램이 실행될 때 어떻게 동작하는지와는 관계가 없어요.

마지막으로, TypeScript는 추가적인 런타임 라이브러리를 제공하지 않아요. 여러분의 프로그램은 JavaScript 프로그램과 같은 표준 라이브러리(또는 외부 라이브러리)를 사용하므로, 배울 TypeScript 전용 프레임워크가 따로 없어요.

JavaScript와 TypeScript 배우기

"JavaScript를 배워야 하나요, TypeScript를 배워야 하나요?"라는 질문을 자주 봐요.

답은, JavaScript 없이 TypeScript를 배울 수 없다는 거예요! TypeScript는 문법과 런타임 동작을 JavaScript와 공유하므로, JavaScript에 대해 배우는 모든 것이 동시에 TypeScript를 배우는 데 도움이 돼요.

프로그래머가 JavaScript를 배울 수 있는 자료는 아주, 아주 많아요. TypeScript를 작성한다고 해서 이런 자료를 무시하면 안 돼요. 예를 들어 StackOverflow에서 javascript 태그가 붙은 질문은 typescript보다 약 20배 많은데, javascript 질문 모두 가 TypeScript에도 적용돼요.

"TypeScript에서 목록을 정렬하는 법" 같은 걸 검색하고 있다면 기억하세요: TypeScript는 컴파일 타임 타입 검사기가 달린 JavaScript의 런타임이에요. TypeScript에서 목록을 정렬하는 방법은 JavaScript에서 정렬하는 방법과 같아요. TypeScript를 직접 사용하는 자료를 찾는다면 그것도 좋지만, 일상적인 런타임 작업 수행에 대해 TypeScript 전용 답이 필요하다고 생각하며 자신을 제한하지 마세요.

다음 단계 (Next Steps)

지금까지 일상 TypeScript에서 쓰이는 문법과 도구의 간략한 개요를 살펴봤어요. 여기서부터 다음과 같은 것들을 할 수 있어요:

더 알아보기 (Learn more)