“Toolchains, the hard parts” 요약 및 재구성
현대 개발 환경에서 “툴체인(toolchain)”이라는 말은 아주 많이 쓰이지만, 정작 직접 만들어본 사람은 많지 않다. 이 글은 Biome 유지보수자가 툴체인을 만들면서 겪은 경험을 바탕으로,
- 툴체인이 무엇인지
- 왜 JavaScript 세계의 툴체인은 유난히 복잡한지
- 진짜 어려운 부분이 무엇인지(DX, 에러 메시지, 포매터 등)
를 정리한 내용을 기반으로 재구성한 것이다.
툴체인(toolchain)
→ 보통 CLI 형태로 제공되며, 여러 개발 도구를 하나의 인터페이스로 묶어서 제공하는 소프트웨어.
예: Rust의 cargo
cargo fmt)cargo test)cargo build)특징:
JS/TS에는 cargo 같은 단일 공식 툴체인은 없다. 대신 프레임워크 단위로 비슷한 개념이 등장한다.
next dev, next build, next lint 등 프레임워크 중심 툴체인이들은 프레임워크 전용 툴체인에 가깝고, 언어 자체의 공식 툴체인과는 다르다.
최근에는 런타임 자체가 툴체인 역할을 포함하는 흐름이 나타난다.
→ 런타임 + 패키지 매니저 + 빌드 + 테스트를 한 번에 다루려는 시도.
→ JS/TS + 툴체인을 하나의 “플랫폼”으로 통합하려는 모델.
Node도 이 흐름을 인지하고 언어 레벨에서 툴 기능을 추가하고 있다.
node:test – 내장 테스트 러너node run – 태스크 러너 성격의 기능→ JS/TS 생태계 전체가 “언어 + 툴체인 일체형” 모델로 수렴하는 중이라는 이야기.
글쓴이는 Biome 유지보수자로서 툴체인을 만들며 다음을 깨달았다고 한다.
“툴체인의 가장 어려운 부분은 성능이나 기능이 아니라 개발자 경험(DX) 이다.”
결론:
10년 전만 해도 DX에 대한 관심은 거의 없었고, 에러 메시지는 대충 “SyntaxError: XXX” 수준으로 끝나는 경우가 많았다.
예: 다음 코드
const something;
Uncaught SyntaxError: missing = in const declaration
기술적으로는 맞는 말이지만:
같은 코드를 Biome에 돌리면:
at file.js:1:7 parse ━━━━━━━━━━━━━━━━━━━━
✖ Const declarations must have an initialized value.
> 1 │ const something;
│ ^^^^^^^^^
ℹ This variable needs to be initialized.
특징:
var foo = ;브라우저:
Uncaught SyntaxError: expected expression, got ';'
Biome:
at file.js:1:11 parse ━━━━━━━━━━━━━━━━━━━━
✖ Expected an expression, or an assignment but instead found ';'.
> 1 │ var foo = ;
│ ^
ℹ Expected an expression, or an assignment here.
여전히 “expression”이라는 용어는 어렵지만,
→ DX를 고려하면, 에러 메시지는 “문법 설명”이 아니라 “이 코드를 어떻게 고치면 되는지”를 중심으로 설계해야 한다.
“이 에러를 어떻게 해결해야 하는지까지 안내하는 메시지”
단순히 “문제가 있다”고만 말하면 사용자가 막힌다.
“어떻게 고칠 수 있는지”까지 알려줘야 DX가 완성된다.
다음 코드:
async function foo() {
doSomething();
}
require-await 메시지1:1 error Async function 'foo' has no 'await' expression require-await
× eslint(require-await): Async function has no 'await' expression.
╭─[file.js:1:16]
1 │ async function foo() {
· ───
2 │ doSomething();
╰────
help: Consider removing the 'async' keyword.
at file.js:1:1 lint/suspicious/useAwait ━━━━━━━━━━━
⚠ This async function lacks an await expression.
> 1 │ async function foo() {
│ ^^^^^^^^^^^^^^^^^^^^^^
> 2 │ doSomething();
> 3 │ }
│ ^
ℹ Remove this async modifier, or add an await expression in the function.
> 1 │ async function foo() {
│ ^^^^^^^^^^^^^^^^^^^^^^
> 2 │ doSomething();
> 3 │ }
│ ^
ℹ Async functions without await expressions may not need to be declared async
현대 개발자는 툴체인에서 다음을 당연하게 기대한다:
하지만 이 네 가지를 모두, 높은 완성도로, 일관된 철학 아래 제공하는 것은 매우 어렵다.
대표적인 예:
→ 언어(ECMAScript)와는 별도의 조직·팀이 각각 구현
→ 프로젝트마다:
를 각각 따로 설치·설정·연결해야 한다.
“새로운 웹 프로젝트 셋업에 얼마나 많은 시간을 쓰고 있는지 떠올려 보라”는 의미.
gofmt가 언어 소스 코드 레벨에 사실상 내장→ 젊은 언어일수록 “언어 + 공식 툴체인” 모델이 강하다.
많은 사람이 “컴파일러가 제일 어렵겠지”라고 생각하지만, 실제로는:
사례:
Biome 공동 작성자로서 글쓴이의 코멘트:
핵심 포인트 정리:
툴체인은 “당연히 있는 것”이 아니라, 매우 고난이도의 소프트웨어 공학 결과물이다.
앞으로도 언어·런타임·툴 체인은 계속 진화할 것이고, DX 기준도 점점 높아질 것이다.