Biome으로 ESLint·Prettier 대체하기: Rust 파서·규칙 엔진 구조와 에디터·CI 통합
이 글의 핵심
Biome은 빠르지만 포맷 결과가 Prettier와 완전히 같지 않고 모든 ESLint 규칙이 1:1로 옮겨지지도 않습니다. Rust 파서가 오류를 허용하며 CST를 만드는 방식, 규칙 엔진과 포매터 IR, 데몬 기반 증분 실행을 이해하면 어떤 규칙을 ESLint에 남겨야 하는지, .astro·.vue·.svelte 파일은 어떻게 다룰지 판단할 수 있습니다.
이 글의 핵심
Biome은 프런트엔드·풀스택 코드베이스에서 흔히 쓰이는 ESLint(린터)와 Prettier(포매터) 역할을 하나의 CLI와 설정 파일로 통합하려는 도구입니다. Rust로 구현되어 실행 속도가 빠르고, 별도의 Node.js 런타임 없이도 동작하는 배포 모델을 갖추고 있어 대규모 저장소에서도 체감 이점이 큽니다.
이 글에서는 Biome의 핵심 개념과 장점, Rust 파서·CST·린트 엔진·포매터 층의 내부 구조, ESLint·Prettier에서의 마이그레이션 절차, biome.json 설정, VS Code와 JetBrains 통합, CI/CD 파이프라인, 모노레포 구성을 순서대로 다룹니다. 이미 린트·포맷 파이프라인을 운영 중인 팀이 새 도구를 도입할 때 필요한 판단 기준과 실무 체크리스트를 함께 정리했습니다.
Biome이란 무엇인가
핵심 개념
Biome은 린터(linter), 포매터(formatter), 임포트 정리(import organizer) 기능을 한 프로젝트 안에서 제공합니다. 사용자는 주로 biome.json(또는 biome.jsonc) 하나로 규칙과 스타일을 선언하며, CLI 또는 에디터 확장을 통해 동일한 설정을 공유합니다.
설계상 다음과 같은 특징이 있습니다.
- 단일 도구: 규칙 충돌(린터가 원하는 코드 vs 포매터가 바꾼 코드)을 줄이기 위해, 한 엔진이 진단과 포맷을 함께 다룹니다.
- 빠른 실행: 대규모 저장소에서 반복 실행되는
lint·format작업의 대기 시간을 줄이는 것이 주요 목표 중 하나입니다. - 일관된 기본값: 팀이 “권장 프리셋”에서 출발해 필요한 규칙만 조정하는 워크플로를 지향합니다.
ESLint·Prettier 대비 장점
속도는 Biome을 도입하는 가장 흔한 이유입니다. 기존에는 ESLint와 Prettier를 각각 실행하거나, 에디터에서 두 번의 포맷·진단 사이클이 돌아가는 경우가 있었습니다. Biome은 한 번의 check 또는 ci로 여러 단계를 묶을 수 있어, 로컬 개발과 CI 모두에서 시간을 절약할 수 있습니다.
설정 단순화도 중요한 장점입니다. .eslintrc, .prettierrc, 플러그인 의존성, ignore 파일 규칙이 분산되어 있던 구조를 biome.json 중심으로 모을 수 있습니다. 물론 ESLint 생태계의 모든 플러그인 규칙이 1:1로 대응되는 것은 아니므로, “완전 동일한 규칙 집합”이 필요한 팀은 마이그레이션 후 규칙 갭 분석이 필요합니다.
개발자 경험 측면에서는 공식 VS Code 확장과 JetBrains 플러그인을 통해 LSP 기반 진단·수정·포맷을 제공한다는 점이 큽니다. 팀 전원이 같은 버전의 Biome을 쓰도록 package.json에 고정하는 방식이 일반적입니다.
내부 아키텍처: 파서·분석기·포매터·증분 실행
이 절에서는 CLI 사용법에 앞서, Biome이 왜 한 번의 실행으로 빠르게 린트와 포맷을 함께 처리할 수 있는지 구현 관점에서 정리합니다. 공식 문서의 Architecture와 크레이트 구조를 바탕으로 하되, 버전에 따라 세부 구현은 달라질 수 있으므로 원리 중심으로 이해하는 것이 좋습니다.
Rust 기반 파서와 CST(rowan)·오류 허용 파싱
Biome의 JavaScript·TypeScript 파싱은 biome_js_parser 등 언어별 파서 크레이트에서 수행되며, 트리 표현은 rowan 라이브러리를 내장 포크한 형태의 Green/Red 트리 패턴을 따릅니다. 여기서 중요한 것은 “추상 문법 트리(AST)만”을 만드는 것이 아니라, 공백·탭·주석까지 포함한 CST(Concrete Syntax Tree, 구체적 구문 트리) 를 유지한다는 점입니다.
CST는 소스 텍스트와 1:1에 가까운 대응을 유지하므로, 포매터가 “원문을 잃지 않고” 다시 인쇄(print)하기에 적합합니다. 트리비아(trivia)는 토큰에 선행(leading)·후행(trailing) 으로 붙으며, 공식 문서에서 설명하듯 한 줄을 왼쪽에서 오른쪽으로 읽을 때의 규칙으로 정리됩니다. 개발자가 직접 Green 트리를 만지기보다는, 문법 정의로부터 생성된 타입 안전 API 로 Red 트리를 탐색하는 사용 모델이 일반적입니다.
파서는 오류 허용(error-tolerant) 과 복구(recoverable) 를 목표로 합니다. 문법 오류가 있어도 가능한 한 나머지 구문을 계속 읽으며, 깨진 구간은 Bogus 노드 등으로 격리해 소비자(린터·포매터)가 전체 분석을 중단하지 않도록 합니다. 덕분에 에디터에서 저장 중간 상태의 파일도 어느 정도 진단·포맷 피드백을 받을 수 있습니다. 다만 복구 경로는 “항상 동일한 의미”를 보장하지 않으므로, 깨진 구문에 의존한 규칙 결과는 보조적으로 보는 편이 안전합니다.
스캐너·프로젝트 메타데이터와 린트의 입력
Biome은 파일을 열기 전에 스캐너가 워크스페이스를 훑어 중첩 biome.json, .gitignore 계열(설정에 따라), 필요 시 package.json·소스 인덱스 등을 수집합니다. 특히 프로젝트(project) 도메인 규칙을 켠 경우에는 매니페스트와 소스 맵을 더 넓게 조사하는 경로로 들어가며, 반대로 해당 규칙이 꺼져 있으면 세션에 필요한 폴더만 타깃으로 삼아 불필요한 크롤을 줄이는 최적화가 적용됩니다. 모노레포에서 특정 패키지 아래만 실행하면, 루트·상위 폴더와 해당 패키지 위주로만 스캔 범위가 잡히는 식입니다.
이 구조는 “린트가 텍스트 파일만 본다”는 단순 모델을 넘어서, 저장소 레이아웃·설정 계층까지 입력으로 삼을 수 있게 합니다. 운영 관점에서는 files.includes와 스캐너의 상호작용을 이해해야, 왜 특정 폴더가 분석에서 빠지거나 반대로 의도보다 넓게 스캔되는지를 설명할 수 있습니다.
AST·구문 노드 순회와 규칙 엔진
린터 층은 파서가 만든 구문 트리를 따라 규칙(rule)을 적용합니다. 구현 세부는 버전마다 다르지만, 개념적으로는 각 규칙이 특정 노드 종류·패턴을 방문(visit)하며 진단(diagnostic) 과 선택적 수정(fix) 을 생성합니다. 진단은 심각도·카테고리(correctness, suspicious, style 등)와 함께 보고되고, 수정은 안전(safe) 과 안전하지 않음(unsafe) 으로 나뉘어 기본 워크플로에서 다르게 취급됩니다.
규칙은 서로 독립적으로 설계되지만, 같은 CST/구문 모델 위에서 동작하므로 ESLint처럼 “파서는 Babel, 규칙은 ESTree 가정” 같은 이중 스택을 줄이는 효과가 있습니다. 반대로 말하면, 커스텀 ESLint 플러그인에 있던 매크로·타입 정보·특수어트리뷰트에 기대던 규칙은 Biome에 그대로 이식되지 않을 수 있어, 마이그레이션 시 규칙 갭 분석이 필요합니다.
TypeScript의 경우, 파싱 이후 시맨틱/타입 계층(체크된 시맨틱 모델)을 규칙이 참조하는 경로가 있으며, 이는 “단순 텍스트 정규식 린트”와 성격이 다릅니다. 팀 내부에서 규칙을 논할 때는 구문 기반 규칙과 타입·스코프를 아는 규칙을 구분해 기대치를 맞추는 것이 좋습니다.
포매터 알고리즘과 Prettier 호환 철학
포매터는 별도의 Prettier 프로세스를 띄우지 않으며, 동일 바이너리 안의 biome_formatter 계열 인프라로 동작합니다. 코드는 구문 트리에서 포맷 규칙에 따라 중간 표현(일반적으로 IR: 줄바꿈·들여쓰기·그룹핑 등의 문서(document) 모델)으로 올라간 뒤, 설정된 줄 폭(line width) 과 들여쓰기 스타일에 맞춰 문자열로 인쇄됩니다.
Biome은 공개적으로 Prettier와의 호환을 목표로 하며, 옵션도 “팀마다 취향이 갈리기 쉬운 스타일 논쟁”을 줄이기 위해 소수의 언어 중립·언어별 옵션에 두는 철학을 따릅니다. 그러나 바이트 단위 동일성을 보장하는 도구는 아니며, 엣지 케이스·버전 차이·옵션 조합에 따라 Prettier 출력과 미세한 차이가 남을 수 있습니다. 실무에서는 대표 샘플로 스냅샷을 비교하거나, 전환 직후 한동안 포맷 전용 CI 잡을 별도로 두어 차이를 감시하는 패턴이 안전합니다.
포매터가 CST를 유지하는 이유와 연결지어 보면, 주석·줄바꿈 위치를 보존하는 정책이 Prettier 사용자에게 익숙한 결과로 이어지는 경우가 많습니다. 다만 “주석을 어떻게 다룰지”류의 디테일은 구현·버전에 민감하므로, 논쟁이 되는 파일은 팀 합의 후 예외 경로(overrides) 로 빼는 것이 비용 대비 효과가 좋습니다.
증분 분석: 데몬·CLI 변경분·에디터 세션
“증분(incremental)”은 한 가지 기술 이름이라기보다, 다시 전부 읽지 않도록 만드는 여러 장치의 총칭으로 이해하는 편이 맞습니다.
- 데몬(daemon): Biome은 장기 실행 서버를 띄워 에디터·CLI 요청을 처리하는 서버–클라이언트 구조를 취합니다. 워크스페이스 인덱스와 세션 상태를 메모리에 유지할 수 있어, 반복 편집에서 고정 비용(amortized cost) 을 줄이는 데 유리합니다.
- CLI의 변경분 옵션: CI에서는
--changed·--since등(정확한 플래그는biome ci --help기준)으로 베이스 브랜치 대비 차이만 검사해 시간을 줄입니다. 이는 “파일 단위 캐시”라기보다 검사 대상 집합을 줄이는 층에 가깝습니다. - 스캐너 타깃팅: 앞서 설명한 대로, 프로젝트 규칙이 꺼져 있으면 세션에 필요한 폴더 위주로만 중첩 설정·ignore를 조사합니다. 거대 모노레포에서 “왜 이 패키지에서만 빠르다/느리다”를 설명할 때 자주 등장합니다.
증분과 최적화는 결정적(deterministic) 린트 결과와 트레이드오프가 될 수 있으므로, CI에서는 동일 커밋에 대해 로컬과 CI가 같은 파일 집합을 검사하는지(특히 변경분 검사 사용 시)를 점검하는 것이 좋습니다.
프로덕션에서 통하는 Biome 패턴
내부 구조를 알면, 운영 규칙이 자연스럽게 정리됩니다.
- 바이너리 단일화:
package.json·lockfile·CI·IDE가 동일@biomejs/biome버전을 가리키게 하여, 파서·규칙 세트·포맷 IR 해석의 분기를 없앱니다. - 데몬과 CI의 역할 분리: 로컬은 LSP·데몬으로 빠른 피드백, CI는
biome ci로 읽기 전용·결정적 검증. CI에서 포맷을 쓰기(--write)하지 않습니다. - 프로젝트 도메인 규칙은 신중하게:
package.json인덱싱 등 스캔 범위가 넓어지는 규칙은 대규모 저장소에서 비용이 커질 수 있습니다. 필요할 때만 켜고, 그렇지 않으면 스캐너 최적화를 살리는 구성을 유지합니다. - Prettier 병행 금지: 동일 파일에 Prettier와 Biome 포매터를 동시에 적용하면 줄바꿈 전쟁이 납니다. 전환 기간에도 경로 단위로 한 도구만 선택합니다.
- 규칙 롤아웃:
recommended에서 출발해 카테고리별로 확장하며, 소음이 큰 규칙은overrides로 테스트·레거시 경로에만 적용합니다. “한 번에 전부 error”로 올리면 팀이off로 되돌리기 쉽습니다. - 대용량·생성 코드 제외: CST 기반이라도 불필요한 거대 파일은 비용이 듭니다.
files·VCS ignore·경로 예외로 분석 입력 자체를 줄입니다.
이후 절에서는 위 원리를 바탕으로, 설치·biome.json·마이그레이션·에디터·CI·모노레포 설정을 실제 명령과 옵션으로 연결합니다.
설치 및 초기화
프로젝트 루트에서 개발 의존성으로 설치합니다. 버전 고정을 위해 --save-exact를 권장합니다.
npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init
init은 기본 biome.json을 생성합니다. 이후 package.json에 스크립트를 추가해 팀이 동일한 명령을 사용하도록 합니다.
{
"scripts": {
"check": "biome check .",
"check:write": "biome check --write .",
"format": "biome format --write .",
"lint": "biome lint .",
"ci": "biome ci ."
}
}
biome check는 포맷·린트·임포트 정리 등을 묶어 실행하는 상위 명령으로 생각하면 됩니다. 로컬에서는 --write로 자동 수정까지 적용하며, CI에서는 수정 없이 검증만 하는 편이 안전합니다.
biome.json 설정 작성
최소 구성 예시
아래는 Biome 2.x 기준으로 권장 규칙을 켠 기본 형태입니다. 프로젝트에 맞게 files, vcs, overrides를 확장합니다. Biome 1.x를 쓰는 레거시 저장소는 organizeImports 필드 등 예전 키를 참고해야 할 수 있습니다.
{
"$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
"files": {
"ignoreUnknown": false,
"includes": ["**", "!!**/dist", "!!**/build", "!!**/.next", "!!**/coverage"]
},
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 2,
"lineWidth": 100
},
"assist": {
"enabled": true,
"actions": {
"source": {
"recommended": true
}
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
}
}
$schema는 설치한 버전에 맞춰 https://biomejs.dev/schemas/<버전>/schema.json 형식으로 두거나, ./node_modules/@biomejs/biome/configuration_schema.json을 가리키면 로컬 스키마로 자동완성할 수 있습니다.
린터 규칙과 심각도
linter.rules 아래에는 style, suspicious, correctness 등 카테고리별 규칙이 배치됩니다. 팀 합의 하에 특정 규칙을 error·warn·off로 조정합니다. “Prettier와 동일한 스타일”을 우선한다면 포매터 옵션을 먼저 맞추고, 린트는 점진적으로 강화하는 방식이 충돌이 적습니다.
| 카테고리 | 잡는 것 | 예 |
|---|---|---|
correctness | 거의 확실한 버그 | noUnusedVariables, noUndeclaredVariables |
suspicious | 버그일 가능성이 높은 코드 | noExplicitAny, noConsole |
complexity | 불필요하게 복잡한 코드 | noBannedTypes, noExtraBooleanCast |
performance | 성능 함정 | noAccumulatingSpread, noDelete |
security | 보안 위험 | noDangerouslySetInnerHtml |
a11y | 접근성 | useAltText, useKeyWithClickEvents |
style | 코드 스타일 | useConst, useImportType |
nursery | 아직 안정화 전인 실험 규칙 | 버전마다 바뀜 — 켜려면 명시적으로 |
특정 줄만 예외로 두려면 주석으로 규칙 ID와 사유를 적습니다. 사유 없이 쓰면 경고가 나므로, 나중에 이 예외를 왜 뒀는지 추적하기 쉬워집니다.
// biome-ignore lint/suspicious/noExplicitAny: 레거시 SDK가 타입을 제공하지 않음
const data: any = legacyClient.fetch();
package.json에는 로컬용(수정)과 CI용(검사만) 스크립트를 나눠 두면 헷갈리지 않습니다.
{
"scripts": {
"check": "biome check .",
"check:fix": "biome check --write .",
"ci": "biome ci ."
}
}
VCS 연동(.gitignore 존중)
ESLint·Prettier가 Git이 무시하는 파일을 건너뛰는 것과 유사하게, Biome도 vcs 설정을 통해 버전 관리 클라이언트와 통합할 수 있습니다. 저장소 루트에 biome.json이 있다면, vcs.useIgnoreFile 등을 켜서 불필요한 파일을 분석 대상에서 제외하는 구성이 일반적입니다. CI에서도 동일한 무시 규칙이 적용되어야 로컬과 결과가 일치합니다.
{
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
}
}
overrides로 경로별 규칙
테스트 코드, 스크립트, 레거시 폴더에만 다른 규칙을 적용하려면 overrides에 include 패턴을 지정합니다. ESLint의 overrides와 같은 역할로 이해하면 됩니다.
{
"overrides": [
{
"include": ["**/*.test.ts", "**/__tests__/**"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
}
}
}
}
]
}
ESLint·Prettier에서 마이그레이션
자동 마이그레이션 명령
Biome은 기존 설정을 읽어 biome.json으로 옮기는 서브커맨드를 제공합니다.
npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write
migrate eslint: 레거시.eslintrc와 flat config 모두 지원 범위 내에서 동작합니다. 플러그인 의존성 해석을 위해 Node.js가 필요할 수 있습니다. YAML 형식의 ESLint 설정은 지원되지 않을 수 있으므로, 해당 경우 JSON/JS로 옮기거나 수동 매핑을 검토해야 합니다. ESLint 규칙과 Biome 규칙 이름이 다르고, 동작이 100% 동일하지 않을 수 있습니다.migrate prettier:.prettierrc등을 반영합니다. JSON5·TOML·YAML Prettier 설정은 마이그레이션 제약이 있을 수 있어, JSON 기반 설정으로 정리한 뒤 실행하는 편이 안전합니다.
Biome이 “영감을 받은(inspired)” 규칙까지 ESLint에서 그대로 옮기지 않는 경우가 있으므로, 필요하면 --include-inspired 옵션을 검토합니다(프로젝트 규모에 따라 노이즈가 늘 수 있습니다).
마이그레이션 후 꼭 할 일
biome check로 전체 저장소를 돌려 오류·경고 목록을 확보합니다.- ESLint 주석(
eslint-disable)에 의존하던 부분은 Biome 규칙 ID 기준으로 정리합니다. 주석 체계가 다르므로, 숨겨 두었던 기술 부채가 드러날 수 있습니다. - CI에서
biome ci로 최종 검증합니다. - 팀 문서(기여 가이드)의 명령어·에디터 설정을 Biome 기준으로 갱신합니다.
일부 팀은 전환 기간에 ESLint를 병행하기도 하지만, 규칙이 두 벌이면 개발자 혼란이 커집니다. 가능하면 단일 도구로 수렴하는 일정을 잡는 것이 좋습니다.
ESLint를 남겨야 하는 경우와 병행 구성
그래도 ESLint를 완전히 빼기 어려운 경우가 있습니다.
eslint-plugin-react-hooks,@next/eslint-plugin-next처럼 프레임워크 전용 규칙에 의존할 때(Biome에 대응 규칙이 일부만 있거나 동작이 다름)- 팀이 만든 커스텀 ESLint 규칙이 많을 때
- 테스트 프레임워크·보안 전용 플러그인을 쓸 때
이럴 땐 역할을 나눕니다. 포맷·임포트 정렬·일반 린트는 Biome, 특수 규칙만 ESLint. 핵심은 두 도구가 같은 걸 검사하지 않게 하는 것입니다. ESLint 쪽에서는 스타일·포맷 관련 규칙을 전부 끄고 필요한 플러그인 규칙만 남깁니다.
// eslint.config.js (flat config) — Biome과 병행할 때 ESLint는 플러그인 규칙만
import reactHooks from 'eslint-plugin-react-hooks';
export default [
{
plugins: { 'react-hooks': reactHooks },
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
},
},
];
.astro·.vue·.svelte 파일
Biome의 1순위는 JS/TS/JSX/JSON/CSS이고, 단일 파일 컴포넌트(SFC)는 버전마다 지원 범위가 넓어지는 중입니다. 현재 사용 중인 버전의 언어 지원 표를 먼저 확인하세요. 지원이 부분적이라면 .ts/.tsx는 Biome에 맡기고, SFC 파일은 기존 Prettier 플러그인(prettier-plugin-astro, prettier-plugin-svelte)이나 eslint-plugin-vue를 유지하는 절충이 현실적입니다. files.includes로 Biome이 볼 확장자를 명시해 두면 두 도구가 같은 파일을 서로 다르게 포맷하는 일을 막을 수 있습니다.
속도 차이는 도입 이유로 가장 많이 거론되는데, 실제 폭은 저장소 크기·규칙 수·ESLint 플러그인 구성에 따라 크게 다릅니다. 타입 정보가 필요한 ESLint 규칙(typescript-eslint의 type-aware 규칙)을 많이 쓰는 저장소일수록 차이가 커지고, 규칙이 적은 작은 프로젝트에서는 체감이 크지 않을 수 있습니다. 도입 전후로 자기 저장소의 lint 잡 시간을 재 보는 게 가장 정확합니다.
VS Code 통합
공식 확장 Biome(biomejs.biome)을 설치합니다. Visual Studio Code Marketplace 또는 Open VSX(VSCodium 등)에서 받을 수 있습니다.
저장 시 포맷·안전 수정·임포트 정리
워크스페이스 .vscode/settings.json 예시는 다음과 같습니다.
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
}
}
source.fixAll.biome은 안전한(safe) 수정 위주입니다. 불안전한 수정이 필요하면 규칙 설정을 조정하거나, CLI에서 명시적으로 적용하는 전략을 씁니다.
자주 쓰는 설정
biome.requireConfiguration:true로 두면biome.json이 있을 때만 포맷·진단을 등록해, 엉뚱한 폴더에서 확장이 개입하는 것을 막을 수 있습니다.biome.lsp.bin: 모노레포에서 특정 패키지의 Biome 바이너리를 가리킬 때 유용합니다.
멀티 루트 워크스페이스에서는 폴더마다 Biome 인스턴스가 생기므로, 각 패키지에 biome.json을 두는 모노레포 구조와 잘 맞습니다.
JetBrains 통합(WebStorm 등)
Biome 팀은 IntelliJ 계열용 플러그인(biomejs/biome-intellij)을 제공합니다. JetBrains Marketplace에서 “Biome”을 검색해 설치합니다. WebStorm 등 최신 IDE 버전과의 호환 범위는 플러그인 페이지의 릴리스 노트를 확인하세요.
동작 방식과 설정 요지
플러그인은 프로젝트 로컬의 node_modules에 설치된 biome을 우선 탐지하는 방식이 일반적입니다. 팀원마다 전역 설치에 의존하지 않도록, @biomejs/biome를 devDependencies에 고정해 두는 것이 중요합니다.
설정에서 LSP 기반 포맷을 활성화해야 포맷터로 일관되게 동작하는 경우가 많습니다. 저장 시 자동 수정을 켜려면 IDE의 “저장 시 액션” 또는 플러그인 옵션을 함께 조정합니다. VS Code와 마찬가지로 CLI와 IDE의 Biome 버전 불일치가 가장 흔한 불일치 원인이므로, 버전을 레포에서 통일합니다.
CI/CD 파이프라인 구성
biome ci를 쓰는 이유
CI에서는 파일을 수정하면 안 되므로 biome ci가 적합합니다. 포맷터·린터·임포트 정리를 읽기 전용으로 실행하며, 문제가 있으면 비제로 종료되어 파이프라인이 실패합니다.
npx @biomejs/biome ci .
GitHub Actions 예시
name: Biome
on:
pull_request:
push:
branches: [main]
jobs:
biome:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
- run: npm ci
- run: npx @biomejs/biome ci .
변경 분만 검사하기
대규모 저장소에서는 PR에 포함된 변경 파일만 검사해 시간을 줄일 수 있습니다. Biome CLI의 --changed 또는 --since 계열 옵션을 사용하면, 기본 브랜치 대비 차이만 대상으로 삼을 수 있습니다. 정확한 플래그 이름과 동작은 사용 중인 Biome 버전의 biome ci --help를 기준으로 삼으세요.
캐시 전략으로는 node_modules 캐시, 또는 Biome 바이너리 설치 캐시를 활용하는 방법이 있습니다. 팀에서는 로컬 npm run check와 CI biome ci가 같은 버전을 쓰는지 먼저 확인하는 것이 좋습니다.
모노레포 지원
Biome v2 이후 모노레포를 공식적으로 고려한 설정 모델이 정리되었습니다. 핵심은 루트 biome.json을 기준(base)으로 두고, 패키지별로 세부 설정을 중첩하는 것입니다.
루트와 패키지별 설정
루트에 공통 규칙·포맷 폭을 정의합니다.
{
"formatter": { "lineWidth": 100, "indentStyle": "space", "indentWidth": 2 },
"linter": { "enabled": true, "rules": { "recommended": true } }
}
특정 패키지(packages/logger 등)에만 다른 규칙을 주려면 해당 폴더에 biome.json을 두며, 루트 설정을 상속합니다. 공식 문서에서는 "extends": "//" 마이크로문법으로 루트 구성을 물려받는 패턴이 소개됩니다. 코드 생성물 폴더처럼 포맷을 끄고 싶다면 해당 패키지에서 formatter.enabled를 false로 두는 식으로 조정합니다.
extends로 공통 파일·npm 패키지 공유
팀 내부 공통 규칙을 common.json으로 두고 여러 앱에서 extends로 불러올 수 있습니다. 더 나아가 npm 패키지의 exports에 Biome 설정을 노출해 @org/shared-configs/biome 형태로 재사용하는 방법도 지원됩니다. 이때 패키지가 node_modules에 올바르게 설치되어 있어야 하며, 해석 기준은 실행 위치(워킹 디렉터리)에 따릅니다.
실행 위치와 탐색 규칙
Biome은 가장 가까운 biome.json을 파일 경로 쪽에서 탐색합니다. 루트에서 한 번에 전체를 돌릴지, 패키지별로 스크립트를 둘지는 팀 워크플로에 맞게 선택합니다. 루트에서 통합 실행 시에는 각 패키지의 중첩 설정이 기대대로 적용되는지 샘플 PR로 검증하는 것이 좋습니다.
트러블슈팅과 운영 팁
- 버전 불일치: 로컬·CI·IDE가 서로 다른 Biome을 쓰면 진단 결과가 달라집니다.
package.json과 lockfile로 버전을 고정하며, IDE는biome.lsp.bin으로 명시할 수 있습니다. - Prettier와 탭/스페이스 불일치: Biome은 기본값이 Prettier와 다를 수 있습니다.
migrate prettier후에도 팀 스타일이 남아 있다면formatter.indentStyle·javascript.formatter를 다시 확인합니다. - 규칙 과다: 마이그레이션 직후 경고가 폭증하면,
recommended에서 출발해 카테고리별로 단계적으로 켜는 것이 유지보수에 유리합니다. - 대용량 파일: CI에서 특정 생성 파일이 분석에 포함되면 시간이 늘어납니다.
files.ignore와 VCS ignore 연동으로 제외합니다. - 저장해도 Biome으로 포맷되지 않음:
editor.defaultFormatter를 Biome으로 바꿔도"[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }같은 언어별 설정이 남아 있으면 그쪽이 이깁니다. 사용자 설정과 워크스페이스 설정 양쪽을 확인하세요. 마이그레이션 직후 “어떤 파일은 되고 어떤 파일은 안 된다”는 제보의 대부분이 이 원인입니다. - Tailwind 클래스 정렬:
prettier-plugin-tailwindcss를 쓰던 팀은 nursery 규칙useSortedClasses로 대체할 수 있습니다. 실험 규칙이라 정렬 결과가 플러그인과 완전히 같지 않을 수 있으니, 도입 시 한 번에 전체 파일을 정렬하는 커밋을 따로 만들어 리뷰 노이즈를 줄입니다. - 모노레포에서 중복 실행: 패키지마다
biome check를 돌리는 스크립트를 루트에서 또 호출하면 같은 파일을 여러 번 검사합니다. 루트에서 한 번 실행하고 중첩biome.json이 패키지별 차이를 처리하게 두는 편이 빠릅니다.
자주 묻는 질문 (FAQ)
Q. Biome 포맷 결과가 Prettier와 완전히 같나요?
A. 대부분의 코드에서 같게 나오도록 설계됐지만 드물게 차이가 있습니다(긴 체인 호출 줄바꿈, 일부 JSX 경계 등). 전환할 때는 biome format --write로 전체를 한 번 포맷한 커밋을 따로 만들고, 이후 PR에서는 포맷 diff가 섞이지 않게 하는 것이 리뷰 부담을 줄이는 방법입니다.
Q. 기존 eslint-disable 주석은 어떻게 되나요?
A. Biome은 eslint-disable 주석을 인식하지 않으므로, 마이그레이션 후 그동안 숨겨져 있던 경고가 드러날 수 있습니다. 여전히 필요한 예외만 biome-ignore lint/<카테고리>/<규칙>: 사유 형식으로 옮기고, 나머지는 이번 기회에 정리하는 편이 낫습니다.