FRONTEND

내 친구 TypeScript를 알아보자

주니어발록 2025. 1. 18. 18:50

새삼... 이미 오래전부터 친하게 지내던 TypeScript 친구가 궁금해졌다. 내가 혹시 너무 무심하게 대했던 건 아닐까? JavaScript와 한 몸처럼 굴러가는 녀석이지만, 정작 어떻게 탄생했고, 왜 쓰는 건지 깊이 생각해본 적이 없다는 걸 깨달았다.

그래서 오늘은 TypeScript가 언제, 어디서, 왜 등장했는지, 그리고 어떤 특징을 갖고 있는지까지 살짝 파헤쳐보려고 한다. JavaScript 역사를 훑어가면서, 왜 이 녀석이 ‘Superset’이라는 이름을 달고 나왔고, 어떻게 우리 개발자들의 삶을 편하게 만들어주는지 천천히 알아보자.


JavaScript, 너부터 알아보자.

Long long time ago.. 내가 옹알이를 하고 있엇을 1990년대이다. 마이크로소프트의 인터넷익스프로러와 넷스케이프 커뮤니케이션즈의 넷스케이프 네비게이터 이 두개가 가장 많이 사용되는 웹 브라우저였다. 

넷스케이프에서는 웹에서 이미지, 플러그인 등 다양한 요소를 보여주고싶어 단 10일만에 JavaScript라는 언어를 만들게 되었다. 마이크로소프트도 이에 질세라 Jscript를 만들었지만, 이렇게 추가된 기능들은 각자의 브라우저에만 동작했고 크로스 브라우징 이슈가 발생했다. 

JavaScript의 발전 속도를 브라우저는 따라가기 힘들었고, 이런 문제를 해결하기 위해 polyfill과 transpile이란 개념이 등장 햇다. 

polifill
브라우저가 지원하지 않는 코드를 사용할 수 있도록 변환한 코드 조각이나 플러그인
ex) core.js, polyfill.io
transpile
최신 버전의 코드를 예전 버전의 코드로 변환 하는 과정
ex) Babel

크로스 브라우징 이슈로 모든 브라우저에 동일하게 동작하는 표준화된 JavaScript의 필요성이 느껴졌다. 

넷스케이프는 컴퓨터 시스템 표준화 기구인 Ecma 인터내셔널에 JavaScript 기술 규격을 제출했고, ECMAScript 표준화가 공식화되었다.

이렇게 JavaScript가 표준화되자 정적 웹사이트에서 동적 웹 어플리케이션으로 전환되고, 서비스 규모가 커짐에 따라 컴포넌트 베이스 개발 방법론이 등장 했다. 

컴포넌트 베이스 개발(Component Based Development, CBD)
재사용할 수 있는 컴포넌트를 개발, 조합해서 하나의 애플리케이션을 만드는 개발 방법론

컴포넌트는 모듈과 유사하게 독립된 기능을 재사용하기위한 목적을 갖고 있지만, 모듈과 달리 런타임 환경에서 독립적으로 배포, 실행 될 수 있다. 컴포넌트는 다른 컴포넌트와의 의존성을 최소화 해야한다. 

JavaScript는 동적 타입 언어다 보니, 서비스가 점점 커지면서 많은 개발자들과 협업을 하는데 불편함을 느끼기 시작했다. 

아래 코드에서 선언된 sumNumber 함수는 두수의 합을 구하기 위한 함수이다. 하지만 string 타입을 넣어서 함수를 사용한 경우에 오류가 발생되는 것이 아닌 잘못된 결과 값이 반환된다. 즉 개발자의 의도와 다르게 동작 할 수 있는것이다.

const sumNumber = (a, b) => {
	return a+b;
};

sumNumber(1,2) // 3 <-- 정상적인 사용과 결과

sumNumber("1", "2") // "12" <-- 잘못된 사용과 잘못된 결과

이와같이 규모가 큰 서비스를 개발하면서 동적 타이핑 시스템은 많은 불편함을 발생 시켰다. 

그래서 TypeScript가 나온건가?

맞다. 이런 JavaScript의 불편함을 개선하고자, 마이크로소프트는 JavaScript의 Superset 언어인 TypeScript를 공개했다.

TypeScript는 JavaScript의 Superset이므로 점진적 도입이 가능하기 때문에 많은 환영을 받았다.

Superset
기존 언어에 새로운 기능과 문법을 추가해서 보완한 것으로, Superset언어는 기존 언어와 호환되고 일반적으로 컴파일러 등으로 기존언어 코드로 변환되어 실행됨. JavaScript 에 Type이라는 레이어를 추가한 템플릿언어, 확장언어라고도 불림

JavaScript는 interpreter 언어니까 TypeScript 너도?

JavaScript는 우리가 알고있다 싶이 run time 환경에서 실행되는 interpreter 언어로, 별도의 컴파일 없이 run time에서 오류가 발생 할 수 있다. 

run time에 interpreter가 코드를 해석하고 실행함
interpreter
코드 해석과 실행하는 도구
런타임
변환된 파일이 메모리에 적재되어 실행되는 시점

그러면 TypeScript도 JavaScript와 같이 interpreter 언어일까? TypeScript가 탄생한 이유는 JavaScrip의 compile time에 run time 에러를 사전에 찾아내기 위함이다. TypeScript를 컴파일하면 타입이 모두 제거된 JavaScript소스만 남게된다. 그러므로 Typescript는 compiler 언어이다.

JavaScript는 interpreter 언어

  • 자바스크립트는 런타임 환경에서 실행되는 인터프리터 언어
  • 브라우저나 Node.js 같은 자바스크립트 엔진에서 자바스크립트 코드를 즉시 실행
  • 별도의 컴파일 단계가 없고, 런타임에서 구문 오류나 타입 오류가 발생

타입스크립트는 Compiler 언어

  • 타입스크립트는 컴파일 타임에 타입 검사를 수행
  • 즉, TypeScript Compiler (tsc)가 타입스크립트 코드를 자바스크립트로 변환(컴파일)하는 과정에서 오류를 감지
  • 하지만, 타입스크립트는 최종적으로 자바스크립트 코드로 변환되기 때문에 브라우저나 Node.js와 같은 런타임에서는 결과적으로 자바스크립트로 실행

타입스크립트는 타입 검사만 하고 런타임에는 영향을 주지 않음

  • 타입스크립트의 타입 시스템은 개발 도구와 컴파일 단계에서만 유효
  • 컴파일이 완료된 후에는 타입 정보가 모두 제거되고 순수한 자바스크립트 코드만 남음
  • 따라서 타입스크립트는 런타임에 타입 검사를 하지 않으며, 실행 단계에서는 자바스크립트와 동일하게 동작

TypeScript의 타입 시스템

TypeScript은 구조적 타이핑이다. 구조적 서브타이핑은 이름이 다른 객체라도 가진 속성이 동일하다면 서로 호환이 가능한 동일한 타입으로 여긴다.

아래 코드와 같이 Cat 타입으로 선언한 cat을 Pet 타입으로 선언한 pet에 할당할 수 있다.

interface Pet {
	name: string;
}

interface Dog {
	name: string;
    age: number
}

let pet: Pet;
let cat: Cat = { name: "cookie", age: 2 };

pet = cat;

대조적으로, Java나, C++ 에서는 명목적 타이핑으로 타입의 이름만으로 구별한다. 명목적 타이핑에서 두변수는 같은 이름의 데이터 타입으로 선언된 경우에만 호환된다. 이런 명목적 타이핑은 타입의 동일성을 확인하는데 구조적 타이핑에 비해 더 안전하다. 

하지만 왜 TypeScript에는 이안전한 구조적 타이핑을 적용하지 않았을까?

JavaScript는 본질적으로 덕 타이핑을 기반으로 한다. TypeScript는 JavaScript의 특징을 그대로 받아들여 객체가 가진 속성을 기반으로 타입을 검사하는 구조적 타이핑을 채택했고, 덕분에 더욱 유연한 타이핑이 가능해졌다.

덕타이핑
"만약 어떤 새가오리처럼 걷고, 헤엄치며 꽥꽥 거리는 소리를 낸다면 나느 그새를 오리라고 부를 것이다."
이 문장은 James Whitcomb Riley라는 미국의 시인이 19세기 후반에 쓴 시나 에세이에서 유래되었다고 알려짐.
이 문장이 현대의 프로그래밍 패러다임에서 덕 타이핑을 설명할 때 자주 인용되며, 프로그래밍 철학의 일부로 자리 잡음

TypeScript의 점진적 타입 확인

TypeScript는 점진적으로 타입을 확인하는 언어이다. compile타임에 타입을 검사하면서 타입 선언 생략도 허용한다. 생략된 타입은 compiler가 암시적 타입 변환으로 any로 추론한다.

이러한 특징 덕분에 JavaScript 코드를 TypeScript코드로 마이그레이션할 때 유용하게 활용할 수 있다.

noImplicitAny 옵션
TypeScript로 코드를 작성할 때 정확한 타이핑을 위한다면 tsconfig파일에서 noImplicitAny 옵션을 true로 설정해두는것이 좋다. 
혹시나 any 타입을 사용한 경우 아래와 같은 오류를 발생한다.

Parameter 'name' implicitly has an 'any' type.

자, 여기까지 TypeScript가 어떻게 태어났고, 왜 필요한지 대략 살펴봤다. 10일 만에 번개처럼 만들어진 JavaScript가 세상에 자리를 잡아가면서, 어느 순간 혼자 감당하기에는 규모가 너무 커졌고, 그 틈새를 파고든 게 바로 TypeScript다.

서비스가 커질수록 협업도 늘어나고, 어디서 어떻게 이상한 버그가 튀어나올지 모르는 게 대규모 JavaScript 프로젝트의 숙명이었다. 그런 부담을 덜어주기 위해, 컴파일 타임에 에러를 잡아주고, IDE 지원까지 빵빵하게 해주니, 개발자 입장에서는 정말 반가울 수밖에 없다.

앞으로도 JavaScript 생태계가 계속 확장될 거고, TypeScript는 그중에서도 사실상 표준처럼 자리를 잡아갈 가능성이 크다. 그만큼 규모가 큰 프로젝트나 협업이 잦은 환경에서는 꼭 한번 써볼 만한 가치가 있다. 다음엔 TypeScript가 자랑하는 다양한 타입들부터, 실제 프로젝트에 적용할 때 유용한 팁들까지 차근차근 살펴보려고 한다.