방구석 IT

[Language] TypeScript 본문

실무 탐방/기술 탐방 및 소개

[Language] TypeScript

펭잉 2026. 6. 8. 13:30

TypeScript란?


TypeScript는 JavaScript 위에 정적 타입 시스템과 개발 도구 계층을 얹은 언어이다. 실행 환경을 새로 만드는 언어라기보다, 브라우저와 Node.js에서 실행될 JavaScript를 생성하기 전에 타입 관계를 검사하고 IDE 자동완성, 리팩터링, API 계약 관리를 강화하는 개발-time 언어에 가깝다. TypeScript 공식 핸드북은 JavaScript 프로그램 규모가 커질수록 모듈 사이 값의 관계를 표현하기 어렵고, 많은 오류가 타입 오류로 설명될 수 있다는 문제의식에서 TypeScript를 설명한다.

실무에서 TypeScript의 핵심 가치는 "런타임을 바꾸지 않고 코드베이스의 의미를 더 명시적으로 만든다"는 점이다. 타입은 대부분 컴파일 결과에서 제거되므로 운영 시점의 안전성은 TypeScript만으로 완성되지 않는다. 대신 빌드 이전에 잘못된 호출, 누락된 필드, 깨진 리팩터링, 잘못된 라이브러리 사용을 빠르게 드러내고, 팀이 공유하는 API 계약을 코드 안에 남긴다.

TypeScript는 JavaScript의 대체 런타임이 아니라, JavaScript 생태계 위에서 정적 분석과 설계 계약을 제공하는 언어 계층이다.
그림 1. TypeScript 소스가 타입 검사 단계를 거쳐 JavaScript로 출력되는 흐름이다. 타입 정보는 주로 개발과 빌드 단계에서 동작하며, 운영 안정성은 런타임 검증과 빌드 정책까지 함께 설계해야 한다.

왜 등장했는가?


JavaScript는 동적 타입, 프로토타입 기반 객체 모델, 매우 넓은 실행 환경 덕분에 웹의 기본 언어가 되었다. 그러나 프론트엔드 애플리케이션이 단순 스크립트에서 대규모 SPA, 서버 사이드 렌더링, Node.js 백엔드, 빌드 도구, 클라우드 함수까지 확장되면서 문제의 성격이 바뀌었다. 작은 코드에서는 유연함이 장점이지만, 수십 명이 공유하는 코드베이스에서는 함수 시그니처 변경, API 응답 구조 변경, null/undefined 처리 누락, 라이브러리 업그레이드 영향이 런타임 장애로 늦게 발견되기 쉽다.

TypeScript는 JavaScript와의 호환성을 유지하면서 이 문제를 줄인다. 기존 JavaScript 코드를 점진적으로 받아들이고, 필요한 곳부터 타입을 추가하며, npm 생태계의 선언 파일을 통해 외부 라이브러리의 사용 계약을 IDE와 컴파일러가 이해하게 만든다. 이 점 때문에 TypeScript 도입은 "새 언어로 전면 재작성"보다 "기존 JavaScript 개발 방식의 위험을 점진적으로 낮추는 마이그레이션"에 가깝다.

TypeScript가 해결하려는 문제는 JavaScript의 표현력을 줄이는 것이 아니라, 규모가 커진 JavaScript 코드에서 변경의 영향을 더 일찍 발견하는 것이다.

핵심 동작 원리


TypeScript 컴파일러는 소스 코드를 파싱하고, 타입을 추론하거나 명시 타입을 읽고, 값이 사용되는 위치와 선언된 계약이 맞는지 검사한다. 이때 타입 시스템은 구조적 타입을 사용한다. 이름이 같은 클래스나 인터페이스인지보다 "필요한 속성과 메서드를 갖고 있는가"가 더 중요하다. 이 특성은 JavaScript의 객체 리터럴과 덕 타이핑 문화에 잘 맞지만, 의도하지 않은 호환을 막고 싶을 때는 브랜드 타입이나 엄격한 도메인 모델링이 필요하다.

컴파일 결과는 보통 JavaScript이다. 인터페이스, 타입 별칭, 제네릭 매개변수 같은 타입 정보는 대부분 제거된다. 따라서 TypeScript 타입은 런타임 데이터 검증을 대신하지 않는다. 외부 API, 사용자 입력, 메시지 큐, 파일, 환경 변수처럼 프로세스 밖에서 들어오는 데이터는 zod, valibot, io-ts, JSON Schema 같은 런타임 검증 계층과 함께 다루는 것이 안전하다.

TypeScript의 타입 검사는 빌드 시점 안전망이며, 외부 입력의 런타임 검증과는 역할이 다르다.

타입 시스템과 설계 방식


TypeScript의 기본 타입 모델은 원시 타입, 배열, 객체, 유니언, 리터럴 타입, 제네릭, 조건부 타입, 매핑 타입, 템플릿 리터럴 타입 등으로 구성된다. 특히 유니언 타입과 좁히기(narrowing)는 실무에서 중요하다. 예를 들어 성공/실패 응답을 하나의 객체에 optional 필드로 흩뿌리는 대신, 판별 가능한 유니언으로 모델링하면 각 분기에서 필요한 필드를 컴파일러가 추론할 수 있다.

반대로 타입 표현력이 강하다는 점은 복잡한 타입 프로그래밍으로 이어질 수 있다. 라이브러리 작성자는 고급 타입으로 사용성을 높일 수 있지만, 애플리케이션 코드는 지나치게 추상적인 조건부 타입보다 명확한 도메인 타입과 간단한 제네릭을 우선하는 편이 유지보수에 좋다. 팀에서는 strict, noImplicitAny, strictNullChecks, exactOptionalPropertyTypes 같은 설정을 어떤 단계에서 켤지 합의해야 한다.

좋은 TypeScript 코드는 타입 묘기를 보여주는 코드가 아니라, 도메인 상태와 실패 가능성을 읽기 쉬운 계약으로 드러내는 코드이다.

생태계에서의 위치


TypeScript는 언어, 컴파일러, 언어 서버, 선언 파일 생태계를 함께 가진다. 에디터의 자동완성, go-to-definition, rename refactor, import 정리 같은 경험은 TypeScript 언어 서버의 도움을 크게 받는다. React, Next.js, Angular, NestJS, Vue, Svelte, Express, Prisma, tRPC 같은 주요 JavaScript/Node.js 생태계 도구들은 TypeScript를 1급 사용 경로로 제공하거나 강하게 지원한다.

빌드 도구 측면에서는 tsc가 타입 검사와 변환을 모두 할 수 있지만, 대규모 프론트엔드에서는 esbuild, SWC, Babel, Vite 같은 빠른 트랜스파일러와 tsc --noEmit 타입 검사를 분리하는 구성이 흔하다. 이 경우 "빌드는 통과했지만 타입 검사는 실패"하는 상태를 배포 파이프라인에서 반드시 차단해야 한다. 모노레포에서는 project references, incremental build, declaration emit, path alias와 패키지 경계 정책이 중요해진다.

TypeScript의 실무 가치는 컴파일러 하나보다 IDE, 선언 파일, 빌드 도구, 프레임워크가 연결된 개발 경험 전체에서 나온다.

운영 관점의 트레이드오프


  • 장점: 리팩터링 안정성, API 계약 가시성, IDE 생산성, 대규모 협업에서의 변경 추적성이 좋아진다.
  • 비용: 빌드 시간이 늘 수 있고, 타입 선언 관리와 설정 이해가 필요하며, 잘못된 any 사용은 안전성을 빠르게 무너뜨린다.
  • 장애 포인트: 런타임 데이터 검증 누락, 타입 정의와 실제 라이브러리 동작의 불일치, ESM/CJS 모듈 설정 충돌, path alias와 배포 산출물 경로 불일치가 자주 문제가 된다.
  • 보안 관점: TypeScript 타입은 신뢰 경계를 검증하지 않는다. 인증 토큰, 웹훅 payload, 사용자 입력, 파일 업로드 메타데이터는 별도의 런타임 검증과 권한 검사가 필요하다.
  • 성능 관점: 타입은 대체로 런타임 비용을 만들지 않지만, 빌드/CI 시간과 에디터 반응성에는 영향을 줄 수 있다. 복잡한 타입 연산과 거대한 유니언은 개발 경험을 느리게 만든다.
TypeScript 도입의 성공 기준은 "타입을 많이 썼는가"가 아니라, 빌드와 운영 사이에서 실제 결함을 얼마나 일찍 발견하게 만들었는가이다.

도입과 마이그레이션 전략


기존 JavaScript 프로젝트는 전면 전환보다 점진 전환이 현실적이다. 먼저 allowJs, checkJs, JSDoc 기반 타입 체크로 위험 구간을 드러내고, 새 파일부터 .ts 또는 .tsx로 작성하는 방식을 사용할 수 있다. 이후 공통 API 타입, 데이터 모델, 프레임워크 경계, 테스트 유틸리티처럼 변경 영향이 큰 부분부터 타입을 강화한다.

신규 프로젝트라면 처음부터 strict를 켜고, any 사용 규칙, 타입 가드 패턴, 런타임 스키마 검증 위치, API 타입 생성 방식(OpenAPI, GraphQL codegen, tRPC 등)을 정하는 것이 좋다. 팀 규모가 커질수록 tsconfig 표준화, ESLint 규칙, CI의 tsc --noEmit, 패키지별 declaration 생성, dependency upgrade 검증이 운영 품질을 좌우한다.

마이그레이션은 파일 확장자를 바꾸는 작업이 아니라, 신뢰 경계와 변경 빈도가 높은 지점부터 타입 계약을 심는 작업이다.

사례


  • 공식 TypeScript Handbook은 JavaScript 프로그램 규모가 커지면서 값 사이 관계를 표현하기 어려워진다는 배경과, TypeScript가 타입 오류를 사전에 잡는 방식을 설명한다. 출처: TypeScript Handbook
  • TypeScript Everyday Types는 원시 타입, 배열, 객체, 유니언 등 실무 코드에서 가장 자주 쓰는 타입 모델을 다룬다. 출처: Everyday Types
  • Microsoft TypeScript 5.0 발표는 ECMAScript decorators, ESM/bundler 지원, JSDoc 확장, 성능과 패키지 크기 개선 등 현대 JavaScript 생태계와의 접점을 보여준다. 출처: Announcing TypeScript 5.0
  • DefinitelyTyped는 JavaScript 라이브러리의 타입 선언을 공유하는 대표 생태계로, TypeScript가 기존 npm 생태계와 결합되는 방식을 보여준다. 출처: DefinitelyTyped GitHub Repository

'실무 탐방 > 기술 탐방 및 소개' 카테고리의 다른 글

[Language] SQL  (0) 2026.06.11
[Language] Rust  (0) 2026.06.09
[Language] JavaScript  (0) 2026.06.06
[Language] Python  (0) 2026.06.04
[Language] Java  (0) 2026.06.03