Part12_TypeScript_ProjectManagement
장경은
책 읽기

[12장] 타입스크립트 프로젝트 관리

12.1 앰비언트 타입 활용하기

앰비언트 타입 선언

  • .d.ts 확장자를 가진 파일에서는 타입 선언만 할 수 있으며 값을 표현할 수는 없다.
    • 값을 포함하는 일반적인 선언과 구별하기 위해 .d.ts 확장자를 가진 파일에서 하는 타입 선언을 앰비언트(ambient) 타입 선언이라고 부른다.
    • 앰비언트 타입 선언으로 값을 정의할 수는 없지만 declare라는 키워드를 사용하여 어딘가에 자바스크립트 값이 존재한다는 사실을 선언할 수 있다.

대표적인 앰비언트 타입 선언 활용 사례

  • 타입스크립트는 기본적으로 .ts와 .js 파일만 이해하며 그 외의 다른 파일 형식은 인식하지 못한다.
    • 따라서 알지 못하는 파일 형식을 모듈로 가져오려 하면 에러가 발생한다.
    • 이런 상황에서 타입스크립트의 declare 키워드를 사용하여 아래와 같이 특정 형식을 모듈로 선언하면 타입스크립트 컴파일러에 미리 정보를 제공함으로써 에러를 수정할 수 있게 된다.
      declare module \*.png {
         const src: string;
         export default src;
      }
    • 보다시피 declare 키워드는 이미 존재하지만 타입스크립트가 알지 못하는 부분을 컴파일러에 이러한 것이 존재해라고 알려주는 역할을 한다.

자바스크립트로 작성된 라이브러리

  • 자바스크립트 라이브러리 내부 함수와 변수의 타입을 앰비언트 타입으로 선언하면 타입스크립트는 자동으로 .d.ts 확장자를 가진 파일을 검색하여 타입 검사를 진행하게 되므로 문제없이 컴파일된다.
    • 예를 들어 @types/react를 npm install -D 명령을 통해 설치하면 node_modules/@types/react에 index.d.ts와 global.d.ts가 설치된다.

타입스크림트로 작성된 라이브러리

  • 타입스크립트로 작성된 라이브러리일지라도 자바스크립트 파일과 .d.ts 파일로 배포되는 것이 일반적이다.
    • 자바스크립트 파일과 .d.ts 파일로 배포하면 라이브러리 코드를 따로 컴파일하지 않아도 되기 때문에 컴파일 시간이 크게 줄어든다.

자바스크립트 어딘가에 전역 변수가 정의되어 있음을 타입스크립트에 알릴 때

  • 전역 객체 Window에 변수나 함수를 추가할 때 해당 속성이 정의되어 있다는 것을 나타내기 위해 앰비언트 타입 선언을 사용할 수 있다.
declare global {
    interface Window {
        deviceId: string | undefined;
        appVersion: string;
    }
}

앰비언트 타입 선언 시 주의점

  • tsconfig.json의 declaration을 true로 설정하면 타입스크립트 컴파일러가 .d.ts 파일을 자동으로 생성해주기 때문에 수동으로 .d.ts 파일을 작성할 필요가 없다.
  • 서로 다른 라이브러리에서 동일한 이름의 앰비언트 타입 선언을 한다면 충돌이 발생하여 어떤 타입 선언이 적용될지 알기 어려우며, 의도한 대로 동작하지 않을 수 있다.
  • .ts 파일 내의 앰비언트 변수 선언은 개발자에게 혼란을 야기할 수 있다. 대표적으로 .ts 파일 내부에 엠비언트 변수를 선언할 때 앰비언트 타입의 의존성 관계가 보이지 않기 때문에 변경에 의한 영향 범위를 파악하기 어렵다.
    • .d.ts 확장자 파일 내에서 앰비언트 타입 선언을 하는 것은 일종의 개발자 간의 약속이다. 타입 선언 위치가 명확해야 가독성이 높아지고 유지보수도 편하게 할 수 있기 때문이다.

앰비언트 타입 활용하기

  • 타입을 정의하여 임포트 없이 전역으로 공유

    • .d.ts 파일에서의 앰비언트 타입 선언은 전역 변수와 같은 역할을 한다. 따라서 앰비언트 타입을 선언하면 모든 코드 내에서 임포트하지 않고 사용할 수 있다.
    • 앰비언트 타입으로 유틸리티 타입을 선언하면 모든 코드에서 임포트하지 않아도 해당 타입을 사용할 수 있다.
// src/index.d.ts
type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
 
// src/components.ts
type Props = { name: string; age: number; visible: boolean };
type OptionalProps = Optional<Props>; // Expect: { name?: string; age?: number; visible?: boolean; }
  • declare type 활용하기
    • 보편적으로 많이 사용하는 커스텀 유틸리티 타입을 declare type으로 선언하여 전역에서 사용할 수 있다.
    • 아래 예시처럼 Nullable 타입을 선언해서 어디에서든 쉽게 사용할 수 있다.
declare type Nullable<T> = T | null;
 
const name: Nullable<string> = 'woowa';
  • declare module 활용하기

    • CSS-in-JS를 사용할 때 정의된 theme 타입이 자동으로 안성되는 기능을 지원할 수 있다.
  • declare namespace 활용하기

    • Node.js 환경에서 .env 파일을 사용할 때, declare namespace를 활용하여 process.env로 설정값을 손쉽게 불러오고 환경변수의 자동 완성 기능을 쓸 수 있다.
declare namespace NodeJS {
    interface ProcessEnv {
        readonly API_URL: string;
        readonly API_INTERNAL_URL: string;
        // ...
    }
}
  • declare global 활용하기

    • 위의 예시처럼 전역 변수 Window 객체의 스코프에서 사용되는 모듈이나 변수를 추가할 수 있다.
  • declare와 번들러의 시너지

    • 번들 시점에 번들러를 통해서 해당 데이터를 주입할 수 있다.
    • injext 모듈을 사용하여 해당하는 데이터를 삽입할 수 있다.

12.2 스크립트와 설정 파일 활용하기

  • 타입스크립트 프로젝트에서 스크립트와 tsconfig 등을 잘 활용하면 개발 생산성을 높일 수 있다.

스크립트 활용하기

yarn tsx --noEmit --incremental -w

  • 이 스크립트는 프로젝트의 tsx(타입스크립트 컴파일러)를 실행한다.

npx type-coverage --detail

  • 이 스크립트를 사용하면 현재 프로젝트의 타입 커버리지와 any를 사용하고 있는 변수의 위치가 나타난다.

설정 파일 활용하기

  • tsconfig의 incremental 속성을 활용하여 타입스크립트의 컴파일 속도를 높일 수 있다.
    • incremental 속성을 true로 설정하면 중분 컴파일이 활성화되어 매번 모든 대상을 컴파일 하는 것이 아니라 변경된 부분만 컴파일하게 된다.

12.3 타입스크립트 마이그레이션

  • 점진적인 마이그레이션
    • allowJS를 true, noImplicitAny를 false로 설정하면 점진적으로 마이그레이션을 진행할 수 있다.

12.4 모노레포

  • 여러 프로젝트를 관리하는 경우 일반적으로 개별 프로젝트마다 별도의 레포지토리를 생성하여 관리한다.

    • 이때 통합할 수 있는 요소를 찾아 공통으로 통합하여 사용할 수도 있다.
  • 모노레포란 버전 관리 시스템에서 여러 프로젝트를 하나의 레포지토리로 통합하여 관리하는 소프트웨어 개발 전략이다.

    • 이전에는 다양한 기능을 가진 프로젝트를 하나의 레포지토리로 관리하는 모놀리식 기법을 주로 사용했다.
    • 하지만 모놀리식 구조는 코드 간의 직접적인 의존이 발생하여 일부 로직만 변경될 때도 전체 프로젝트에 영향을 줄 수 있다.
    • 이후 폴리레포 방식과 모노레포 방식이 등장하게 되었다.
스크린샷 2024-05-25 오후 10 07 28