Biome 완벽 가이드 — 빠른 린터·포맷터와 팀 운영

Biome 완벽 가이드 — 빠른 린터·포맷터와 팀 운영

이 글의 핵심

Biome은 JavaScript·TypeScript·JSON·CSS 등을 한 바이너리로 다루는 린터·포맷터입니다. 이 글에서는 Rust 기반 설계가 주는 이점, biome.json 구성, 린트와 포맷 워크플로, 임포트 정렬, Git Hooks 연동, ESLint·Prettier 전환, 팀 단위 거버넌스까지 실무 중심으로 설명합니다.

이 글의 핵심

Biome은 프런트엔드·풀스택 저장소에서 흔히 분리되어 있던 린터(linter)포맷터(formatter), 그리고 임포트 정리(import sorting)를 하나의 CLI와 설정 파일로 묶는 도구입니다. 구현 언어가 Rust라는 점은 부수적 특징이 아니라, 파싱·규칙 적용·출력 포맷까지 단일 네이티브 바이너리에서 처리하려는 설계 방향과 맞닿아 있습니다. 팀 입장에서는 “같은 저장소를 여러 번 훑지 않는다”는 점이 로컬 개발과 CI 시간을 동시에 줄이는 요인이 됩니다.

이 글에서는 Biome의 핵심 개념, biome.json 구성 요소, 린트와 포맷의 역할 분리, 임포트 정렬, Git Hooks와의 연동, ESLint·Prettier 마이그레이션, 마지막으로 실전 팀 규칙까지를 순서대로 다룹니다. 이미 biome-linter-complete-guide 등으로 기본기를 익힌 독자라도, 본문의 훅·거버넌스 절은 저장소 운영에 바로 옮길 수 있도록 구성했습니다.


1. Biome의 핵심 개념

1.1 단일 엔진과 일관성

Biome이 강조하는 철학은 한 엔진이 진단과 포맷을 모두 담당한다는 점입니다. 과거에는 ESLint가 제안한 수정과 Prettier가 다시 쓴 코드가 미세하게 어긋나 “린트 통과 → 포맷 → 다시 린트 실패” 같은 순환이 생기기도 했습니다. Biome은 기본적으로 이런 규칙 충돌을 같은 도구 안에서 조정하려고 하며, 팀은 biome.json 한곳에서 스타일과 규칙 심각도를 맞춥니다.

1.2 Rust 구현이 주는 실무적 의미

“Rust로 만들었다”는 표현은 마케팅 문구로만 읽기 어렵습니다. 네이티브 바이너리로 배포되므로 Node.js 버전과 무관하게 동일한 CLI를 고정하기 쉽고, CI 에이전트에서 콜드 스타트 비용을 줄이는 데도 유리한 편입니다. 다만 마이그레이션 서브커맨드처럼 기존 ESLint 설정을 읽어들이는 경로에서는 Node 생태계와의 상호운용을 위해 추가 의존이 개입할 수 있으므로, 팀 문서에 “어떤 명령이 Node를 요구하는지”를 적어 두는 것이 좋습니다.

1.3 LSP와 에디터 통합

Biome는 Language Server Protocol을 통해 VS Code·JetBrains 등에서 진단·수정·포맷을 제공합니다. CLI로 강제한 규칙과 에디터에서 보이는 메시지가 어긋나지 않도록, 프로젝트 로컬에 @biomejs/biome를 고정하고 IDE가 같은 바이너리를 바라보게 하는 것이 운영의 기본입니다.


2. 설치와 기본 명령

프로젝트 루트에서 개발 의존성으로 설치하고 초기 설정 파일을 생성합니다. 버전은 lockfile로 고정하는 것을 권장합니다.

npm install --save-dev --save-exact @biomejs/biome
npx @biomejs/biome init

자주 쓰는 npm 스크립트 예시는 다음과 같습니다. 팀원이 동일한 동작을 쓰도록 스크립트 이름을 문서화해 두는 것이 중요합니다.

{
  "scripts": {
    "check": "biome check .",
    "check:write": "biome check --write .",
    "format": "biome format --write .",
    "lint": "biome lint .",
    "ci": "biome ci ."
  }
}
  • biome check: 포맷·린트·소스 액션(임포트 정리 등)을 묶어 실행하는 상위 명령입니다. 로컬에서는 --write로 자동 수정까지 적용하는 경우가 많습니다.
  • biome ci: CI에서 파일을 수정하지 않고 검증만 할 때 씁니다. 실패 시 비제로 종료되어 파이프라인을 막습니다.
  • biome lint / biome format: 역할을 분리해 실행하고 싶을 때 사용합니다. 스크립트를 나누면 팀 온보딩 시 의도가 분명해집니다.

3. biome.json 설정

3.1 스키마와 최소 구성

$schema에는 설치한 버전에 맞는 JSON 스키마 URL을 지정하거나, ./node_modules/@biomejs/biome/configuration_schema.json을 가리켜 에디터 자동완성 품질을 높일 수 있습니다. 아래는 Biome 2.x 계열에서 흔히 쓰는 출발점입니다. 세부 숫자는 팀 합의에 맞게 조정합니다.

{
  "$schema": "https://biomejs.dev/schemas/2.4.12/schema.json",
  "files": {
    "ignoreUnknown": false,
    "includes": ["**", "!!**/dist", "!!**/build", "!!**/.next", "!!**/coverage"]
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "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"
    }
  }
}

files.includes포함·제외를 동시에 표현하는 패턴이 많습니다. !! 접두는 제외를 뜻하는 관례적 표기로 이해하면 됩니다. 빌드 산출물·캐시 디렉터리는 여기서 빼는 것과 더불어, 다음 절의 VCS 연동으로 .gitignore와 맞추는 편이 안전합니다.

3.2 VCS와 무시 규칙

로컬과 CI에서 분석 대상이 달라지면 “내 컴퓨터에서는 통과” 문제가 생깁니다. Git을 쓰는 저장소에서는 vcs 블록으로 버전 관리 클라이언트와의 정합을 맞춥니다.

{
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  }
}

3.3 overrides로 경로별 완화

테스트·스토리북·레거시 폴더만 규칙을 완화하려면 overridesinclude 패턴을 둡니다. ESLint의 overrides와 유사한 역할입니다.

{
  "overrides": [
    {
      "include": ["**/*.test.ts", "**/__tests__/**"],
      "linter": {
        "rules": {
          "suspicious": {
            "noExplicitAny": "off"
          }
        }
      }
    }
  ]
}

4. Linting과 Formatting

4.1 역할 구분

  • 포맷터는 공백·줄바꿈·따옴표처럼 표현 형식을 통일합니다.
  • 린터는 의심스러운 패턴·버그 가능성·팀 컨벤션 위반을 진단합니다. 일부 규칙은 자동 수정과 연결됩니다.

팀 문서에는 “포맷은 무조건 Biome, 논리 규칙은 린트 레벨로 관리”처럼 책임 경계를 한 줄이라도 적어 두면 코드 리뷰에서 논쟁이 줄어듭니다.

4.2 check 한 번에 묶는 이유

로컬에서는 biome check --write .포맷·린트·소스 액션을 한 번에 돌리고 커밋하는 흐름이 효율적입니다. 반대로 CI에서는 biome ci처럼 읽기 전용 경로를 써서 아티팩트를 바꾸지 않는다는 원칙을 지키는 것이 좋습니다.

4.3 안전한 수정과 주의할 규칙

일부 자동 수정은 의미를 바꿀 수 있어 안전(safe) / 비안전(unsafe)으로 나뉩니다. 팀 정책으로 “PR에서는 safe만 자동 적용”처럼 단계를 둘 수 있습니다. 주석으로 규칙을 끄던 ESLint 시절과 달리, Biome 규칙 ID와 주석 문법을 맞춰 기술 부채를 드러내는 방향으로 정리하는 편이 장기적으로 유리합니다.


5. Import 정렬

5.1 Assist와 소스 액션

Biome 2.x에서는 Assist 아래 소스(source) 액션으로 임포트 정리·권장 리팩터링을 묶는 방식이 일반적입니다. 앞 절 biome.jsonassist.actions.source.recommended가 이 축을 켭니다. 프로젝트 스타일에 따라 특정 액션만 켜거나 끄는 식으로 조정할 수 있습니다.

5.2 VS Code에서 저장 시 정리

에디터에서 저장 시 한 번에 맞추려면 codeActionsOnSave에 Biome 관련 액션을 넣습니다. 팀 전원이 같은 설정을 쓰도록 .vscode/settings.json을 저장소에 커밋하는 방식이 널리 쓰입니다.

{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

source.fixAll.biome안전한 수정 위주입니다. 임포트 정리가 대량으로 바뀌면 diff가 커질 수 있으므로, 처음 도입할 때는 한 번의 스타일 마이그레이션 커밋을 분리하는 전략이 리뷰 부담을 줄입니다.

5.3 CLI에서의 기대치

biome check --write는 설정과 버전에 따라 임포트 정리를 포함할 수 있습니다. 팀에서 “임포트 정리는 포맷의 일부”로 볼지, “별 스크립트로만 돌린다”고 할지 정책을 합의해야 합니다. 정책이 없으면 PR마다 줄 순서만 바뀌는 diff가 반복됩니다.


6. Git Hooks 통합

Git Hook은 커밋 직전에 최소한의 품질 게이트를 거는 도구입니다. Biome을 붙일 때 흔한 조합은 Husky + lint-staged, 또는 Lefthook, 혹은 pre-commit 프레임워크입니다. 공통 원칙은 다음과 같습니다.

  • 스테이징된 파일만 대상으로 하여 전체 저장소 스캔 시간을 줄인다.
  • Hook에서는 biome check --write로 자동 수정을 허용할지, 검증만 할지 팀 규칙으로 정한다.
  • CI의 biome ci와 명령 체계를 맞춰 “로컬에서 고쳐도 CI에서 실패” 상황을 방지한다.

6.1 Husky와 lint-staged

lint-staged는 스테이징된 파일 경로만 인자로 넘깁니다. package.json 예시는 다음과 같습니다.

{
  "lint-staged": {
    "*.{js,jsx,ts,tsx,json,css}": ["biome check --write --no-errors-on-unmatched"]
  }
}

--no-errors-on-unmatched는 Biome이 해당 패턴에 맞는 파일이 없을 때 불필요하게 실패하지 않게 하는 데 도움이 됩니다. 팀에서 사용하는 확장자 목록은 프로젝트에 맞게 조정합니다.

Husky pre-commit에서 npx lint-staged를 호출하는 전제입니다. Husky 설치 절차는 공식 문서를 따르되, Windows·macOS·Linux 모두에서 동작하는지 CI 외에도 한 번 확인하는 것이 좋습니다.

6.2 Lefthook

YAML로 훅을 선언하고 싶다면 Lefthook을 쓸 수 있습니다. 모노레포에서 명령을 루트에만 둘지, 패키지마다 둘지에 따라 설정 파일 위치가 달라집니다.

# lefthook.yml 예시
pre-commit:
  commands:
    biome:
      glob: "*.{js,jsx,ts,tsx,json,css}"
      run: npx @biomejs/biome check --write {staged_files}

{staged_files} 치환 문법은 Lefthook 버전에 따라 다를 수 있으므로, 사용 중인 버전 문서를 기준으로 맞춥니다.

6.3 훅에서의 운영 원칙

  • 시간 제한: 거대 저장소에서는 훅이 길어져 개발자가 --no-verify를 남용할 수 있습니다. 캐시·범위 축소·선택적 규칙으로 10~30초 이내를 목표로 삼는 팀이 많습니다.
  • 포매터 분리 커밋: 첫 도입 시 “포맷만” 커밋을 분리하면 리뷰어가 로직 변경과 스타일 변경을 구분할 수 있습니다.
  • 문서화: 신입 온보딩 문서에 “훅 실패 시 어떤 명령을 실행하는지”를 고정합니다.

7. ESLint / Prettier 마이그레이션

7.1 자동 마이그레이션

Biome은 기존 설정을 읽어 biome.json에 반영하는 명령을 제공합니다.

npx @biomejs/biome migrate eslint --write
npx @biomejs/biome migrate prettier --write

ESLint는 레거시 .eslintrc와 flat config 모두 지원 범위가 있으나, 플러그인 해석을 위해 Node가 필요할 수 있고, YAML 형식 설정은 제약이 있을 수 있습니다. Prettier도 JSON 기반이 가장 안전하게 이전되는 편입니다. JSON5·YAML·TOML 등은 한번 JSON으로 정리한 뒤 마이그레이션하는 방식을 권장합니다.

7.2 규칙 1:1 대응을 기대하지 않기

Biome 규칙 이름과 ESLint 규칙 이름은 다릅니다. “영감을 받은(inspired)” 규칙도 완전 동일하지 않을 수 있습니다. 마이그레이션 후에는 biome check 전체 결과를 기준으로 팀 합의를 다시 잡습니다.

7.3 주석과 점진적 전환

eslint-disable 주석에 의존하던 코드는 Biome 주석 체계에 맞게 옮겨야 합니다. 이 과정에서 숨겨 두었던 냄새가 드러날 수 있으므로, 스프린트 단위로 영역을 나누어 정리하는 편이 안전합니다. 전환 기간에 ESLint와 병행할 수는 있으나, 장기적으로는 단일 도구로 수렴하지 않으면 규칙 이중화로 혼란이 커집니다.


8. 실전 팀 규칙 설정

8.1 거버넌스 원칙

  • 버전 고정: package.json@biomejs/biome와 lockfile, CI 이미지의 Node 버전까지 포함해 “재현 가능한 조합”을 적습니다.
  • 심각도 정책: error는 CI에서 반드시 막을 것, warn은 기간 한정으로만 허용할 것 등 경고의 생명주기를 정합니다.
  • 예외 절차: overrides로 폴더를 완화할 때는 승인자·만료 조건을 둡니다. 예외가 누적되면 설정이 쌓여 유지보수가 어려워집니다.

8.2 모노레포에서의 패턴

루트 biome.json에 공통 폭·기본 규칙을 두고, 패키지별로 중첩 설정을 두는 방식이 흔합니다. extends로 내부 공유 패키지의 설정을 가져오는 경우, 해석 경로와 배포 타이밍(내부 패키지 버전)을 릴리스 프로세스에 묶습니다.

8.3 코드 리뷰 체크리스트

  • 포맷·임포트·린트가 같은 Biome 버전에서 돌았는가.
  • 생성 코드·벤더 디렉터리가 files·vcs로 제외되었는가.
  • 신규 규칙 도입 시 기존 PR과 충돌하지 않도록 롤아웃 일정이 있는가.

8.4 문서화

기여 가이드에 다음을 고정합니다: 로컬 명령(npm run check), CI 명령(biome ci), 에디터 확장 이름, 훅 실패 시 대처, 마이그레이션 담당자 연락처. 도구는 사람이 쓰는 것이므로 문장으로 남는 표준이 있어야 합니다.


9. 트러블슈팅

  • 버전 불일치: 로컬·CI·IDE가 서로 다른 Biome을 쓰면 진단이 달라집니다. IDE에서 biome.lsp.bin으로 로컬 바이너리를 명시하는 방법을 검토합니다.
  • 탭 vs 스페이스: Prettier 습관이 남아 있으면 formatter.indentStylejavascript.formatter를 다시 확인합니다.
  • 규칙 폭증: 마이그레이션 직후 경고가 많으면 recommended에서 출발해 카테고리별로 단계적으로 켭니다.
  • 대용량 생성 파일: 분석에 포함되면 시간이 늘어납니다. files 무시와 VCS ignore를 함께 점검합니다.

10. 정리

Biome은 Rust 기반 단일 엔진으로 린트·포맷·임포트 정리를 묶어, 반복 실행 비용과 규칙 충돌을 줄이려는 도구입니다. biome.json으로 표준을 고정하고, biome check / biome ci로 로컬과 CI의 책임을 나누며, Git Hook으로 스테이징된 변경만 빠르게 검증하는 구성이 실무에서 많이 쓰입니다. ESLint·Prettier에서 옮길 때는 자동 마이그레이션 이후의 규칙 갭을 팀 합의로 정리하고, 장기적으로는 단일 도구에 수렴하는 로드맵을 권장합니다.

배포 전에는 git addgit commit, git push를 마친 뒤 npm run deploy를 실행하는 저장소 규칙을 따르십시오.