2026년 7월 15일, Roc 프로그래밍 언어의 창시자 Richard Feldman이 기술 블로그를 발행해, 그들이 487일을 들여 30만 줄의 Rust 코드를 Zig로 다시 썼다고 발표했다. 컴파일러의 증분 빌드 시간은 3.4초에서 35밀리초로 — 정확히 100배 빨라졌다.
이것은 고립된 사건이 아니다. 그 이전에도, Gleam 언어의 컴파일러가 같은 길을 걸었다. Rust에서 Zig로 이주한 것이다. 반면, Bun 런타임은 2026년 초에 반대 방향의 작업을 마쳤다 — Zig에서 Rust로, 단 11일 만에.
최고 수준의 컴파일러 개발자 두 그룹이, 두 방향으로 정반대의 선택을 내렸다. 어떤 일의 앞뒤 양면에 모두 진짜 코드로 투표한 이들이 있을 때, 당신은 안다. 이 이면에는 하나의 저울 문제가 있다는 것을.

35밀리초가 정말 의미하는 것
3.4초도 이미 빠르다. 필자는 Rust로 적지 않은 프로젝트를 써 봤고, cargo check가 2~3초 돌아가고 rust-analyzer가 백그라운드에서 윙윙거리는 것 — 솔직히, 익숙해지면 그만이다. Rust의 컴파일 속도는 지난 18개월간 눈부시게 발전해, Rust 1.97은 1.85 대비 증분 빌드 시간을 3분의 2로 줄였다. Feldman 본인도 글에서 Rust 팀의 이 노력을 진심으로 칭찬했다.
하지만 35밀리초는 다른 차원이다.
35밀리초는 당신이 Ctrl+S를 누르는 찰나 — 손가락이 키보드에서 아직 떨어지기도 전에 — 컴파일러가 결과를 알려준다는 뜻이다. 이것이 피드백 루프의 소멸이다. Zig 팀의 멤버 mlugg는 Lobsters에서 이 체험을 묘사하며 하나의 세부 사항을 말했다. 그는 매번 증분 빌드에 약 30밀리초만 쓰는데, 그중 링커 작업은 약 1밀리초를 차지한다. Zig의 새 ELF 링커는 단일 함수 단위의 증분 링크가 되도록 설계되었기 때문 — 함수 하나를 고치면, 컴파일러가 새 기계어를 생성하고, 링커가 출력 파일의 .text 섹션 안에서 예전 코드 위치를 찾아 덮어쓴다. syscall이 없는데, 출력 파일이 메모리에 mmap되어 있기 때문이다.
이러한 “제로 대기”는 개발자와 컴파일러의 관계를 바꾼다. 당신은 더 이상 수정분을 모아 한 번 빌드를 돌릴 필요가 없고, 수정과 피드백 사이를 빠르게 시험할 수 있다 — 마치 인터프리터 언어를 쓰듯 컴파일 언어를 쓰는 것처럼. Roc 컴파일러의 새 버전은 심지어 핫 코드 로딩을 지원한다. 실행 중인 서버가 프로세스를 재시작하지 않고도, 수정된 코드로 자동 전환한다. 이는 Python 세계에서는 기본 사항이지만, 컴파일 언어 세계에서는 사치품이다.
그렇다면 Rust의 타입 안전이 정말 ‘비싼’ 이유는?
35밀리초가 Zig가 준 달콤한 유혹이라면, Rust가 거둬가는 “안전 세금”은 대체 얼마나 비싼가? 우리는 이 문제를 뜯어봐야 한다.
첫 번째 장부: 컴파일 시간. Rust의 borrow checker는 컴파일 시점에 극히 사치스러운 일을 한다 — 당신의 프로그램에 use-after-free도 없고, double-free도 없고, 데이터 경쟁도 없음을 증명한다. 이 증명 과정은 전체 프로그램의 참조 관계 그래프를 순회해야 하며, 복잡도는 코드 규모에 따라 초선형적으로 증가한다. Rust의 증분 컴파일은 계속 발전하지만, borrow checker의 본질상 Zig처럼 “순식간에 나온다”는 것은 불가능하다.
두 번째 장부: 아키텍처 자유도. Roc 컴파일러는 arena 할당자와 struct-of-arrays 배치를 대거 사용한다 — 모든 데이터 구조가 포인터 대신 32비트 인덱스로 대체되고, 필드별로 독립 배열로 쪼개진다. 이 스타일은 현대 CPU에서 아주 빠르게 돌고, 디스크에 직접 mmap해 “제로 파싱 역직렬화”도 구현한다 — 두 번째로 roc check를 돌릴 때, 이미 파싱된 모든 데이터 구조가 디스크에서 메모리로 바로 뛰어들어, 속도가 memcpy에 가깝다.
하지만 문제는 이거다. 이러한 프로그래밍 스타일은 거의 필연적으로 Rust의 borrow checker와 부딪힌다. arena+인덱스 패턴은 Rust의 소유권 시스템을 우회하므로, 이는 당신의 unsafe 비중이 전형적인 Rust 프로젝트보다 훨씬 높아짐을 뜻한다. Feldman의 팀은 30만 줄의 Rust 코드 중 약 1,200곳에 unsafe가 있는데 — 이는 rustc 컴파일러 자체의 unsafe 밀도보다 한 자릿수 이상 높다. unsafe가 “소수의 감사가 필요한 구석”에서 “곳곳에 널린 일상”이 되면, borrow checker가 제공하는 안전감은 깎이게 된다.
양쪽 장부: 숫자는 거짓말도 하고, 진실도 한다
Feldman은 아주 솔직한 일을 했다. 그는 컴파일러 두 버전 각각의 메모리 손상 버그 개수를 집계했다.
Rust 버전: 21개. Zig 버전: 10개.
처음 보면, Zig가 이겼다. 하지만 뜯어보면 — Rust 버전의 21개 메모리 손상 버그는 전부 miscompilation(컴파일러가 잘못된 기계어를 생성한 것)이었고, 컴파일러 자체의 로직에서 발생한 것은 하나도 없었다. borrow checker는 해야 할 일을 제대로 했다. Zig 버전의 10개 중 8개도 miscompilation이었고, 나머지 2개는 use-after-free — 모두 오류 보고에서 파일명을 렌더링하는 곳에서 나왔고, 증상은 오류 메시지의 파일명이 깨진 글자가 되는 것이었다.
Feldman의 결론은 의외할 만치 차분했다. “18개월의 개발, 수백 건의 버그 보고, 수십만 줄의 코드를 돌아보며 내 주된 느낌은 이거다. 어느 쪽을 고르든 똑같다.” 그 2개의 use-after-free는 Rust의 borrow checker가 막을 수 있고, Zig의 ReleaseSafe 모드가 런타임에 panic을 일으킬 수 있지만 — 세 가지 방식의 실제 영향은 모두 “두 건의 버그 보고: 일부 오류 메시지에 파일명이 표시되지 않는다”였다.
이 결론은 Bun 팀과 미묘한 대조를 이룬다. Bun은 Zig에서 Rust로 이주할 때, JavaScript의 GC 값과 수동 메모리 관리를 동시에 다뤄야 하는 프로젝트에서는 use-after-free가 “수많은 버그의 원천”이라고 강조했다. Feldman은 이 점에 완전히 동의한다 — 그리고 나서, Roc의 컴파일러는 JavaScript와 상호운용할 필요가 없다고 지적한다.
핵심은 누가 맞고 틀린 것이 아니다. 핵심은 이거다. 맥락이 모든 것을 결정한다.

커뮤니티의 균열: 이것은 성전이 아니다
이 글은 Lobsters에서 175점, 62개 댓글을 받았고, Hacker News에서도 격렬한 토론을 불러일으켰다. 하지만 가장 주목할 만한 점은, 양쪽 모두 일선에서 나온, 근거 있는 논거를 갖추고 있다는 것이다.
Rust 핵심 팀원 Ralf Jung은 Feldman이 인용한 “rustc에 unsafe가 4만 곳”이라는 데이터의 문제를 지적했다 — 이 숫자에는 표준 라이브러리, 테스트, 주석에 나오는 횟수가 포함되어 있어, 실제 컴파일러 내의 unsafe는 이보다 훨씬 적다. 그는 동시에 인정했다. “unsafe Rust가 올바로 쓰기 어렵다는 점에는 완전히 동의한다, 이는 내가 매우 우려하는 문제다.”
llogiq — compact_arena 크레이트의 저자 — 는 Rust의 타입 태그 시스템이 컴파일 시점에 서로 다른 arena의 인덱스를 구분해 “잘못된 배열을 쓰는” 문제를 피할 수 있다고 지적했다. 하지만 그는 또한 인정했다. 이 기술은 arena 개수가 컴파일 시점에 알 수 없는 상황에서는 실패한다는 것을.
aapoalas — 스스로를 “데이터 지향 설계 광신자”라 부르는 Rust 사용자 — 는 전형적인 모순된 심경을 표현했다. “데이터 지향 설계의 광신자이자 Rust 중증 사용자로서, 뜻이 맞는 프로젝트 하나가 Rust를 떠나는 것을 보니 슬프다.” 그는 이어 자신이 Rust에서 비슷한 최적화를 구현해 본 일련의 시도를 나열했고, 그 어조에는 “억울함” 같은 솔직함이 서려 있었다.
필자는 생각한다. 이러한 논쟁이 건강한 까닭은, 아무도 상대를 바보라 부르지 않는다는 데 있다. “Rust를 고르면 성능을 모르는 것”이라거나 “Zig를 고르면 안전을 중시하지 않는 것”이라 말하는 이는 아무도 없다. 모두가 이것이 진짜 트레이드오프임을 인정하고, 각자의 프로젝트 맥락에 따라 다른 선택을 내린다.
더 깊은 층의 겨룸: comptime vs proc macro
컴파일 속도 숫자 놀이의 이면에는, 더 음미할 만한 철학적 갈림길이 하나 더 있다. 컴파일 시점 메타프로그래밍을 어떤 방식으로 할 것인가?
Zig의 선택은 comptime — 당신이 쓰는 것은 평범한 Zig 코드 그 자체고, 다만 그것을 컴파일 시점에 실행한다고 표시할 뿐이다. 그것은 컴파일러 안에 내장된 인터프리터처럼, 제네릭 프로그래밍, 코드 생성, 타입 조작을 모두 런타임 코드를 쓰는 것만큼 자연스럽게 만든다. 두 번째 문법도 없고, 토큰 트리 조작도 없고, 위생적 매크로의 괴상함도 없다.
Rust의 선택은 proc macro — 컴파일 시점에 실행되는 독립된 Rust 프로그램으로, 토큰 스트림을 받아 토큰 스트림을 조작하고 토큰 스트림을 출력한다. 그것은 극히 강력하다(이론상 당신은 무엇이든 할 수 있다), 하지만 극히 무겁기도 하다. 각 proc macro는 독립된 크레이트이며, 그것을 컴파일하는 데도 시간이 든다. Zig의 comptime은 같은 컴파일 과정에 내장되어 있어, 추가 비용이 거의 없다.
이것이 Zig가 빨리 컴파일되는 숨은 이유다. 매크로 시스템을 먼저 컴파일하고 나서 당신의 코드를 컴파일할 필요가 없다. 메타프로그래밍과 주 프로그램이 같은 컴파일러 파이프라인을 공유한다. Feldman은 글에서 이렇게 말했다. “나는 Zig에 매크로가 없는 것이 좋다.” — 이 말은 홀로 보면 불평처럼 들리지만, 맥락과 함께 보면 하나의 뺄셈 미학을 표현한다. 메커니즘 하나가 적을수록, 추상화 한 층이 적을수록, 컴파일 부담 하나가 줄어든다.
물론, 뺄셈은 곧 잃음을 뜻한다. Feldman은 또한 Rust의 trait 시스템과 비공개 필드가 그리웠다고 인정한다. 이것들은 Rust가 덧셈으로 바꾼 표현력이다. Zig를 고르는 것은, “단순함”이 “표현력”보다 더 중요하다는 것을 받아들이는 것이다 — 적어도 컴파일 속도를 생명줄로 삼는 프로젝트들에게는.
필자의 판단: 물고기와 곰, 하지만 접시는 고를 수 있다
이것은 어느 한쪽에 서는 글이다 아니다. 필자가 위 분석을 마친 뒤의 진짜 느낌은 이거다. Rust와 Zig의 관계는, “누가 더 낫나”에서 “누가 무엇에 더 맞나”로 바뀌고 있다.
당신의 프로젝트가 웹 서버나 데이터베이스 같다면 — 코드 구조가 비교적 안정적이고, unsafe가 소수의 핫스팟에 모여 있으며, 당신에게 더 필요한 것은 borrow checker가 주는 장기적 확신 — 그렇다면 Rust는 여전히 당분간 가장 안전한 선택이다.
당신의 프로젝트가 컴파일러 같다면 — 코드가 빈번히 반복적으로 리팩터링되어야 하고, unsafe가 곳곳에 흩어져 있으며, 컴파일 속도가 당신의 사고 리듬에 직결된다 — 그렇다면 Zig는 무시할 수 없는 옵션이 되어가고 있다. 이러한 상황에서는 안전 비용의 가격이 다르게 매겨지기 때문이다.
Roc 팀이 한 일은 그들이 필요로 하는 안전의 종류를 재정의한 것이다. 그들의 메모리 안전 문제는 주로 생성된 기계어에 있었지, 컴파일러 자체에 있지 않았다 — 그리고 borrow checker는 전자에는 아예 미치지 못한다. 안전의 병목이 언어가 제공하는 보장 범위 안에 있지 않을 때, 그것을 위해 컴파일 시간 비용을 지불하는 것은 협의 가능한 일이 된다.
35밀리초와 3.4초의 격차는, 본질적으로 두 가지 개발 철학의 구체화다. 하나는 기계가 컴파일 시점에 모든 것을 증명할 수 있다고 믿고, 하나는 개발자가 런타임에 모든 것을 잘 관리할 수 있다고 믿는다. 둘 다 완벽하지 않다 — 하지만 적어도 지금, 개발자에게는 진짜로 다른 옵션이 있다.
이것은 시스템 프로그래밍이 근래에 맞이한 최고의 소식일지도 모른다.
참고 링크:
- Richard Feldman: 《How Our Rust-to-Zig Rewrite is Going》(2026년 7월 15일)
- Lobsters 토론: How Our Rust-to-Zig Rewrite is Going(175점/62댓글)
- Bun 팀: 《Why We’re Rewriting Bun from Zig to Rust》(2026년)
- Zig 공식 개발 로그: Incremental Compilation Demo
- Gleam 언어 FAQ: Why Rust for the compiler?