[TS]왜 TS인가?

개발 log·2022년 1월 9일
0

TS 지식

목록 보기
5/15
post-thumbnail

이 글을 쓰게 된 이유

JS로 첫 프로그래밍을 시작한 나로서는 동적 타이핑이라는 개념을 접하면서 동적 타이핑이 얼마나 불편한 것인지 알지 못했다.

하지만 정적 타이핑과 동적 타이핑의 개념을 배우고 컴파일 과정에서 타입을 체크하는 것이 왜 더 편하고 많은 프로그래머들에게 동적 타이핑이 어색하게 느껴지는지 알게 되었다.

때문에 이제는 TS를 사용해보며 디버깅과정을 줄이고 싶어졌고 정확히 TS가 어떤 장점이 있는지 궁금했기에 이러한 궁금증을 해결하고자 이 글을 쓰게 되었다.



TS란?

공식문서에서는 아래와 같이 언급한다.

타입스크립트는 자바스크립트로 컴파일되는, 자바스크립트의 타입이 있는 상위집합입니다.

정적 타입 시스템을 도입한 JS

타입이 있는 JS라는 단어를 보다 정확히 표현하자면 정적 타입 시스템을 도입한 JS라는 뜻이다.

정적 타입 시스템이 있는 언어, 즉 정적 타입 언어에서는 프로그램의 예상 동작을 타입을 통해 나타내고 예상대로 동작할 지의 여부를 타입 검사기를 통해 런타임 전에 확인할 수 있다.

TS는 JS의 상위집합

TS는 정적 타입 시스템이라는 기존 JS에서 전혀 없던 개념을 제공함에도 불구하고 TS와 JS는 완전히 동떨어진 다른 언어가 아니다.
올바르게 작성된 모든 JS코드는 TS코드로 치환될 수 있으며 TS는 조금의 추가사항 없이도 JS코드를 이해할 수 있다.

TS는 현존하는 JS의 문제점을 해결하기 위해 등장했고, 그 수단으로서 정적 타입 분석을 내세운 것이다.
그렇다면 정적 타입 분석은 무엇이고 어떤 장점을 제공하는지,
정적 타입 분석을 제공하는 여러 대체제(ex: JSDOC) 중 TS를 사용해야 하는 이유는 무엇인지,
TS는 어떤 요소들로 구성되어 있는지 아래 글을 통해 알아보려 한다.



정적 타입 분석

왜 TS를 사용하는지 알아보기 전 정적 타입 분석이란 무엇인지 짚고 넘어가려 한다.

타입

타입이란 프로그램의 올바른 동작이 무엇인지에 대한 프로그래머의 의도를 인코딩하는 수단이다.

정적 타입언어와 동적 타입언어

프로그램의 타입을 분석하는 방식을 기준으로 프로그래밍 언어를 크게 둘로 나눌 수 있는데
런타임 시점에 타입을 분석하는 동적 타입언어와
컴파일 시점에 타입을 분석하는 정적 타입언어로 구분할 수 있다.

사실 동적 타입언어와 정적 타입언어 중 무엇이 더 좋은가에 대해선 아직도 많은 논쟁이 있다.

이들의 특징을 비교하자면 아래와 같다.

정적 타입언어동적 타입언어
프로젝트 초기비용높다낮다
디버깅비교적 편하다비교적 불편하다
타입 분석 시점컴파일 시점런타임 시점

이렇게 비교해보면 정적 타입언어와 동적 타입언어는 다른 것이지 무엇이 맞고 틀리고를 결정하기는 힘든 주제이다.

하지만 동적 타입언어인 JS에 왜 정적 타입 시스템을 도입하려 했을까?

사실 런타임 시점까지 타입에 대한 검사를 늦춰 프로그래밍을 유연하게 하는 것은 양날의 검과 같다.

개발자가 타입을 잘 기억하고 있으면 크게 에러를 발생시키지 않고 빠르게 코드를 작성할 수 있지만 코드가 길어질 수록 개발자가 타입을 파악하기 어려워지고 에러가 발생할 확률이 높아지기 때문이다.

때문에 시스템의 규모가 커질수록 정적 타입언어가 빛을 발하게 된다.

정적 타입언어의 장점

  • 보다 빠른 버그 발견

    • 프로그램이 실제로 실행되기 전에 상당수의 오류를 잡아낼 수 있다.
  • 툴링

    • 소스코드에 대한 정적 타입 분석이 가능하다면 컴파일러 및 코드에디터가 코드를 실행하지 않아도 프로그램에 대해 훨씬 더 많은 정보를 알 수 있다.
  • 주석으로서의 타입

    • 타입 정의와 다르게 동작하는 코드는 에러를 발생시킨다.(실행을 막아버린다)
    • 이는 TS는 적용되지 않는다.(JS로 컴파일은 해주기 때문)


왜 TS인가?

TS는 JS 개발에 정적 타입 시스템을 도입하고자 적용한 수많은 사례 중 압도적으로 많은 사용자 커뮤니티를 갖고 있다.

과연 어떤 장점 덕분에 선두주자로 위치할 수 있었을까?

JS의 상위 집합

JS에 정적 타입 시스템을 도입하고자 한 시도에는 Elm이나 Reason, PureScript등의 언어가 있는데 이는 JS의 문법과 다르기 때문에 여기서 TS의 장점이 부각된다.

TS의 차별점을 정리하자면 아래와 같다.

  • 기존 JS 코드베이스의 마이그레이션에 드는 노력이 적다.
  • 사실 상 JS 위의 상위집합이기 때문에 러닝 커브가 낮다.
  • 서드파티 JS 패키지의 사용이 상대적으로 수월하다.

일정 수준의 트레이드 오프

TS는 생산성와 안정성 부분에서 적절한 트레이드 오프를 했다.

TS 깃허브 위키의 TypeScript Design Goals라는 페이저에서는 TS를 만들며 정한 목표와 목표로 정하지 않은 점에 대한 내용을 담고 있는데 목표가 아닌 것들 중에서 아래와 같은 언급이 있다.

Apply a sound or "provably correct" type system. Instead, strike a balance between correctness and productivity.

안전하고 “증명 가능하게 올바른” 타입 시스템 적용하기. 그 대신 정확성과 생산성 사이의 균형을 노린다.

위의 말을 자세히 읽어보면 의문이 생길 것이다.

안전하고 올바른 타입시스템을 적용하는 것이 왜 목표가 아닌 것일까?
안전성을 위해 정적 타입시스템을 도입하는 것이 아니었나?

하지만 아래 글을 계속 읽다보면 이해할 수 있을 것이다.

function getFirstThreeCharsUnsafe(arg: { x: string | null }) {
    if (arg.x !== null) {
        window.alert('arg.x is string!');
        console.log(arg.x.substr(0, 3));
    }
}

위의 함수를 해석하자면 arg라는 매개변수에는 { x: string | null }arg.x의 타입은 string 또는 null을 허용한다는 것이다.

이는 아래와 같이 동작한다.

  1. arg.xnull인지 여부를 체크한다.
  2. null이 아니라면 arg.x를 문자열이라 판단하고 String.prototype.substr을 호출한다.

타입체크를 잘 해주었기 때문에 이 코드는 별 문제가 없어 보인다.
하지만 아래와 같은 경우가 발생한다면 어떨까

function getFirstThreeCharsUnsafe(arg: { x: string | null }) {
    if (arg.x !== null) {
        window.alert('arg.x is string!');
        console.log(arg.x.substr(0, 3));
    }
}

var a: { x: string | null } = { x: 'ok' };

window.alert = function (str: string) {
    a.x = null;
};

getFirstThreeCharsUnsafe(a);

이 코드는 아래와 같이 동작한다.

함수 재정의 파트

  1. window.alert를 덮어쓴다.
  2. 덮어 쓴 함수는 인자로 넘어오는 a.xnull로 변환한다.

getFirstThreeCharsUnsafe함수 실행 파트

  1. arg.xnull인지 여부를 체크한다.
  2. null이 아니라면 재정의한 window.alert를 실행한다.
  3. arg.xnull로 변환된다.
  4. arg.x를 문자열이라 판단하고 String.prototype.substr을 호출한다.
  5. arg.xnull로 변환되었으므로 String.prototype.substr 메서드를 사용할 수 없다.
  6. 에러발생!!

우리는 분명 타입을 체크했다. 그렇게 안정성이 확보되었다고 생각했으나 예상치 못한 부수효과로 인해 런타임 시점에 타입 에러가 발생하게 되었다.

이러한 상황까지 가정하여 안정성을 챙기려면 타입을 체크한 블록 내에서도 항상 타입이 유지될 것이라는 가정을 버려야 한다.

그렇다면 코드는 아래와 같이 복잡해진다.

function getFirstThreeCharsSafe(arg: { x: string | null }) {
    if (arg.x !== null) {
        window.alert('arg.x is string!');
        if (arg.x !== null) {
          console.log(arg.x.substr(0, 3));
        }
    }
}

여기서 TS가 말한 안전하고 "증명 가능하게 올바른" 타입 시스템을 왜 목표로 삼지 않았는지 알게된다.

안정성을 충분히 챙기게 된다면 생산성 측면에서 코드가 길어지고 복잡해 질 것이다.

이런 점에서 타입 안정성을 일부 희생하면서 사용성을 극대화시키는 과감한 선택 덕분에 TS 사용자는 생산성의 희생 없이도 대부분의 경우 안전한 코드를 작성할 수 있다



VS Code와의 호환성

VS Code와 TS 모두 Microsoft에서 만들었다.

때문에 TS와 VS Code의 릴리스 노트를 나란히 보면 종종 동일한 내용이 등장하는 것을 알 수 있다.
(ex: 대표적으로 타입 정의 자동 설치 기능이 있다.)

또한 VS Code는 다른 어떤 에디터보다도 강력한 TS 툴링(익스텐션)을 갖고 있기 때문에 현 개발시장에서 점유율이 가장 높은 VS Code를 사용하는 유저라면 TS를 사용할 때 VS Code에서 제공하는 강력한 툴링을 사용할 수 있다는 장점이 있다.



정리

TS란?

정적 타입 시스템을 도입한 JS의 상위집합

TS를 사용하는 이유

  • JS의 상위 집합

    • 기존 JS 코드베이스의 마이그레이션에 드는 노력이 적다.
    • JS의 상위 집합이기 때문에 러닝 커브가 낮다.
    • JS 기반이기 때문에 서드파티 JS패키지의 사용이 상대적으로 수월하다.
  • 일정 수준의 트레이드 오프

    • TS는 완전한 안정성을 보장하지는 않지만 생산성 측면과 트레이드 오프를 통해 일정 수준의 안정성과 생산성을 제공해주었다.
  • VS Code와의 호환성

    • 제작사가 같다는 점에서 TS는 VS Code에서 강력한 툴링을 제공한다.
    • 이는 개발의 생산성을 올리는데에도 기여한다.


참고

ts-for-jsdev

profile
프론트엔드 개발자

0개의 댓글

관련 채용 정보