Biome을 슬쩍 먹어보자 (feat.eslint flat-config)
2025-07-05

우연히 FE 아티클을 보던중 2025 React Libraries글의 저자가 2025의 라이징스타는 biome이 될것이라고 한걸 봤다. 서치를좀 해보니 이게왠걸 biome이 기존의 rome을 fork해서 만들었다고 한다. 예전에 rome은 frontend 종합 툴체인이라는 글을 봤던 기억이있고, 그때는 별 흥미가 안갔지만 최근 프로젝트에서 여러 세팅을 바꾸고 줄인 경험을 하다보니 지금은 그때와다르게 관심이 갔다. 그래서 먹어보기로했다.


biome! 왜 주목할만하다고 했을까?

biome은 Eslint + Prettier 종합 툴체인이다. eslint, prettier의 분리된 설정파일을 하나로 합쳐 설정복잡도를 줄일 수 있고 무엇보다 내부적으로 Rust를 사용하기때문에 기존 린팅작업시에도 몇배는 더 빠른 속도를 낸다고한다. 설정복잡도도 줄이는데 더 빠르기까지 안먹어볼수가 없다.


기존 Eslint + Prettier의 불편함은 뭐길래?

개인마다 느낀 불편함은 다르겠지만 나는 2가지정도의 불편함을 느꼈다.

  • 설정파일이 나뉨
  • eslint설정시 거진 필수급으로 있어야할 plugin이 꽤나 있어서 eslint설정파일 및 package.json 의존성이 장황해짐

Eslint와 Prettier설정은 코드의 품질과 일관성을 높이고 안정적인 서비스를 유지하기위해 현대 웹개발에선 거진 필수요소다. 둘은 다른 기능을 수행해 설정파일이 나뉘어져있는게 당연하고 prettier나 eslint 둘다 설정해놓으면 바꿀일이 드물어 괜찮다곤 하지만. 어떻게보면 같은 목적을 갖고있기에 같은 설정 파일내 있는게 좀더 깔끔하지 않을까라고 문득문득 생각은 했었다.

사실상 가장큰 찝찝함은 package.json이 장황해지는거다. biome으로 마이그레이션 하기위한 프로젝트를 따로 만들고, React + Typescript기준 최소한의 의존성만 설치했는데 다음과 같다.

"devDependencies": {
  "prettier": "^3.2.5",
  "eslint": "^9.30.1",
  "typescript-eslint": "^8.35.1",
  "@eslint/js": "^9.30.1",
  "eslint-import-resolver-typescript": "^4.4.4",
  "eslint-plugin-import-x": "^4.16.1",
  "eslint-plugin-prettier": "^5.5.1",
  "eslint-plugin-react": "^7.37.5",
  "eslint-plugin-react-hooks": "^5.2.0",
  "@typescript-eslint/eslint-plugin": "^8.35.1",
  "@typescript-eslint/parser": "^8.35.1",
  "eslint-config-prettier": "^10.1.5",
  "eslint-plugin-prettier": "^5.5.1",
}

아는 내용일수도 있지만 각각 의존성에 대해 간단히 살펴보자면


eslint도 flatConfig이라는 설정이 새로 도입되었다. 알고는 있었지만 적용해본적은 없었는데 biome 마이그레이션을 위해 설정파일을 만들면서 에러처리를 좀 하다보니 얻어가는게 있었다.

특히 외부라이브러리를 import하는데 불구하고 eslint가 path를 읽지못해 no-unresolved 에러를 자꾸 뱉어서 의아해 찾아보니, 위의 문제때문이었고 이외에도 여러 이슈를 해결한 플러그인이라고헤 import-x플러그인을 적용했다. 완성된 설정은 다음과 같고 각 옵션 및 파서옵션은 공식문서를 참조하면 좋을것같다!

// eslint.config.js
import js from '@eslint/js';
import tsEslintPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import { globalIgnores } from 'eslint/config';
import prettierConfig from 'eslint-config-prettier';
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';
import importPlugin from 'eslint-plugin-import-x';
import prettierPlugin from 'eslint-plugin-prettier/recommended';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import tsEslint from 'typescript-eslint';

export default tsEslint.config([
  globalIgnores(['node_modules/*', '.react-router']),
  {
      files: ['**/*.{ts,tsx}'],
      settings: {
       'import-x/resolver-next': [createTypeScriptImportResolver({})],
      },
      languageOptions: {
      parser: tsParser,
      parserOptions: {
          ecmaVersion: 'latest',
          projectService: true,
      },
      },
      plugins: {
          react,
          '@typescript-eslint': tsEslintPlugin,
      },
      extends: [
          js.configs.recommended,
          tsEslint.configs.recommended,
          importPlugin.flatConfigs.recommended,
          reactHooks.configs['recommended-latest'],
      ],
      rules: {
          ...Custom
      },
  },
  prettierPlugin,
  prettierConfig // 충돌방지를 위한거니 항상 마지막에,
]);

// .prettierrc

{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 100,
  "tabWidth": 2,
  "endOfLine": "lf",
  "arrowParens": "always"
}

flatConfig 덕분에 설정파일 자체는 과거 eslint를 설정할때보다 훨씬 깔끔하게는 나왔지만 문제의 핵심인 package.json의 의존성이 길어짐은 어쩔수없었다.


biome을 적용해보자!

# install
pnpm add -D -E @biomejs/biome
# init
pnpm exec biome init # create biome.json

init시에 biome.json이 생성되면서 터미널에 친절하게 다음스텝을 가이드해준다. 우선 eslint및 prettier는 보통 IDE와 통합을 해서 사용하기때문에 vscode기준 biome을 통합해보자.

1. biome extension 설치

설치를 하고 공식문서를 보면 vscode의 setting precedence를 따른다고 되어있다. 즉 .vscode/settings.json의 적용 순서를 따른다는거다.

기본적으로 "biome.enabled" = true기 때문에 biome.json이 없는곳에서도 biome-LSP 연결을 시도해서 아마도 biome.json이 없는 프로젝트라면 vscode 하단에 계속 오류를 뱉을것이다.

image
연결을 시도하지만 biome.json이 없는 프로젝트다.

그래서 global/settings.json 에서 "biome.enabled" = false로 해주고 biome을 적용할 프로젝트 루트에 별도의 .vscode/settings.json을 만들어서 옵션을 설정하니 해결됐다.

// .vscode/settings.json
{
  "editor.defaultFormatter": "biomejs.biome", // 기존 esbenp.prettier-vscode 포매터에서 biome 포매터로 변경
  "biome.enabled": true,
}

공식문서에 보면 "biome.requireConfiguration"라는 옵션이 존재하는데 이는 biome.json이 있다면 biome 포매터가 등록되어서 LSP연결을 시도하고 없다면 안하는 옵션이라 해당옵션을 먼저 건드렸었는데 적용이 되지않아 issue를 찾아보니 Closed됐지만 requireConfiguration가 무시된다는 이슈가 존재했다. 그래서 위와같이 적용했다.

2. migration

IDE와 통합을 완료했으면 이제 기존 eslint+prettier 세팅을 biome.json에 합쳐야한다. 감사하게도 biome쪽에 migration을 도와주는 커맨드가 존재했다.

biome migrate eslint --write
biome migrate prettier --write

아마 biome이 global로 설치되어있지 않다면 biome 환경변수가 없을것이기 때문에 npxpnpm exec 커맨드를 이용하자.

"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
"files": { "ignoreUnknown": false },
"formatter": {
  "enabled": true,
  "formatWithErrors": false,
  "indentStyle": "space",
  "indentWidth": 2,
  "lineEnding": "lf",
  "lineWidth": 100,
  "attributePosition": "auto",
  "bracketSameLine": false,
  "bracketSpacing": true,
  "expand": "auto",
  "useEditorconfig": true
},
"linter": {
  "enabled": true,
  "rules": { "recommended": false, "style": { "useBlockStatements": "off" } },
  "includes": ["**", "!node_modules/**/*", "!.react-router/**/*"]
},
"javascript": {
  "formatter": {...Config}
},
"html": { "formatter": { "selfCloseVoidElements": "always" } },
"overrides": [...Config],
"assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } }
}

갑자기 왠 설정이 많이튀어나오나 싶겠지만 기존 plugin의 recommend 설정이 전부 풀어헤쳐졌다고 보면 된다. biome이 적용하는 rules는 모두 eslint-plugin에서 따온것이며 공식문서에서 plugin들을 확인할 수 있다.


결과

모두 검사범위를 "app/**/*"로 설정하고 package.json에 다음과같은 스크립트를 만들어 간략히 테스트를 해봤다.

"lint": "time eslint . --fix",
"biome": "time biome check --fix"
image
얼핏봐도 차이가 큰걸 알 수있다. Rust의 힘이다.

오랜만에 기존 eslint+prettier 조합을 flatConfig도 알아볼겸 초기부터 설정해가면서 biome까지 migrate해봤는데 front-end의 기술이 이제는 안정화에 접어들어서 예전처럼 새로운 기술이 나오지 않는 상황이라고 어디서 본것같은데,, 아닌거같다 당장 React19의 RSC도 등장한지는 오래됐지만 정말 놀라웠고 biome도 그렇다고 본다. 오랜만에 재밌었다 🚀