[Typescript] 타입스크립트를 사용하며 고민했던것들

chosh·2023년 9월 4일

Typescript는 예상치 못한 실수를 방지해주는 JS 컴파일러다.
런타임 환경에서는 타입은 모두 제거되고, JS로 브라우저에서 동작하기 때문에, Typescript를 런타임 환경과 같이 생각해서는 안되며, 런타임 오류를 최소화 해주는 이유는, 개발자가 미리 발견하지 못한 잘못된 JS 사용을 컴파일 단계에서 잡아주기 때문에 런타임 오류를 최소화 해준다고 생각한다.

지인 개발자 회사 중에서 어떤 회사는 "Typescript는 개발에 자신 없는 사람이 사용하는거다", "Typescript는 JS의 자유로움을 제한하는건데 왜 쓰는지 모르겠다" 라고 했다는 말을 들었다.

나는 정반대라고 느끼는데 이유는, 사람은 누구나 실수를 하게 되는데 오히려 Typescript를 사용함으로써 내 프로젝트에 믿음을 가질수 있게 되고, Typescript의 제네릭 타입이나, 타입 추론을 통해 충분히 자유롭게 개발할 수 있다고 생각한다.


Typescript를 사용하기로 하고 여러 고민을 했던 내용들을 정리해봤다.

interface vs type

Typescript를 사용하면서 제일 고민 됐던게, interface 와 type 중 뭘 사용할 것인가 였던거 같다.
공식문서를 읽어보니,타입 별칭과 인터페이스는 매우 유사하며, 대부분의 경우 둘 중 하나를 자유롭게 선택하여 사용할 수 있습니다. interface가 가지는 대부분의 기능은 type에서도 동일하게 사용 가능합니다. 라는 내용을 봤었기에 더 큰 고민이 되었던거 같다.
interface 와 type 의 다른점은 분명히 있기 때문에 고민하면서 여러가지 생각을 했던것 같다.

interface

  1. 객체 구조 정의
    interface는 객체 구조의 커스텀 타입만을 만들수 있다.

  2. 확장 가능
    extends 키워드를 사용해 확장이 가능하다

    interface Animal {
      name: string;
    }
    
    interface Bear extends Animal {
      honey: boolean;
    }
  3. 선언병합
    똑같은 이름으로 다시 선언하면 이전에 선언했던것과 병합됨

    interface Window {
      title: string;
    }
    
    interface Window {
      ts: TypeScriptAPI;
    }
    
    const src = 'const a = "Hello World"';
    window.ts.transpileModule(src, {});

Type

  1. 객체 구조 정의

    type Point = {
      x: number;
      y: number;
    };
  2. 유니온 타입 정의

    type Status = 'success' | 'error';
  3. 인터섹션 타입 정의

    type Person = {
      name: string;
      age: number;
    };
    
    type Employee = {
      jobTitle: string;
      company: string;
    };
    
    type PersonAndEmployee = Person & Employee;
  4. 타입 별정 정의 및 리터럴 타입 정의

    type Success = 'success'

이렇게 특징이 있기 때문에 interface는 확장에 장점이 있는것 같고, type은 더 많은 타입을 자유롭게 지정할 수 있는것 같았다.
interface는 내가 오픈소스를 개발하게 되면 확장의 이점을 잘 활용할 수 있었고, type은 더 많은 타입 지정을 제공해주고 자동완성을 지원해줬다.

그래서 처음에는 오픈소스 개발을 해본적이 없기 때문에 더 많은 타입을 지정할 수 있어 자유로운 type alias 를 사용했다.
하지만 객체를 지정할 때 확장성 있게 interface를 사용하는걸 권장한다는 글을 봤고, 공식문서에서도 자유롭게 선택하여 사용할 수 있다고 해서 지금은 객체는 interface, 나머지는 type alias를 사용해서 지정하고 있다.

리터럴 타입

나는 아래의 두개의 타입 선언 중에 type alias 를 한번 더 이용하는걸 더 선호 한다.

interface Text {
  s: string;
  align: "left" | "right" | "center";
}
export type Align = "left" | "right" | "center";

interface Text {
  s: string;
  align: Align;
}

이유는 나는 보통 html 에서 바로 함수를 정의하는것보다, html 태그쪽에서는 구조나 스타일에 집중할수 있게 handle 함수로 빼서 사용하는걸 좋아하는데 그러면 text 객체에 align을 업데이트하는 식의 함수를 따로 만들어야 된다. 그럼 다시 "left" | "right" | "center"를 지정하기 보다, Align을 import 해와서 사용하면 값을 추가할때 Align 타입에서만 추가하면 전체적으로 추가 되기 때문에 당장 사용하지 않더라도 항상 type alias로 빼두고 작업한다.

함수인자같은 제네릭타입

처음 함수를 배웠을때 인자가 대단하다는걸 몰랐다..
그냥 가져다 쓰는거라고만 생각했는데, 어느 순간 일급객체, 고차함수와 같이 이해하면서 함수를 바라봤을때, 함수를 어떻게 쓰느냐에 따라 엄청난 추상화를 할 수 있다고 생각했다.

그런 추상화를 자유롭게 할 수 있게 타입을 지정하는것이 제네릭 타입이기 때문에 함수의 타입을 사용하는곳에서 지정할 수 있게 할 때는 제네릭 타입을 지정했다.

타입 추론

모든 곳에 타입 지정을 해도 되지만, Typescript는 타입 추론을 지원해 준다.
처음에는 모든곳에 명시적으로 타입을 지정해주는게 바로 타입을 알 수 있기 때문에 좋다고 생각해, 추론이 되는곳까지 타입을 지정했다.
하지만 타입 추론 시스템이 굉장히 잘 되어 있고 타입 추론을 사용하면 실질적으로 코드가 짧아지기 때문에 가독성도 개선되었으며, 추론 되는 타입도 마우스만 올려놓으면 바로 알 수 있기 때문에 추론 되는 내용은 타입 지정을 하지 말자라는 기준을 가지고 타입스크립트를 사용했다.

profile
제가 참고하기 위해 만든 블로그라 글을 편하게 작성했습니다. 틀린거 있다면 댓글 부탁드립니다.

0개의 댓글