bun check 등장, tsc보다 3.6배 빨랐다

타입스크립트 프로젝트에서 tsc를 돌려 놓고 결과가 나오길 기다려 본 적이 있다면, 2026년 10월 10일에 나온 Bun 1.4.3에서 볼 곳은 bun check 한 줄이다. Bun은 이 명령이 타입스크립트 7.0.2의 tsc보다 3배에서 6.4배 빠르다고 적었다.
이 사이트는 그 말을 믿고 옮기는 대신 직접 재 봤다. 인텔 맥에서 같은 프로젝트를 세 번씩 돌리니 tsc는 약 2.9초, bun check는 약 0.8초로 3.6배였다. 더 놀라운 쪽은 결과다. 오류 597개가 나왔는데, 경로 표기와 설치 안내 문구 두 가지를 맞추고 나면 한 줄도 다르지 않았다.
이 글은 타입스크립트를 막 쓰기 시작한 사람도 읽을 수 있게 썼다. 타입 검사, 컴파일, 번들 같은 말은 처음 나올 때 한 줄로 풀었다. 근거로 삼은 자료는 Bun 공식 블로그의 v1.4.3 릴리스 노트와 bun check --help, npm에 올라온 타입스크립트 7.0.2 패키지 정보, 설치 안내 부분의 푸시 시큐리티 보고서다.
직접 해 본 일은 네 가지다. 공식 배포 파일로 Bun 1.4.3을 받아 별도 폴더에서 돌렸고, 오류가 일부러 들어 있는 작은 프로젝트에서 새 명령들을 눌러 봤고, 공개 저장소 한 곳(zod)에서 속도와 메모리를 쟀고, 출력 결과를 tsc와 한 줄씩 비교했다. 그 밖의 숫자는 전부 Bun이 릴리스 노트에 적은 내용이고, 어느 쪽인지 문장마다 밝혔다.
먼저, 타입 검사는 실행과 별개의 일이다
타입스크립트는 자바스크립트에 “이 값은 숫자다, 문자다” 같은 꼬리표(타입)를 붙여 쓰는 언어다. 꼬리표가 맞는지 따지는 일이 타입 검사다. 숫자가 들어갈 자리에 문자를 넣으면 오류로 알려 준다.
그런데 이 검사와 코드를 실행하는 일은 서로 다른 프로그램이 맡는다. Bun은 자바스크립트와 타입스크립트 코드를 실행하는 프로그램(런타임)인데, 파일을 돌릴 때 꼬리표를 떼고 실행만 했다. 그래서 타입 오류가 있어도 코드는 그냥 돌았다. 오류를 잡는 일은 마이크로소프트가 만든 tsc가 따로 맡았다.
tsc는 타입스크립트 컴파일러(변환기)이기도 해서, 검사만 하려면 보통 tsc --noEmit처럼 “결과 파일은 만들지 마라”는 옵션을 붙여 쓴다. 파일이 수천 개인 프로젝트에서는 이 검사가 몇 초에서 수십 초까지 걸리고, CI(저장소에 코드를 올릴 때 자동으로 도는 검사)에서는 그 시간이 그대로 대기 시간이 된다.
타입스크립트 7.0.2는 7월 8일 npm에 올라온 버전이다. 우리 맥에 설치해 보니 typescript 패키지와 함께 @typescript/typescript-darwin-x64라는 플랫폼별 실행 파일 패키지가 같이 깔렸다. 예전처럼 자바스크립트로 도는 tsc가 아니라, 기계어로 만든 네이티브 실행 파일이 붙어 있다는 뜻이다. Bun이 비교 상대로 삼은 것이 바로 이 버전이다.
bun check가 들어온 자리: 명령 하나와 붙임 옵션 셋
Bun 릴리스 노트는 bun check를 “TypeScript 7.0.2의 적합성 시험(conformance test)을 100% 통과하는 매우 빠른 타입 검사기”라고 소개한다. 마이크로소프트의 타입스크립트 네이티브 포트인 typescript-go를 옮겨 만든 것이고, tsconfig.json을 읽어 tsc와 같은 오류를 내며 CPU 코어를 전부 쓴다. typescript 패키지를 따로 설치할 필요가 없다.
명령은 이렇게 나뉜다. 아래 동작은 전부 이 사이트가 오류가 일부러 든 작은 프로젝트에서 직접 눌러 확인했다.
| 명령 | 하는 일 | 직접 돌려 본 결과 |
|---|---|---|
bun check |
타입 검사만 한다 | 오류가 있으면 종료 코드 1, 없으면 ✓ No type errors in 2 files |
bun run --check 파일 |
검사하고, 통과하면 실행 | 오류가 있으면 코드는 돌지 않고 멈춘다 |
bun test --check |
검사하고, 통과하면 테스트 | 릴리스 노트 설명(직접 실행은 안 함) |
bun build --check 파일 |
검사하고, 통과하면 번들 | 오류가 있으면 출력 폴더가 만들어지지 않았다 |
Bun.build({ check: true }) |
코드에서 같은 동작 | 릴리스 노트 설명(직접 실행은 안 함) |
눈여겨볼 점은 --check 없는 bun run이다. 같은 파일을 bun run src/index.ts로 돌리면 타입 오류가 있는데도 HELLO ADA가 그대로 출력됐다. 기존 습관대로 쓰면 오류를 못 막는다는 뜻이고, 막으려면 --check를 붙여야 한다. package.json의 스크립트나 --watch와도 같이 쓸 수 있다고 릴리스 노트는 적었다.
bun check에는 -p(다른 tsconfig 지정), --timing(로딩·검사 시간 출력), --threads(스레드 수), --all(오류 50개가 넘어도 전부 표시), --no-pretty(오류당 한 줄)가 있다. --strict 같은 컴파일러 옵션을 tsc처럼 바로 얹을 수도 있어서, tsconfig를 고치지 않고 더 엄격한 조건을 시험해 볼 수 있다.
Bun이 공개한 숫자: 8개 프로젝트에서 3.0~6.4배
아래 표는 Bun 릴리스 노트에 있는 값을 그대로 옮긴 것이다. 16코어 애플 실리콘 맥에서 Bun이 직접 쟀다.
| 프로젝트 | 파일 수 | bun check | tsc 7.0.2 | 빠른 정도 |
|---|---|---|---|---|
| VS Code src | 9,795 | 1.24초 | 5.98초 | 4.8배 |
| mikro-orm | 2,883 | 1.20초 | 5.97초 | 5.0배 |
| Next.js packages/next | 2,881 | 0.28초 | 1.82초 | 6.4배 |
| Next.js 루트 | 3,547 | 0.42초 | 1.28초 | 3.1배 |
| Storybook scripts | 1,039 | 0.27초 | 0.96초 | 3.5배 |
| Nuxt | 839 | 0.27초 | 0.80초 | 3.0배 |
| Playwright | 706 | 0.15초 | 0.64초 | 4.2배 |
| lit packages/react | 6 | 0.46초 | 2.12초 | 4.6배 |
메모리는 2.2배에서 4.9배 적게 쓴다고 했다. VS Code 소스는 tsc가 7.97GB를 쓸 때 bun check는 2.14GB(3.7배), mikro-orm은 6.67GB 대 1.35GB(4.9배), Next.js packages/next는 1.81GB 대 0.71GB(2.5배)다.
표에서 읽을 것은 두 가지다. 첫째, 가장 큰 배율(6.4배)은 파일 2,881개짜리 Next.js 하위 패키지에서 나왔고, 파일이 가장 많은 VS Code(9,795개)는 4.8배였다. 크다고 배율이 더 커지는 구조는 아니다. 둘째, 절대 시간은 이미 tsc 7이 충분히 빠른 편이라 줄어드는 양은 몇 초 안팎이다. 이 차이가 의미 있는지는 CI를 하루 몇 번 돌리는지, 저장할 때마다 검사를 돌리는지에 달렸다.
이 사이트가 우리 맥에서 재 본 값: 3.6배, 메모리는 3.6배 덜 쓴다
Bun의 숫자는 Bun이 잰 것이다. 그래서 같은 질문을 다른 기계, 다른 프로젝트에 던져 봤다.
- 기계: 인텔 코어 i7-9750H(12스레드), 메모리 16GB, 맥 OS. 애플 실리콘이 아니다.
- 도구: GitHub 공식 릴리스에서 받은
bun-darwin-x641.4.3, npm에서 받은[email protected] - 대상: 공개 저장소 zod의 10월 9일 커밋(284243f)을
--depth 1로 받은 것.node_modules는 설치하지 않았고, 우리가 건드린 코드는 없다. 검사한 파일은 471개다. - 방법: 같은 폴더에서 각각 세 번씩 실행한 벽시계 시간
| 1회 | 2회 | 3회 | 최대 메모리 | |
|---|---|---|---|---|
| bun check | 0.79초 | 0.83초 | 0.78초 | 약 335MB |
| tsc 7.0.2 | 3.01초 | 2.84초 | 2.80초 | 약 1.22GB |
세 번 평균으로 bun check가 약 0.80초, tsc가 약 2.88초라서 3.6배 빨랐고, 최대 메모리도 약 3.6배 적었다. 개별 쌍으로 비교하면 3.4배에서 3.9배 사이다. Bun이 내놓은 3.0~6.4배 범위 안에 들어가는 값이다. --timing을 켜면 bun check는 560개 파일을 불러오는 데 102.7ms, 471개를 검사하는 데 762.0ms가 걸렸다고 직접 알려 준다.
주의할 점도 있다. 저장소 하나, 기계 하나의 값이다. node_modules가 없어서 타입 선언을 못 찾는 오류가 대부분이라 실제 개발 환경과는 다르다. 마지막으로 tsc 쪽 시간에는 node가 tsc 실행 파일을 띄우는 시간이 조금 섞여 있다. 그래도 “수 배 빠르다”는 방향은 Bun의 주장과 같게 나왔다.
오류 597개는 한 줄도 다르지 않았다
빠르다는 말은 쉽다. 틀린 답을 빨리 내는 검사기는 쓸모가 없다. 그래서 두 도구의 출력을 오류 한 줄씩 비교했다.
zod 검사에서 두 도구는 똑같이 오류 597개를 냈고, 순서도 같았다. 달랐던 것은 두 가지뿐이다. 하나는 일부 오류 문장 속 경로가 /private/tmp/…와 /tmp/…로 갈린 것인데, 같은 폴더를 가리키는 맥의 심볼릭 링크 표기 차이다. 다른 하나는 타입 선언이 없을 때 붙는 설치 안내가 tsc는 npm i --save-dev @types/node, bun check는 bun add -d @types/node라는 점이다. 이 둘을 같은 문자열로 맞춘 뒤에는 diff가 0줄을 냈다.
Bun은 릴리스 노트에서 “같다”는 주장을 이렇게 뒷받침했다. 전부 Bun의 자체 시험 결과다.
| 시험 | 내용 | 결과 |
|---|---|---|
| 타입스크립트 적합성 시험 | 7.0.2의 오류·타입·심볼·선언 파일·모듈 해석 기준 파일 | 51,210개 중 51,210개 통과 |
| 일부러 망가뜨린 오픈소스 72곳 | node_modules 없음, 버전 불일치, 빌드 안 함 상태에서 tsc와 오류의 파일·줄·열·코드까지 대조 |
1,103개 설정 중 1,102개 동일, 오류 286,267개 |
| 컴파일러 옵션 11가지로 69곳 재시험 | strict, checkJs, nodenext 등 | 752개 중 751개 동일, 오류 895,051개 |
| 퍼저(코드를 대량으로 자동 생성해 비교하는 시험) | 커밋마다 도는 것 함수 136,235개, 따로 돌린 큰 시험 프로그램 3,032,182개 | 커밋마다 도는 쪽은 차이 0, 큰 시험은 서로 다른 결과 191개 |
| 변이 시험 | Effect 소스를 913번 일부러 망가뜨림 | tsc 오류 2,925개를 하나도 놓치지 않았고 더 낸 것도 없음 |
완벽하지 않다는 것도 같은 글에 적혀 있다. 72곳 중 어긋난 한 곳은 vuejs/core였고, tsc가 파일 순서에 따라 오류를 내거나 안 내는 경우였다. 옵션 11가지 시험에서 어긋난 12개는 strict를 끈 설정에서 나왔다. 퍼저의 191개 가운데 162개는 “선언하는 도중에 자기 자신을 가리키는 코드”에서 나왔다고 한다.
Bun이 제안하는 확인법은 간단하다. 내 프로젝트에서 두 결과를 뽑아 diff로 비교하라는 것이다.
bunx -p [email protected] tsc --noEmit --pretty false > tsc.txt
bun check --no-pretty > bun.txt
diff tsc.txt bun.txt
차이가 있으면 그건 Bun의 버그이니 이슈로 올려 달라고 릴리스 노트는 적었다.
에이전트가 돌리면 출력이 바뀐다: CLAUDECODE 한 줄의 효과
직접 돌리다가 릴리스 노트에 없는 동작을 하나 발견했다. 이 사이트의 순찰은 Claude Code 안에서 명령을 실행하는데, 같은 bun check가 평소 터미널에서 보던 모양과 다르게 출력됐다.
<error file="src/index.ts" line="2" column="25" code="TS2322">
Type 'string' is not assignable to type 'number'.
<source>
1 | import { greet } from "./user";
2 | const message = greet({ id: "1", name: "Ada" });
^^
...
</source>
<related file="src/user.ts" line="1" column="25">The expected type comes from property 'id' which is declared here on type 'User'</related>
</error>
오류마다 파일, 줄, 열, 오류 코드가 속성으로 붙고 소스 몇 줄이 함께 묶인 XML 비슷한 모양이다. bun check --help에는 “파이프로 보낼 때는 오류당 한 줄이 기본”이라고만 적혀 있고, 릴리스 노트에도 이 동작은 없다.
원인을 좁혀 봤다. 환경 변수를 하나씩 지우고 돌렸더니 CLAUDECODE만 지웠을 때 출력이 src/index.ts(2,25): error TS2322: … 같은 한 줄 모양으로 돌아왔다. AI_AGENT나 CLAUDE_CODE_ENTRYPOINT를 지우는 것으로는 바뀌지 않았다. 이 사이트 환경에서 확인한 결과이고, Bun이 다른 에이전트 도구의 환경 변수도 알아보는지는 시험하지 않았다.
정리하면 이렇다. 사람이 터미널에서 돌리면 소스 발췌가 붙은 보기 좋은 출력이, 파이프로 넘기면 tsc와 같은 한 줄 출력이, Claude Code 안에서 돌리면 이 사이트에서는 구조화된 출력이 나왔다. CI에서 bun check 결과를 다른 도구로 파싱하는 사람은 --no-pretty를 명시해 두는 편이 안전하다. Claude Code를 쓰는 환경에서 결과를 기계로 읽을 때 형식이 달라질 수 있기 때문이다. Claude Code를 팀 작업에 어떻게 얹는지는 Claude Code CLI로 팀 워크플로를 짜는 방법 글에서 다뤘다.
못 하는 일 세 가지: 내보내기, 에디터, 검증 기준
bun check는 이름 그대로 검사만 한다. 릴리스 노트가 직접 선을 그었다.
- 자바스크립트도,
.d.ts(타입 선언 파일)도 만들지 않는다.tsc를 빌드 도구로 쓰던 프로젝트라면 이 부분은 그대로tsc나 다른 번들러가 맡아야 한다.bun build --check는 검사 뒤 번들을 만들지만, 그건tsc의 내보내기를 대신하는 것이 아니라 Bun 번들러의 일이다. - 언어 서버가 없다. 에디터에서 빨간 줄을 긋고 자동완성을 해 주는 기능은 지금처럼 타입스크립트가 계속 맡는다. 바뀌는 것은 터미널과 CI에서 돌리는 검사 한 단계다.
- 검증 기준이 타입스크립트 7.0.2 하나다. 적합성 시험과 비교 대상이 모두 이 버전이다. 프로젝트가 아직 다른 버전의
tsc를 쓰고 있다면, 위의diff를 먼저 돌려 오류 목록이 같은지 보는 것이 안전하다.
Bun 쪽 위험도 있다. bun check는 방금 나온 기능이라 외부 사용 후기는 이 글을 쓰는 시점에 찾지 못했다. 위 시험들은 Bun이 직접 짜서 돌린 것이고, 이 사이트가 더한 독립 검증은 저장소 하나다.
같은 날 들어온 런타임 변화 중 실제로 체감될 것
Bun 1.4.3은 bun check 하나만 담은 릴리스가 아니다. 릴리스 노트에서 코드를 고치지 않아도 효과를 볼 만한 항목만 골랐다. 전부 Bun이 직접 잰 값이다.
| 항목 | 릴리스 노트의 내용 |
|---|---|
| 서버가 놀 때의 CPU | 유휴 상태의 CPU 사용량이 67~89% 줄었다고 소개 |
큰 응답을 보내는 node:http |
16KB를 4번 쓰는 경우 초당 요청 6,983건에서 19,160건으로(Node.js 26.3은 17,735건). 큰 응답 8개 시험 모두에서 Node보다 빨라졌다고 적었다 |
Promise.allSettled |
실패하는 비동기 호출 10만 개를 모으는 데 24초에서 148ms로(Node.js는 475ms) |
JSON.parse |
npm 패키지 정보 파일에서 1.2~1.5배 빠름 |
bun install |
lockfile이 없을 때 [email protected]를 설치하며 내려받는 tarball이 196개에서 1개로 |
bun build 메모리 |
진입점 800개를 나눠 묶는 시험에서 최대 메모리 2,121MB에서 276MB로 |
bun build --compile --bytecode |
프로파일 기반 배치를 쓰면 큰 CLI 앱의 첫 시작이 1.01초에서 0.53초로 |
새 API도 있다. Bun.FetchSession은 요청 묶음마다 TLS, 프록시, 연결 풀을 따로 두는 기능이다. --disallow-code-generation-from-strings는 eval()과 new Function()을 막는 옵션인데, Bun은 이 옵션을 받고도 무시하고 있었다고 적었다. Bun.ModuleGraph는 한 프로세스 안에서 같은 앱 여러 개를 따로 돌리는 실험 기능이며, 보안 샌드박스는 아니라고 직접 못 박았다.
릴리스 노트는 버그도 숨기지 않았다. 1.4.0 이후 crypto.randomInt()가 약 20배 느려졌던 문제를 이번에 고쳤다(760ns에서 34ns). 새 기능이 몰린 릴리스에서 이전 버전의 성능 후퇴를 함께 바로잡는 것은 반길 만한 신호지만, 거꾸로 말하면 1.4.0~1.4.2를 쓴 사람은 그동안 느려진 구간이 있었다는 뜻이다.
같은 시기의 다른 런타임 이야기는 Deno가 클라우드플레어에 합류한 소식과 함께 보면 흐름이 읽힌다. 자바스크립트 런타임들이 “실행”을 넘어 검사·번들·테스트까지 한 도구 안에 넣으며 경쟁하는 중이다.
이 숫자를 어디까지 믿어야 하나
세 층으로 나눠서 읽는 것이 좋다.
확인된 것. bun check는 실제로 존재하고, 우리 맥에서 tsc 7.0.2와 같은 오류 597개를 3.6배 빠르게 냈다. bun run --check는 오류에서 멈추고, bun build --check는 아무것도 쓰지 않았다. 여기까지는 이 사이트가 직접 눌러서 본 것이다.
Bun의 주장. 3.0~6.4배라는 범위, 적합성 시험 51,210개 전부 통과, 대규모 퍼저 결과는 Bun이 자기 환경과 자기 시험으로 낸 숫자다. 방법이 투명하게 적혀 있고 직접 재현하는 명령까지 줬지만, 외부 기관의 검증은 이 글을 쓰는 시점에 찾지 못했다.
아직 모르는 것. 윈도우와 리눅스에서의 속도, node_modules가 제대로 설치된 대형 모노레포에서의 결과, 여러 프로젝트 참조(references)를 가진 구성의 안정성은 이 사이트가 시험하지 못했다. 에디터에서 쓰는 언어 서버가 없으니 “저장하자마자 빨간 줄”이 필요한 사람에게는 해당 사항이 없다. 그리고 빠른 검사가 있다고 타입 오류가 줄어드는 것은 아니다. 줄어드는 것은 기다리는 시간이다.
내 프로젝트에서 안전하게 시험하는 순서
바꾸기 전에 비교부터 한다. 기존 tsc 검사는 그대로 두고 옆에 하나 더 얹는 방식이면 잃는 것이 없다.
- Bun을 1.4.3 이상으로 올린다. 이미 쓰고 있다면
bun upgrade, 처음이라면 공식 설치 문서의 명령을 쓴다. 설치 명령은 검색 광고가 아니라 공식 문서 주소에서 복사한다. 10월 9일 보안 업체 푸시 시큐리티가 공개한 사례처럼, Claude 설치 명령으로 위장한 가짜 페이지가 검색 광고를 타고 퍼진 일이 있다. - 프로젝트 폴더에서
bun check --timing을 돌려 시간을 본다. - 위의
tsc --noEmit과bun check --no-pretty를 각각 파일로 뽑아diff로 비교한다. 다르면 그 줄을 따로 적어 둔다. Bun은 그걸 버그로 받겠다고 했다. - 같으면 CI의 타입 검사 단계에
bun check를 병렬로 하나 더 넣어 시간을 비교한다. 한동안은tsc도 같이 돌리고, 결과가 계속 같을 때 한쪽을 뺀다. - 개발 중에 코드를 돌릴 때는
bun run --check를 시험한다. 이 옵션이 없으면 타입 오류가 있어도 코드가 돌아간다는 점은 위에서 확인했다. - CI가
bun check출력을 읽어 어딘가에 붙인다면--no-pretty를 명시한다. Claude Code 같은 에이전트 환경에서는 기본 출력이 달라질 수 있다.
정리하면 이번 릴리스에서 가장 빨리 써 볼 만한 것은 bun check 한 줄이고, 그 전에 할 일은 diff 한 번이다. 파이썬 쪽에서 비슷한 “업그레이드 전 점검” 이야기를 보고 싶다면 파이썬 3.15 정식 출시 글을 이어 읽으면 된다.