ESLint + Prettier를 Biome 하나로 교체하면 린팅 속도가 56배 빨라진다 — 마이그레이션과 하이브리드 전략까지
솔직히 말하면, 저도 처음 eslint.config.js(flat config)를 마주했을 때 한숨부터 나왔습니다. ESLint v9으로 올리려고 보니 기존 .eslintrc.json은 통째로 갈아엎어야 하고, ESLint v9부터 설정 파일 방식 자체가 바뀌었으니(eslint.config.js 하나로 플러그인과 규칙을 모두 선언하는 방식) 플러그인들이 새 방식을 지원하는지 일일이 확인해야 하고 — 이게 과연 린터 업그레이드인지 마이그레이션 프로젝트인지 구분이 안 되더라고요. 저희 팀 기준으로는 TypeScript 파일 3,000개 정도 되는 모노레포였는데, 바로 그 시점에 Biome이 눈에 들어왔습니다.
Biome은 Rust로 짜인 단일 바이너리 툴체인입니다. ESLint, Prettier, import 정렬 플러그인이 하던 일을 @biomejs/biome 패키지 하나로 대신합니다. 이 글에서는 Biome이 왜 빠른지, 기존 프로젝트에 어떻게 얹는지, 그리고 전부 갈아타도 되는 상황과 하이브리드로 가야 하는 상황을 어떻게 구분하는지를 경험을 섞어 정리했습니다. 이미 ESLint 설정으로 빌드나 CI가 느리다면, 이 글 하나면 방향을 잡기에 충분합니다.
핵심 개념
이미 Biome의 원리를 알고 있다면 '실전 적용' 섹션으로 바로 넘어가도 됩니다.
Biome이 빠른 이유 — Rust 파서와 단일 패스
ESLint와 Prettier는 Node.js 위에서 동작하고, 서로 독립적으로 파일을 파싱합니다. 파일 하나를 ESLint가 한 번 읽고, Prettier가 또 한 번 읽죠. 게다가 플러그인이 늘어날수록 분석 패스도 함께 늘어납니다.
Biome은 다릅니다. Rust로 구현된 파서가 파일을 한 번 읽어서 AST(추상 구문 트리)를 만들고, 그 AST 위에서 포맷팅과 린팅을 동시에 처리합니다. Rust는 소유권 시스템으로 컴파일 타임에 메모리를 관리하기 때문에 GC 오버헤드가 없고, 그 덕분에 Node.js 런타임 오버헤드도 사라집니다.
결과가 꽤 극단적입니다.
| 작업 | ESLint / Prettier | Biome | 배율 |
|---|---|---|---|
| 린팅 10,000 파일 | 45.2초 | 0.8초 | ~56배 |
| 포맷팅 10,000 파일 | 12.1초 | 0.3초 | ~40배 |
| 에너지 소비 (M3 Mac) | 기준값 | 30% 수준 | ~70% 절감 |
공식 벤치마크 기준, 10,000개 TypeScript 파일 / M3 MacBook Pro 환경. DEV Community에서 언급되는 "35x faster" 수치는 다른 파일 규모·규칙 구성에서 측정된 값으로, 조건에 따라 배율은 달라질 수 있습니다.
AST (Abstract Syntax Tree): 소스 코드를 트리 구조로 변환한 중간 표현입니다. 린터와 포맷터는 이 트리를 순회하며 규칙을 적용합니다. Biome은 이 트리를 한 번만 만들어 재사용합니다.
Biome v2 "Biotype" — 타입 없이 타입을 안다
저는 이 기능이 발표됐을 때 솔직히 반반이었습니다. "설마 tsc 없이 타입을 제대로 볼 수 있겠어?"라는 의심이 먼저 들었거든요.
2025년 6월 출시된 Biome v2의 가장 큰 변화는 TypeScript 컴파일러(tsc) 없이 타입 인식 린팅(type-aware linting)을 구현한 것입니다. 기존에는 noFloatingPromises 같은 타입 기반 규칙을 쓰려면 typescript-eslint가 tsc를 직접 호출해 타입 정보를 가져와야 했고, 이게 린팅 속도를 크게 떨어뜨리는 주범이었죠. Biome v2는 자체 타입 추론 엔진(Biotype)으로 그 과정을 대체합니다.
Biome v2 공식 릴리즈 노트 기준으로 아직 typescript-eslint 대비 약 75% 케이스를 검출하는 수준이지만, 속도 손실 없이 타입 기반 검사가 가능하다는 점 자체가 방향 전환을 알리는 신호입니다.
지금 지원하는 파일 형식
- 완전 지원: JavaScript, TypeScript, JSX/TSX, JSON/JSONC
- 포맷터 + 기본 린터: CSS
- 포맷터만: GraphQL
- 개발 중: HTML(베타), Vue, Svelte, Astro
Vue나 Svelte 프로젝트라면 아직 조금 기다리는 편이 나을 수 있습니다.
실전 적용
예시 1: 신규 프로젝트 — 처음부터 Biome으로
새 프로젝트라면 처음부터 Biome으로 시작하는 것을 권장합니다. 기존 설정이 없으니 마이그레이션 부담도 없습니다.
# 1. 설치 (버전 고정 권장)
pnpm add -D --save-exact @biomejs/biome
# 2. 기본 설정 파일 생성
pnpm biome initbiome init을 실행하면 biome.json이 생성됩니다. 아래는 실무에서 주로 쓰는 기본 설정입니다.
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "all",
"semicolons": "always"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"typescript": {
"noExplicitAny": "warn"
}
}
},
"organizeImports": {
"enabled": true
}
}package.json 스크립트도 교체해두면 CI까지 깔끔하게 연결됩니다.
{
"scripts": {
"lint": "biome lint .",
"format": "biome format --write .",
"check": "biome check .",
"ci": "biome ci ."
}
}| 명령어 | 용도 |
|---|---|
biome check . |
린트 + 포맷 검사만 (CI에서 fail 여부 확인용) |
biome check --write . |
린트 자동 수정 + 포맷 적용 |
biome ci . |
CI 전용, 수정 없이 종료 코드로만 결과 반환 |
biome lint . |
린트만 |
biome format --write . |
포맷만 |
VS Code를 쓴다면 기존 ESLint·Prettier 익스텐션을 비활성화하고 공식 Biome 익스텐션을 설치한 뒤 .vscode/settings.json에 아래를 추가하면 저장 시 자동 포맷이 동작합니다.
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"[javascript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescript]": { "editor.defaultFormatter": "biomejs.biome" },
"[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" },
"[json]": { "editor.defaultFormatter": "biomejs.biome" },
"[jsonc]": { "editor.defaultFormatter": "biomejs.biome" }
}
[json]과[jsonc]까지 포함해두면biome.json자체를 VS Code에서 편집할 때 Prettier와 충돌하는 상황을 막을 수 있습니다.
예시 2: 기존 프로젝트 — 자동 마이그레이션 활용하기
기존 .eslintrc.*와 .prettierrc가 있는 프로젝트라면 Biome이 자동 변환 명령을 제공합니다. 처음 이 명령어를 봤을 때 반신반의했는데, 의외로 잘 됩니다.
# 1. Biome 설치
pnpm add -D --save-exact @biomejs/biome
pnpm biome init
# 2. ESLint 설정 자동 변환
pnpm biome migrate eslint --write
# 3. Prettier 설정 자동 변환
pnpm biome migrate prettier --write
# 4. 전체 파일에 Biome 적용 (수정사항 확인)
pnpm biome check --write .
# 5. 기존 패키지 제거 (실제 package.json 기준으로 조정해보시면 됩니다)
pnpm remove eslint prettier eslint-config-prettier \
@typescript-eslint/eslint-plugin @typescript-eslint/parser \
eslint-plugin-react eslint-plugin-react-hooks5단계의 제거 목록은 예시입니다. 실제로는
package.json에 설치된 ESLint 관련 패키지를 기준으로 조정하는 것이 좋습니다.
자동 변환이 매핑하지 못하는 규칙은 biome.json에 주석으로 남겨줍니다. 이 부분은 직접 검토해서 Biome의 동등 규칙으로 교체하거나 제거하면 됩니다.
예시 3: 하이브리드 — Biome + ESLint 공존
eslint-plugin-react-hooks나 eslint-plugin-next처럼 Biome이 아직 지원하지 않는 플러그인에 의존하는 프로젝트라면 전부 교체하기 어렵습니다. 저도 이 방식을 참고했는데, 한국 HR테크 스타트업 레몬베이스가 채택한 하이브리드 접근법이 이 경우에 실용적인 해법입니다.
ESLint v9 flat config 환경에서는 --ext 플래그가 제거됐습니다. 확장자 필터링은 eslint.config.js 내부에서 처리하므로, 스크립트는 아래처럼 단순하게 작성할 수 있습니다.
{
"scripts": {
"lint:biome": "biome lint .",
"lint:eslint": "eslint .",
"lint": "pnpm lint:biome && pnpm lint:eslint",
"format": "biome format --write .",
"check": "biome check ."
}
}Biome이 담당하는 영역과 ESLint가 담당하는 영역을 명확히 구분하는 게 포인트입니다.
| 역할 | 담당 도구 |
|---|---|
| 포맷팅 전체 | Biome |
| import 정렬 | Biome (organizeImports) |
| 일반 JS/TS 린팅 (455개+ 규칙) | Biome |
react-hooks 규칙 |
ESLint + eslint-plugin-react-hooks |
| Next.js 특화 규칙 | ESLint + eslint-plugin-next |
이렇게 하면 Prettier는 완전히 제거되고, ESLint는 역할이 크게 줄어든 채 필요한 부분에만 남습니다. 의존성과 설정 복잡도가 절반 이하로 내려오는 효과가 있습니다.
장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 처리 속도 | 공식 벤치마크 기준 10,000개 파일 린팅 ESLint 대비 56배, 포맷팅 Prettier 대비 40배 빠름 |
| 설정 단일화 | 수십 개 패키지와 여러 설정 파일을 biome.json 하나로 통합 |
| 마이그레이션 자동화 | biome migrate eslint/prettier --write로 대부분 자동 변환 |
| Prettier 호환성 | Prettier 출력 결과와 97% 호환, 포맷 충돌 거의 없음 |
| import 정렬 내장 | organizeImports 옵션 하나로 별도 플러그인 없이 동작 |
| 에너지 효율 | 공식 벤치마크에서 M3 Mac 기준 ESLint 대비 약 70% 에너지 절감 |
| 단일 의존성 | 플러그인 간 버전 충돌, 호환성 문제 구조적으로 제거 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 플러그인 미지원 | react-hooks, next, import 일부 규칙 없음 |
→ 예시 3 하이브리드 구성 참고 |
| 타입 검출 커버리지 | typescript-eslint 대비 75~85% 수준 |
엄격한 타입 안전 요구 시 병행 검토 |
| 플러그인 시스템 | v2에서 첫 이터레이션, ESLint 에코시스템 수준은 아직 아님 | GritQL 기반 커스텀 규칙 점진적 이관 |
| CSS 고급 규칙 | Stylelint의 일부 고급 CSS 규칙 미지원 | CSS 린팅은 Stylelint 병행 가능 |
| HTML/Vue/Svelte | 포맷터 미지원 (HTML은 베타) | 해당 프레임워크는 로드맵 완성 후 전환 고려 |
GritQL: Biome v2에서 도입된 커스텀 린트 규칙 작성용 DSL(도메인 특화 언어)입니다. SQL과 비슷한 패턴 매칭 문법으로 코드 구조를 쿼리합니다. ESLint의 커스텀 플러그인 작성보다 진입 장벽이 낮지만 아직 표현력은 제한적입니다.
실무에서 가장 흔한 실수
- VS Code에서 Prettier·ESLint 익스텐션을 비활성화하지 않고 Biome을 추가 — 저장 시 두 포맷터가 충돌해서 코드가 튕기는 현상이 발생합니다. 기존 익스텐션을 workspace 수준에서 끄거나 비활성화한 뒤 Biome 익스텐션을 활성화하는 순서가 중요합니다.
--save-exact없이 설치 — Biome은 마이너 업데이트에서도 포맷 결과가 바뀔 수 있어서 팀 전체가 같은 버전을 써야 합니다.pnpm add -D --save-exact @biomejs/biome으로 버전을 고정하는 것을 권장합니다.biome check와biome ci를 혼동 — 로컬에서는biome check --write .으로 자동 수정을 적용하고, CI에서는biome ci .으로 수정 없이 검사만 하는 것이 좋습니다. CI에--write를 넣으면 파이프라인이 조용히 파일을 수정하고 통과해버리는 상황이 생깁니다.
마치며
지금 당장 전환 여부를 결정해야 한다면, 핵심은 한 가지입니다. 미지원 플러그인이 없으면 완전 전환, 있으면 하이브리드로 시작하면 됩니다. 어느 쪽이든 Prettier는 바로 제거할 수 있고, ESLint 의존성은 절반 이상 줄어듭니다.
지금 바로 시작해볼 수 있는 3단계:
- 의존성 목록 점검:
package.json에서 ESLint 관련 패키지를 확인합니다.eslint-plugin-react-hooks,eslint-plugin-next같은 Biome 미지원 플러그인이 없으면 완전 전환, 있으면 하이브리드 전략으로 진행하면 됩니다. - 사이드 프로젝트나 브랜치에서 먼저 적용: 아래 네 줄로 자동 변환을 적용해볼 수 있습니다.
biome check .으로 결과를 확인하면 어느 규칙이 빠졌는지 바로 보입니다.bashpnpm add -D --save-exact @biomejs/biome pnpm biome init pnpm biome migrate eslint --write pnpm biome migrate prettier --write - CI와 에디터 연결:
package.json스크립트에"ci": "biome ci ."를 추가하고, VS Code라면 Biome 익스텐션을 설치한 뒤.vscode/settings.json에"editor.defaultFormatter": "biomejs.biome"을 설정하면 개발 사이클 전체가 Biome으로 연결됩니다.
참고 자료
- Biome 공식 홈페이지
- Biome GitHub 저장소
- Biome 공식 마이그레이션 가이드 (ESLint & Prettier)
- Biome 공식 설정 레퍼런스
- Biome v2 "Biotype" 공식 릴리즈 노트
- Biome: The ESLint and Prettier Killer? Complete Migration Guide for 2026 | DEV Community
- Replace ESLint + Prettier with Biome: 35x Faster, One Tool | DEV Community
- 레몬베이스 기술블로그: ESLint V9 마이그레이션 & Biome 하이브리드 도입기
- Biome v2 소켓 분석: 플러그인 시스템 & 타입 인식 린팅
- Better Stack: Biome vs ESLint 심층 비교
- Migrate a Node.js project from ESLint and Prettier to Biome