[타입스크립트 뽀개기] Why TS?

Jihan·2024년 1월 10일
1
post-thumbnail

JavaScript는 동적 타입 언어(Dynamic Typed Language)이다. 동적 타입 언어란, 변수의 데이터 타입이 런타임에서 결정되며 변수를 선언할 때 명시적으로 타입을 지정할 필요가 없는 언어를 의미한다.

유저의 상호작용에 따라 동적으로 변하는 웹 페이지를 쉽게 다룰 수 있도록 하거나 코드 작성의 간결성과 빠른 테스트 환경을 제공하기 위해 JS는 동적 타입 언어로 설계되었다. 코드 작성에 있어서 변수의 데이터 타입의 캐스팅이 자동으로 이루어지는 JS는 약타입 언어(Weaked Typed Language)로 분류된다.


JavaScript의 강제 형변환(Type Coercion)

Type coercion is the automatic or implicit conversion of values from one data type to another (such as strings to numbers). Type conversion is similar to type coercion because they both convert values from one data type to another with one key difference — type coercion is implicit whereas type conversion can be either implicit or explicit.(MDN)

JS는 일반적인 프로그래밍 언어에서 갖추고 있는 명시적 형변환(타입 캐스팅)인 Type conversion 기능과 약타입 언어의 특징인 암시적(강제) 형변환인 Type coercion 기능을 갖추고 있다.

console.log("5" + 9)

위의 코드를 실행하면 결과는 “59”가 출력된다. ‘+’연산자는 string 연산자로 피연산자의 타입 중 string이 포함되어있을 경우 모두 string으로 캐스팅하여 concat 연산을 시행한다.

console.log("10" - 1)

반면에 위의 코드를 실행하면 결과는 1이 출력된다.

ECMAScript® 2024 Language Specification

위의 ECMA 표준 스크립트를 확인해보면 각 연산에 대한 시멘틱 연산 과정이 기재되어 있다. 각 연산 과정을 살펴보면 + 연산에서는 형변환 연산이 존재하지만 - 연산에서는 형변환 연산이 존재하지 않는다. 이러한 구현상 차이에서 발생하는 문제점이라고 보면 된다.

다들 한 번쯤은 본 적있는 JS의 type과 관련된 밈들이 이러한 특징에서 생겨난 것이더라…

아무튼 이런 JS의 특징은 빠른 개발을 목표로 하는 프로덕트에 있어서는 어느 정도 적절할 수 있어도, 사이즈가 커지거나 비즈니스 로직을 포함하고 있어 안정성을 최대화하는 것이 최소 목적에 포함되어 있는 프로덕트들의 경우에는 코드의 퀄리티를 저해하는 주범이 될 수 있다.


Too many Type Error

그렇다면 JS로 작성한 코드는 타입 에러를 일으키지 않을까?

알고 있다시피 당연히 절대 아니다.

위 자료는 Rollbar에서 집계한 Top 10 JavaScript Error에 대한 그래프이다. 수집한 프로젝트들이 어떤 경로이고 어떤 기준으로 수집되었는지는 확인하기 힘들었지만, 수집된 프로젝트들에 대하여 공통된 에러에 대하여 집계해본 결과이며 상위 10개 중 대부분의 에러가 TypeError라는 title을 가지고 있는 것을 확인할 수 있다.

object에 존재한다고 생각했던 property에 접근하려 했는데, 실제로 전달받은 object에는 해당 property가 존재하지 않아서 발생하는 에러나, 같은 상황에서 전달받은 데이터가 undefined이나 null이여서 object에 대한 property 참조 연산이 불가한 경우에 발생하는 에러 등 대부분이 API와 통신할 때 JSON을 파싱하여 데이터를 다룰 때, 타입을 확정할 수 없는 상황에서 발생하는 것을 확인할 수 있다.

이러한 문제는 앞서 이야기했던 것처럼, 컴파일 단계에서 타입에 대한 에러를 확인할 수 없는 약타입 언어라는 JS 특성에 의해 발생한다. 런타임에서 발생하는 타입 불일치는 에러를 발생시키고, system failure까지 이를 확장시킬 수 있는 위험성이 존재한다. 하지만 겨우 이 정도 문제로 JS를 사용하지 않기에는 이미 너무 많은 웹 프로덕트와 라이브러리의 기반이 JS로 작성되어 왔다. 그렇다면 타입 에러가 발생하지 않도록 코드를 작성하는 단계나 컴파일 단계에서 이러한 에러를 확인할 수 있는 JavaScript를 만들어서 대체하는 것이 가장 이점이 많은 해결책이라고 볼 수 있는데, 이러한 발상에서 시작된 것이 JavaScript의 강타입 언어 버전. 즉 TypeScript이다.


So TypeScript

TypeScript is JavaScript with syntax for types.

TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.

TypeScript의 공식 문서 페이지에 가면 볼 수 있는 TS의 소개문구이다.

“TS는 타입에 대한 문법을 가진 JS이다. TS는 강타입 프로그래밍 언어로 JS 기반이며, 어떤 규모에 있어서든 더 나은 도구로 활용될 수 있다.”라고 한다. 핵심 키워드 두가지는 “강타입 언어”, “JS 기반”이다. TS는 런타임에서 발생할 수 있는 JS의 타입 에러 대부분을 컴파일 단계에서 확인할 수 있도록 도와주며, 변수를 선언할 때 이에 대한 타입도 함께 선언해줌으로써 해당 변수가 일정한 타입을 갖도록 컴파일러에게 강제시켜줄 수 있도록 한다. 또한 기존에 작성된 JS 코드에 이러한 강타입 특성을 부여해주는 것을 코드의 수정이 아닌, 단순 첨삭을 통해 가능하도록 하여 JS에서 TS로의 마이그레이션을 용이하게 돕고 있다. TS가 해당 두 가지 키워드를 어떤 방식으로 만족시키고 있는지 간단하게 알아보자.

  • 강타입 언어
const compact = (arr) => {
	return arr.length > 10 ? arr.slice(0,10) : arr;
}

위와 같이 특정 array의 길이가 10보다 클 경우 앞선 10개의 인덱스에 대해서 slice한 array를 반환하는 함수를 작성하였다. 함수의 작동에는 문제가 없을 것이다. 하지만 이는 “parameter인 arr의 값이 배열일 것이라는 가정”을 가지고 작성된 함수이다. 어떤 데이터가 함수의 파라미터로 들어올지 확신할 수 없는 상황에서 해당 함수는 TypeError를 발생시킬 가능성을 충분히 가지고 있다.

TS를 기반으로 한 개발환경에서는 이와 같은 에러를 사전에 방지시켜준다. 파라미터로 특정한 값을 받아오는 함수의 경우 파라미터의 타입을 지정해주어야 한다.

const compact = (arr:unknown[]) => {
  return arr.length > 10 ? arr.slice(0,10) : arr;
};

위와 같이 파라미터의 타입을 지정해주면, 더 이상 컴파일러에서 에러를 띄우지 않게 되고, 파라미터로 지정하지 않은 타입의 데이터가 입력되었을 경우에는 또 새로운 에러를 출력해준다.

TS가 적용되는 가장 단순한 방법은 “변수에 대한 타입 지정”이다. 이는 신뢰할 수 있는 데이터 타입에 대한 검사로 이어질 수 있으며, 특정 변수에 대하여 예상하지 못한 타입의 데이터가 할당되는 것을 방지할 수 있도록 도와준다.

  • JS기반

기존의 JS 코드에 TS 기반의 강타입 특성을 부여하기 위해서는 여러 가지 방법이 있다. 코드 작성 단계에서는 위에서처럼 인라인으로 직접 코드를 수정하여 TypeScript 컴파일러를 활용하는 방법이 있을 수 있지만, 기존에 작성된 코드를 수정하는 작업은 의존성에 영향을 주는 꽤 큰 위험도를 가진 작업이 될 수 있다.

TS는 이러한 리스크를 방지하기 위해 기존의 JS 코드에 주석을 추가함으로써 컴파일러에게 JS코드의 일부만을 TS코드로 감지하여 검사하도록 명령할 수 있도록 하였다. 또한 변수의 타입을 지정해주는 부분도 JS Docs를 활용하여 작성할 수 있도록 함으로써 작성되어있던 코드를 수정하는 방향이 아닌, 첨삭하는 방향으로 강타입 특성을 부여할 수 있도록 한다.

// @ts-check
/** param {any[]} arr */
const compact = (arr) => {
	return arr.length > 10 ? arr.trim(0,10) : arr;
}
// @ts-check로 컴파일러에게 해당 js 구문에 대한 타입 검사를 명령할 수 있다.
// 그 아래의 JS Docs 형태의 주석으로 파라미터의 타입을 지정해줄 수 있다.

하지만 이러한 방식도 기존 js파일의 ‘수정’ 및 ‘갱신’을 필요로 하게 된다. 이는 TS로 확장하려는 모든 기존의 JS 프로젝트에 대하여 소스 파일의 갱신이 필요하다는 의미이며, 단순하게는 빌드를 다시 해야한다는 부분부터 아주 다양한 리소스 낭비로 이어질 수 있는 확장방법이 될 수도 있다.

따라서 TS는 파일 자체를 수정하는 것이 아닌 타입 파일을 모듈화하여 타입을 저장할 수 있도록 하여 JS에서 TS로의 확장성을 극대화시키는 방식도 지원하고 있다. .d.ts라는 확장자로 이는 JS로 제작된 서드파티 모듈에 대하여 기존 코드에 대한 의존성을 배제한 채로 타입에 대한 선언을 가능하게 한다.

react/index.d.ts에는 프론트엔드 프레임워크 React의 컴포넌트들에 대한 타입 선언이 빼곡히 적혀있다.

react/index.d.ts에는 프론트엔드 프레임워크 React의 컴포넌트들에 대한 타입 선언이 빼곡히 적혀있다.

이는 특정 서드파티 모듈의 기존 제작자나 기여자가 아니여도 해당 모듈에 대하여 타입 선언이 가능하게 하여 수많은 JS 모듈들의 TS 확장이 가능하게 만들어주었기에 TS의 성장에 굉장히 큰 영향을 준 특징이라고 볼 수 있다.


TypeScript의 성장

여기까지 내가 TypeScript를 사용하는 이유이면서, 내가 생각하는 TypeScript가 성장할 수 있었던 이유이다. 정리하자면 나는 런타임 에러를 사전에 방지해줄 수 있는 TS를 사용해야 한다고 생각한다. 코드 내의 모든 변수와 그 변수에 담기는 모든 데이터를 코드의 작성자 본인이 보장할 수 있다면, TS를 사용하는 것은 컴파일러의 태스크를 추가해 빌드시간을 늘리는 역효과 밖에 없을 수 있다. 하지만 프론트엔드 개발자의 입장에서는 사실상 신뢰할 수 없는 데이터(API response)가 끊임없이 수신되는 상태에서 해당 데이터를 담을 변수를 선언해야 하기 때문에, TS의 도입은 반드시 필요하다고 생각한다.

본인의 개발 환경에 있어서 익숙했던 환경에 무언가를 추가한다던가, 특정 환경에 대한 마이그레이션을 진행할 때는 해당 환경을 필요로 하는 이유 뿐만아니라 해당 환경을 함께하고 있는 동료들이 많은지도 확인해볼 필요가 있다.

Github에서 발표한 연도별 언어 사용량 순위

위 그래프는 Github에서 발표한 연도별 언어 사용량 순위이다. 2014년 TypeScript 1.0이 공개되고 나서 TS의 사용량 비중은 연도마다 크게 증가하여 이제는 JS, Python, Java 다음의 4위라는 자리를 차지할 정도의 사용량을 보이고 있다. 나는 이러한 TS의 성장을 레퍼런스 서치할 때 가장 많이 느끼게 되는 것 같았는데, 특정한 컴포넌트의 구현에 대해 참고를 구하기 위해 블로그나 스택 오버플로우 등을 뒤질 때면, 최근에는 TS를 기반으로 한 React 코드가 점점 늘어나고 있다는 느낌을 받고 있다.

다른 언어와 병행하여 TypeScript를 사용하기 시작한 일부 프로그래머는 점차 TypeScript만 사용하는 방향으로 가고 있는 모양세입니다. 2020년에는 TypeScript 사용자의 47%가 JavaScript와 함께 TypeScript를 사용했지만 올해에는 그 수치가 38%로 떨어졌습니다.

JetBrains의 리서치 결과에 따르면 JS와 TS를 병행하여 사용하고 있는 비율이 점점 줄어들고 있다는 이야기도 확인할 수 있다. 이는 JS에서 TS로의 마이그레이션이 점점 큰 규모로 이루어지고 있다는 것을 뜻하며, 과장을 조금 보태서는 JS를 메인으로 사용하는 프론트엔드 생태계에서는 TS를 다루기 않고서는 살아남기 힘들어질 수 있겠다는 생각도 할 수 있다. 물론 프레임워크 위에서의 개발이 계속해서 주요 개발 방법으로 유행하고 있다는 것을 전제로 해야겠지만 말이다.

마치며

해당 시리즈에서는 TS의 기초적인 문법부터 시작하여 특수한 활용법 등까지 여러 방면을 살펴볼 예정이다. 이에 앞서 우리는 TS를 공부해야 하는 이유를 이번 글에서 확인해보았다. TS를 공부해야하는 이유로 충분히 전달이 되었으리라 믿으며, 다음 글부터는 TS의 구체적인 문법들을 함께 살펴보도록 하자.

profile
DIVIDE AND CONQUER

0개의 댓글