동일한 TypeScript 소스 리비전으로 tsgo와 tsrs에서 합성 픽스처 4개를 실행했으며 진단과 종료 코드가 모두 일치한다.
그림: 단일 파일 합성 픽스처 4개에서 tsgo와 tsrs의 진단을 비교한다. 네 결과가 일치하지만 프로젝트 호환성이나 성능 벤치마크를 뜻하지는 않는다.

Cloudflare는 tsrs를 소개하며 TypeScript 7.0보다 최대 8배 빠르고 메모리 사용량이 약 70% 적다고 밝혔습니다. 그런데 tsrs의 특정 커밋에 고정된 옛 벤치마크를 확인해 보니 mui-docs에는 16.87배, vscode 기본 설정에는 13.55배라고 적혀 있었습니다. 공식 발표의 8배와 이 숫자들은 어떻게 맞는 걸까요?

대형 프로젝트 성능 실험을 제가 다시 수행한 것은 아닙니다. 이번에는 tsrs와 tsgo(TypeScript의 Go 구현)를 서로 대응하는 버전으로 고정해 GitHub의 일시적인 격리 환경에서 TypeScript 예제 네 개의 진단을 비교했습니다. 네 경우 모두 같았습니다. 더 살펴볼 근거는 생겼지만 이것만으로 운영 CI의 타입 검사기를 교체할 생각은 없습니다.

벤치마크를 읽을 때 checker 수가 중요한 이유

Cloudflare 공식 발표는 초기 결과를 최대 8배 속도 향상, 메모리 약 70% 절감으로 요약합니다. 별도로 고정한 tsrs 과거 측정 보고서에는 다음 값이 나옵니다.

과거 업스트림 벤치마크tsgotsrschecker 수보고된 배수
mui-docs, 기본값12.66 초0.75 초4 vs 1616.87배
mui-docs, 양쪽 --checkers 412.69 초1.07 초4 vs 411.81배
mui-docs, 양쪽 --checkers 1612.36 초0.77 초16 vs 1616.07배
vscode, 기본값11.10 초0.82 초4 vs 1613.55배
vscode, 양쪽 --checkers 166.73 초0.81 초16 vs 168.32배

기본 설정부터 조건이 다릅니다. tsgo에는 checker 4개, tsrs에는 16개가 쓰였습니다. 하지만 두 도구 모두 4개나 16개로 맞춘 행도 8배를 넘습니다. 발표는 초기 측정 결과의 요약이고 RESULTS.md는 특정 버전과 빌드 환경에서 얻은 별도의 옛 스냅샷입니다. 8배는 그 표의 최대값도 아니고, 표의 16배가 모든 프로젝트에서 나온다는 약속도 아닙니다.

차이의 정확한 원인을 표만 보고 단정하지는 않겠습니다. 보고서가 밝힌 환경은 Depot CI의 depot-ubuntu-24.04-16(16 vCPU, 메모리 63 GB, Linux x86_64)입니다. 실행 순서를 교차해 측정한 시간과 최대 메모리의 중앙값을 사용했습니다. tsgo 기준은 typescript@7.0.2인 반면 tsrs는 더 새로운 TypeScript 7.1-dev 커밋을 이식했고 PGO 최적화 릴리스 빌드를 썼습니다. checker 개수를 맞춰도 소스 버전과 빌드 방식은 같지 않습니다.

메모리도 상황에 따라 달라집니다. mui-docs의 기본값에서는 4.61배, 양쪽 checker를 16개로 설정하면 8.23배 적은 메모리가 보고됐습니다. 발표의 ‘약 70% 감소’는 초기 비교의 요약이지 보장값이 아닙니다. 이 부분은 업스트림 기록을 확인했을 뿐 대형 프로젝트 벤치마크를 재실행하지 않았습니다.

실제로 실행한 네 가지 진단 샘플

고정한 버전은 tsrs@0.18.0-ts7.1.0-dev.20260929와 typescript@7.1.0-dev.20260930.4의 Go 구현입니다. 두 도구는 같은 TypeScript 소스 커밋 b85298b6을 기준으로 합니다.

의존성 준비에서 제가 먼저 실수했습니다. TypeScript 소스 저장소의 workspace 이름 @typescript/typescript를 npm에서 설치할 수 있는 공개 패키지로 오해해 E404를 받았습니다. 그때는 컴파일러가 실행되지도 않았습니다. 공개 루트 패키지는 typescript입니다. 이 nightly의 명령은 여전히 tsc이며 Linux x64 네이티브 Go 구현은 @typescript/typescript-linux-x64에서 제공됩니다. 패키지 이름을 수정한 뒤 컴파일을 시작할 수 있었습니다.

네 샘플 모두 같은 tsconfig.json을 사용했습니다. types: []는 환경에 설치된 전역 @types가 자동으로 추가되는 문제를 막고, skipLibCheck: false는 선언 파일 검사를 유지합니다. 단지 ‘strict 모드’라고 말하는 대신 실제 설정을 남깁니다.

{
  "compilerOptions": {
    "strict": true,
    "target": "es2022",
    "module": "esnext",
    "noEmit": true,
    "skipLibCheck": false,
    "types": []
  },
  "include": ["src/*.ts"]
}

각 디렉터리에 src/test.ts 파일 하나만 놓았습니다.

픽스처src/test.ts 전체 내용실측 tsgo / tsrs
valid (정상 타입)export const answer: number = 42;진단 0개, 종료 코드 0 / 0
type-error (타입 할당 오류)export const answer: number = "wrong";TS2322 · 종료 코드 2 / 2
module-missing (없는 모듈)import {missing} from "./unavailable.js"; export const answer = missing;TS2307 · 종료 코드 2 / 2
generic-constraint (제네릭 제약 오류)type OnlyText<T extends string> = {value:T}; export type Bad = OnlyText<number>;TS2344 · 종료 코드 2 / 2

두 도구 모두 --noEmit --pretty false --checkers 4로 실행했습니다. 파일명, 행과 열, 오류 코드, 메시지, 출력 순서, 종료 코드를 비교했으며 네 경우가 모두 일치했습니다. 다만 사례마다 소스 파일이 하나라 --checkers 4가 실제 4개 병렬 검사기 성능을 측정한 것은 아닙니다.

오류 개수가 같아도 안심하기 어려운 이유

고정 버전의 호환성 문서는 오류 기준선 13,462개 중 13,458개가 일치하고, .types 및 .symbols 기준선 12,779개는 모두 일치한다고 보고합니다. 그러나 합계가 같다고 진단이 같다고 할 수는 없습니다.

drizzle-orm이 좋은 반례입니다. 업스트림 보고서에서는 양쪽에 checker 16개를 사용했을 때 10,846개의 오류가 나왔지만 진단 위치는 달랐고, tsrs 버그로 분류했습니다. 이 사례는 제가 직접 재현한 것이 아니라 고정된 보고서에서 읽은 내용입니다. 실제 코드 리뷰와 CI에서는 오류가 정확한 파일과 줄을 가리키는지 확인해야 합니다.

버전별 제한도 살펴봐야 합니다. 이 버전은 --init과 --showConfig를 지원하지 않고, --locale로 진단 메시지를 번역하지 못합니다. .astro, .vue, .svelte content mapper는 CLI/build에서는 동작하지만 언어 서버에서는 지원되지 않습니다. 배포 바이너리도 Windows와 Intel macOS는 포함하지 않습니다. 저는 GitHub 격리 Linux 환경의 CLI만 확인했으며 IDE 플러그인, LSP나 이 운영체제들은 테스트하지 않았습니다. astro check나 실시간 편집기 진단이 필요하면 이 버전의 호환성 표부터 확인하세요. Astro 업그레이드의 타입 검사 문제를 다룬 기존 글도 참고할 수 있습니다.

내 프로젝트에서는 무엇부터 비교할까

평소 사용하는 작업 디렉터리를 바로 바꾸지 말고 일회용 worktree나 별도 브랜치에서 시작하는 편이 안전합니다. 실패하면 실험을 그대로 폐기할 수 있기 때문입니다.

아래 스크립트가 확인하는 것은 종료 코드는 같아도 상세 진단이 다를 수 있다는 점입니다. set +e는 의도적입니다. 오류를 넣은 샘플은 0이 아닌 종료 코드를 반환하므로 두 컴파일러를 모두 끝까지 실행하고 상태를 기록한 후에 출력을 비교해야 합니다. 여기의 tsc는 TypeScript nightly의 Go 구현 명령이고 /tmp/tsgo.out는 출력 파일 이름일 뿐입니다.

pnpm add -D --save-exact tsrs@0.18.0-ts7.1.0-dev.20260929 typescript@7.1.0-dev.20260930.4

set +e
pnpm exec tsc -p . --noEmit --pretty false --checkers 4 > /tmp/tsgo.out 2>&1
tsgo_status=$?
pnpm exec tsrs -p . --noEmit --pretty false --checkers 4 > /tmp/tsrs.out 2>&1
tsrs_status=$?
diff -u /tmp/tsgo.out /tmp/tsrs.out
diff_status=$?
set -e
printf 'tsgo_exit=%s tsrs_exit=%s diff_exit=%s\n' "$tsgo_status" "$tsrs_status" "$diff_status"
if [ "$tsgo_status" -ne "$tsrs_status" ] || [ "$diff_status" -ne 0 ]; then
  echo 'DIAGNOSTIC_DIFF_OR_EXIT_MISMATCH: inspect /tmp/tsgo.out and /tmp/tsrs.out'
  exit 1
fi

처음부터 차이가 나면 원본 로그를 남기고 --checkerAssignment go(tsgo의 파일 분배 및 checker 기록)나 --noLazyMembers(tsrs의 lazy-member 최적화 비활성화)를 별도 실험으로 시도하세요. 첫 비교 전에 켜면 기본 동작의 차이를 가릴 수 있습니다. 사용 문서와 기본 설정 차이에 상세한 설명이 있습니다.

진단 비교 스크립트는 소요 시간이나 메모리를 측정하지 않습니다. 이번에는 저도 성능 측정 전 과정을 실행하지 않았습니다. 우선 한 번 기록하려면 Linux의 GNU time을 이용해 실행 시간과 최대 RSS를 수집할 수 있습니다. macOS에서는 gnu-time을 설치하고 첫 줄을 TIME_BIN=gtime으로 바꿉니다.

TIME_BIN=/usr/bin/time

set +e
"$TIME_BIN" -f 'elapsed_s=%e max_rss=%M' -o /tmp/tsgo-bench.metrics \
  pnpm exec tsc -p . --noEmit --pretty false --checkers 4 \
  > /tmp/tsgo-bench.out 2>&1
tsgo_status=$?
"$TIME_BIN" -f 'elapsed_s=%e max_rss=%M' -o /tmp/tsrs-bench.metrics \
  pnpm exec tsrs -p . --noEmit --pretty false --checkers 4 \
  > /tmp/tsrs-bench.out 2>&1
tsrs_status=$?
set -e

printf 'tsgo_exit=%s ' "$tsgo_status"; cat /tmp/tsgo-bench.metrics
printf 'tsrs_exit=%s ' "$tsrs_status"; cat /tmp/tsrs-bench.metrics

이 명령은 결과 한 쌍만 만듭니다. 컴퓨터의 백그라운드 부하만 변해도 값이 흔들립니다. 제대로 비교하려면 같은 runner에서 순서를 바꿔 가며 최소 5쌍을 실행하고 의존성 lockfile, 버전, tsconfig, checker 수를 고정해 시간 중앙값과 최대 RSS를 비교하겠습니다. 콜드 스타트와 웜 캐시도 따로 기록합니다. 저는 운영체제 page cache를 비우지 않았으므로 완전한 시스템 콜드 캐시 측정이라고 주장하지 않습니다. GNU time의 %M은 보통 최대 RSS를 KiB 단위로 표시하므로 플랫폼 간 단위를 확인해야 합니다.

실제 CI에서는 tsrs를 먼저 합병을 차단하지 않는 추가 작업으로 돌리고 기존 tsc와 프레임워크 검사를 필수 게이트로 유지하겠습니다. 설명되지 않은 진단 차이가 없고, 성능 수치가 안정되며, 실제 PR·배포 주기와 롤백 절차까지 확인한 뒤에야 교체를 검토할 수 있습니다. 네 가지 작은 샘플이 입증한 것은 그 입력에서 두 도구의 진단이 같다는 사실뿐입니다.

실측 날짜: 2026년 10월 22일