Biome으로 ESLint·Prettier 옮기기: migrate 명령 결과, 옮겨지지 않는 규칙, unsafe 수정 검토
예제 프로젝트에 Biome 2.5를 실제로 적용하며 확인한 변환 결과와 diff

핵심 요약
- biome migrate eslint/prettier는 legacy·flat 설정을 읽어 옮기고, 옮기지 못한 규칙을 목록으로 알려 준다.
- 예제에서 7개 규칙 중 max-depth가 "아직 구현되지 않음"으로 남았고, 생성된 설정은 vcs 연동이 꺼져 있었다.
- check --write는 safe 수정만 적용하며, unsafe 수정은 --unsafe를 줘야 적용되고 의미가 바뀔 수 있다.
- unsafe 수정은 console.log 줄 삭제, == → === 변경처럼 동작을 바꿀 수 있으므로 규칙별 커밋으로 나눠 검토한다.
린트는 ESLint, 포맷은 Prettier로 나눠 쓰면 설정 파일이 흩어지고, 에디터 저장 시 두 도구가 따로 돌며, CI도 두 번 실행된다. Biome은 포맷터와 린터를 한 실행 파일로 합친 도구이고, 기존 설정을 옮기는 migrate 명령을 제공한다. 이 글은 ESLint flat config와 Prettier 설정을 가진 예제 프로젝트에 Biome 2.5.15를 실제로 적용하며 확인한 결과를 정리한다. 아래 출력은 모두 2026-10-11에 Windows에서 실행한 결과다.
얻는 것과 잃는 것
| 구분 | 내용 |
|---|---|
| 얻는 것 | 설정 파일 하나(biome.json), 저장 시 한 번 실행, CI 단계 하나 |
| 잃는 것 | ESLint 플러그인 생태계 일부. 문서상 플러그인 규칙은 마이그레이션 후 biome.json에서 직접 설정해야 하고, 대응 규칙이 없을 수 있다 |
| 판단 기준 | 타입 정보를 쓰는 규칙이나 프레임워크 전용 플러그인에 의존도가 높으면 포맷부터 옮기고 린트는 병행한다 |
마이그레이션 순서
- 별도 브랜치에서
@biomejs/biome을 개발 의존성으로 설치하고biome init으로 설정 파일을 만든다. biome migrate eslint --write,biome migrate prettier --write로 기존 설정을 옮긴다.- 출력에 나온 "옮기지 못한 규칙" 목록마다 대응 규칙을 찾을지, ESLint에 남길지, 버릴지 정한다.
biome ci로 수정 없이 결과를 확인하고, 포맷 커밋과 린트 수정 커밋을 나눠 적용한다.- 에디터의 ESLint·Prettier 저장 시 실행을 끄고, CI 명령을
biome ci로 바꾼다.
예제 프로젝트의 기존 설정
export default [
{
files: ['src/**/*.js'],
rules: {
'no-unused-vars': 'error',
eqeqeq: 'error',
'no-var': 'error',
'prefer-const': 'error',
'no-console': 'warn',
'no-param-reassign': 'error',
'max-depth': ['error', 3],
},
},
]{ "semi": false, "singleQuote": true, "printWidth": 100, "trailingComma": "all" }var DISCOUNT = 0.1;
export function total(items, coupon) {
let sum = 0
for (let i = 0; i < items.length; i++) { sum += items[i].price * items[i].qty }
if (coupon == "WELCOME") { sum = sum * (1 - DISCOUNT) }
const unused = 42;
console.log("total", sum)
return sum
}migrate 명령 결과
문서에 따르면 ESLint 마이그레이션은 legacy와 flat 설정을 모두 읽고 extends를 따라가며, 기존 biome.json의 린터 설정을 교체하고 프리셋을 none으로 둔 뒤 옮긴 규칙만 켠다. 기본값으로는 ESLint 규칙과 동일한 규칙만 옮기고, 영감을 받은 수준의 규칙까지 옮기려면 --include-inspired를 붙인다.
$ npx biome migrate eslint --write
i Unsupported rules (0 incompatible with formatter, 0 made obsolete by the formatter, 0 covered by a formatter option, 1 not yet implemented, 0 unknown source):
i These rules have not yet been implemented:
- max-depth
i Migration results:
- ...\biome.json: configuration successfully migrated.7개 규칙 중 6개가 옮겨졌고 max-depth는 "아직 구현되지 않음"으로 남았다. 이 목록이 사람이 직접 판단해야 할 규칙이다. 생성된 설정의 린터 부분은 다음과 같다.
"linter": {
"enabled": true,
"rules": {
"preset": "none",
"correctness": { "noUnusedVariables": "error" },
"style": { "noParameterAssign": "error", "useConst": "error" },
"suspicious": { "noConsole": "warn", "noDoubleEquals": "error", "noVar": "error" }
},
"includes": ["src/**/*.js"]
},
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }Prettier 마이그레이션은 semi: false가 "semicolons": "asNeeded"로, singleQuote가 "quoteStyle": "single"로, printWidth: 100이 "lineWidth": 100으로 옮겨졌다. 한 가지 놓치기 쉬운 부분은 vcs.enabled: false다. 문서에 따르면 ESLint와 Prettier는 기본으로 .gitignore 항목을 건너뛰지만 Biome은 VCS 연동을 켰을 때만 그렇게 한다. 그대로 두면 빌드 산출물까지 검사 대상이 될 수 있으므로 "vcs": { "enabled": true, "useIgnoreFile": true }로 바꾼다.
safe 수정과 unsafe 수정은 결과가 다르다
biome ci로 먼저 수정 없이 결과를 확인하면 7개 오류와 1개 경고가 나온다. 그다음 check --write로 수정을 적용했다.
$ npx biome check --write .
src\order.js:11:3 lint/suspicious/noConsole FIXABLE
! Don't use console.
src\order.js:7:14 lint/suspicious/noDoubleEquals FIXABLE
× Using == may be unsafe if you are relying on type coercion.
src\order.js:1:1 lint/suspicious/noVar FIXABLE
× Use let or const instead of var.
src\order.js:10:9 lint/correctness/noUnusedVariables FIXABLE
× This variable unused is unused.
Checked 5 files in 9ms. Fixed 4 files.
Found 3 errors.
Found 1 warning."Fixed 4 files"라고 나오지만 실제로 바뀐 것은 포맷(세미콜론, 따옴표, 줄바꿈)뿐이고, FIXABLE로 표시된 린트 문제는 그대로 남았다. Biome 문서는 safe 수정은 코드의 의미를 바꾸지 않아 검토 없이 적용할 수 있고, unsafe 수정은 의미를 바꿀 수 있어 직접 검토하라고 설명한다. unsafe 수정은 --write --unsafe를 함께 줘야 적용된다.
$ npx biome check --write --unsafe . # 적용 후 src/order.js
const DISCOUNT = 0.1
export function total(items, coupon) {
let sum = 0
for (let i = 0; i < items.length; i++) {
sum += items[i].price * items[i].qty
}
if (coupon === 'WELCOME') {
sum = sum * (1 - DISCOUNT)
}
const _unused = 42
return sum
}unsafe 수정 결과를 하나씩 보면 왜 검토가 필요한지 드러난다.
| 규칙 | unsafe 수정 내용 | 검토 포인트 |
|---|---|---|
| noVar | var → const | 재할당이 없으니 안전하다 |
| noDoubleEquals | == → === | coupon이 숫자나 다른 타입으로 들어오는 호출이 있으면 동작이 바뀐다 |
| noUnusedVariables | unused → _unused로 이름만 변경 | 변수를 지운 것이 아니라 "의도적으로 안 쓴다"는 표시를 붙였다. 정말 필요 없는지 사람이 판단한다 |
| noConsole | console.log 줄 삭제 | 로그를 다른 곳에서 수집하고 있었다면 운영 로그가 사라진다 |
대량의 기존 코드에 처음 적용할 때는 포맷과 safe 수정만 한 커밋으로, unsafe 수정은 규칙별로 별도 커밋으로 나눠 리뷰한다. 수정 후에는 테스트를 반드시 돌린다. 모든 수정을 끝낸 뒤 biome ci는 "No fixes applied."와 함께 종료 코드 0을 냈다.
에디터·CI·커밋 훅 전환
- 에디터에서 Biome 확장을 기본 포맷터로 지정하고, ESLint·Prettier 확장의 저장 시 실행을 끈다. 두 도구가 동시에 저장 훅을 잡으면 서로 다르게 포맷해 파일이 계속 바뀐다.
- CI에서는 수정 없이 검사만 하는
biome ci를 쓴다. - lint-staged를 쓴다면 ESLint·Prettier 명령을
biome check --write로 바꾼다. 커밋 전 검사 구성은 ESLint·Prettier·tsc 커밋 전 자동화를 참고한다. - 되돌릴 때를 대비해 기존 설정 파일은 한동안 남겨 두고, README와 PR 템플릿의 명령을 함께 바꾼다.
AI 코딩 도구가 만든 코드에 이 검사를 자동으로 붙이는 방법은 Claude Code 훅으로 자동 검증 붙이기에서 다룬다.
초보자가 자주 실수하는 포인트
- migrate 출력의 "옮기지 못한 규칙" 목록을 확인하지 않는다.
- vcs 연동을 켜지 않아 .gitignore 대상 파일까지 검사한다.
- "Fixed N files" 메시지만 보고 린트 문제가 모두 고쳐졌다고 생각한다.
- --unsafe 결과를 검토 없이 한 커밋에 섞는다.
체크리스트
- 별도 브랜치에서 migrate eslint·prettier를 실행했다.
- 옮기지 못한 규칙마다 처리 방침을 정했다.
- vcs 연동과 ignore 설정을 확인했다.
- 포맷·safe 수정과 unsafe 수정을 별도 커밋으로 나눴다.
- CI 명령을 biome ci로 바꾸고 에디터 저장 훅 충돌을 정리했다.
자주 묻는 질문
ESLint와 Biome을 같이 써도 되나요?
가능합니다. 포맷은 Biome, 타입 정보가 필요한 규칙이나 플러그인 규칙은 ESLint처럼 역할을 나누고, 같은 규칙이 양쪽에 중복 정의되지 않게 합니다.
--include-inspired는 언제 쓰나요?
기본값은 ESLint와 동일한 규칙만 옮깁니다. 비슷하지만 동작이 완전히 같지 않은 규칙까지 옮기려면 이 옵션을 붙이는데, 문서상 일부 옵션은 구현되지 않았거나 의도적으로 다를 수 있으므로 옮긴 뒤 결과를 확인합니다.
참고 자료 · 검증 기준
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 위 참고 자료를 바탕으로 정리했으며, 내용은 운영 과정에서 순차적으로 보완될 수 있습니다.