Biome로 개발자 경험 향상시키기

Biome로 개발자 경험 향상시키기

ESLint와 Prettier, 멈춰!

소개

안녕하세요! 프론트엔드 개발자 김동규입니다. 많은 개발자들이 실무에서 정적 분석 도구로 ESLint를, 코드 포매터로 Prettier를 사용하고 계실 텐데요. 저 역시 오랫동안 이 조합을 사용해왔습니다. 하지만, 더 나은 개발자 경험을 위해 약 2달 전에 Biome이라는 새로운 도구로 전환하게 되었어요.

이번 포스트에서는 왜 ESLint와 Prettier 대신 Biome를 선택하게 되었는지, 그리고 Biome가 어떻게 개발자 경험을 향상시킬 수 있는지에 대해 제가 직접 경험한 내용을 바탕으로 공유해보려고 해요.

ESLint와 Prettier

ESLint란?

ESLint는 JavaScript와 TypeScript 코드를 위한 정적 코드 분석 도구에요. 정적 코드 분석이란, 코드를 실제로 실행하지 않고 코드의 문법, 스타일, 오류 등을 점검하는 것을 말해요.

ESLint는 코드 품질을 유지하고 코딩 스타일의 일관성을 지키기 위한 목적으로 사용돼요. 또한, 폭넓은 확장성을 제공하여 프로젝트마다 커스터마이징이 가능하며, 다양한 프레임워크와 라이브러리에 맞춘 분석도 지원해요.

Prettier란?

Prettier는 코드 포매터(Formatter)로, 코드의 형식을 자동으로 정리해 주는 도구예요. 이 도구는 코딩 스타일 자동화, 개발자 간 충돌 감소, 사용자 개입 최소화를 위한 목적으로 사용되고 다양한 언어를 지원해요.

ESLint + Prettier 조합은 국룰

2024년 8월 28일 기준으로 Github start가 각각 ESLint는 24.8K, Prettier는 49K로 확인돼요. 정말 많은 개발자들의 사랑을 받고 있어요.

이러한 점은 리액트 서적을 보면 확인할 수 있어요. 예시로 김민준 저자님의 '리액트를 다루는 기술'과 김용찬 저자님의 '모던 리액트 Deep Dive'를 보면 알 수 있어요.

리액트를 다루는 기술

모던 리액트 Deep Dive

프론트엔드 로드맵으로 유명한 roadmap.sh에서도 다음과 같은 프로세스를 확인할 수 있어요.

프론트엔드 개발자로서 커리어를 시작하시는 분들은 대부분 이 조합을 사용해서 프로젝트를 진행할 거에요. 물론, 저도 마찬가지에요.

Biome, 누구세요?

Prettier를 대체하는 Biome

Biome는 JavaScript, TypeScript, JSX, TSX, 그리고 JSON을 위한 빠른 포매터로, Prettier와 97%의 호환성을 자랑하며 CI(지속적 통합) 및 개발자 시간을 절약해 줘요.

ESLint를 대체하는 Biome

Biome는 JavaScript, TypeScript, 그리고 JSX를 위한 성능이 뛰어난 린터로, ESLint, TypeScript ESLint, 그리고 기타 써드파티 라이브러리에서 가져온 266개의 규칙을 특징으로 가져요.

Biome를 사용해야 하는 6가지 이유

  • 빠름: Rust로 구축되었어요.

  • 간단함: 시작하는 데 별도의 설정이 필요하지 않아요.

  • 확장성: 크기에 상관없이 모든 코드베이스를 처리할 수 있도록 설계되었어요.

  • 최적화: 내부 통합이 잘 되어 있어 이전 작업을 재사용할 수 있어요.

  • 정보 제공: 모호한 오류 메시지를 피하고, 문제가 있을 때 정확히 어디가 문제인지와 해결 방법을 알려줘요.

  • 배터리 포함: 외부 지원없이 표준 라이브러리만으로 TypeScript와 JSX에 대해 최상의 지원을 제공해요.

아래의 사진은 Biome의 공식 홈페이지에서 제공하는 Prettier와의 퍼포먼스 비교 차트에요.

Biome 퍼포먼스

eslint-plugin-prettier 이슈

Prettier의 코드 포매팅 규칙을 ESLint의 린팅 과정에 포함해, 코드 스타일과 관련된 문제를 Prettier의 규칙에 따라 검사하고 수정할 수 있도록 해주는 'eslint-plugin-prettier'를 사용할 경우 속도 이슈가 있어요. 왜냐면, ESLint로 한 번, Prettier로 한 번 총 2번 파싱되기 때문인데요. 절대 적지 않은 비용이 들어가요. 만약, Biome를 사용한다면 고려하지 않아도 될 문제에요!

Biome 설정 방법

Biome 설정 방법은 매우 간단해요. 공식 문서에 매우 친절하게 나와 있으니, 공식 문서를 보고 팔로잉하시는 것을 추천해 드려요. 저는 총 5단계로 나누어서 설명해 드릴게요.

vite + react + ts + 멀티레포 환경에서 진행할게요.

첫 번째, Biome 라이브러리 설치

pnpm add --save-dev --save-exact @biomejs/biome

두 번째, Biome 익스텐션 설치

Biome를 효율적으로 사용하기 위해서는 Visual Studio Code(이하, VSC) 마켓스토어에서 제공하는 Biome 익스텐션을 설치해야 해요.

Biome 마켓스토어

익스텐션이 없을 경우에는 Biome의 린트 룰이 적용되었는지 파악이 안 돼요.

익스텐션을 설치하면 다음과 같이 린트 룰 파악이 가능해져요.

단순히 익스텐션을 설치한다고 내 코드에 Biome가 적용되는 걸까요? 아니요. Prettier랑 ESLint 조합과 마찬가지로 추가적인 설정이 필요해요.

세 번째, settings.json 설정

Biome 익스텐션을 사용하기 위해서는 .vscode/settings.json 파일을 생성해야 해요.

{
  "editor.defaultFormatter": "biomejs.biome",
  "editor.codeActionsOnSave": {
    "quickfix.biome": "explicit",
    "source.organizeImports.biome": "explicit"
  }
}

editor.defaultFormatter 설정은 VCS에서 파일을 포매팅할 때 사용할 기본 포매터를 지정해요. VSC 자체에서 설정할 수도 있지만, 모든 프로젝트를 Prettier+ESLint에서 Biome로 마이그레이션 할 예정이 아니라면 추천해 드리지 않아요.

editor.codeActionsOnSave 설정은 파일을 저장할 때 자동으로 실행할 코드 작업을 정의할 수 있어요.

quickfix.biome 설정은 Biome 익스텐션에서 제공하는 빠른 수정(Quick Fix)을 저장할 때자동으로 실행하도록 해요.

source.organizeImports.biome 설정은 Biome에서 제공하는 import 정리 기능을 저장할 때 자동으로 실행하도록 해요.

네 번째, biome 환경 설정

서비스 디렉토리의 root 경로에 biome.json 파일을 생성해요. 그리고 기본적인 설정을 해볼게요.

{
  "files": {
    "ignoreUnknown": true,
    "ignore": ["node_modules"]
  },

  "organizeImports": {
    "enabled": true
  }
}

ignoreUnknown 옵션은 Biome가 알 지 못하는 파일 형식이나 규칙을 무시하도록 설정해요. node_modules 폴더는 정적 분석 또는 포매팅이 필요 없으니 무시(ignore 옵션)하도록 해요.

organizeImports 옵션은 Biome의 import 정리 기능을 활성화해요.

이제 포매터와 린트 규칙을 적용해 볼게요.

{
  ...,

  "formatter": {
    "enabled": true,
    "formatWithErrors": false,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineEnding": "lf",
    "lineWidth": 120,
    "attributePosition": "auto"
  },

  "linter": {
    "recommended": true
    "complexity": {
      "noVoid": "error",
    },
    "correctness": {
      "noUnusedImports": "error"
    },
  }
}

다섯 번째, script 작성

마지막 단계예요. Biome는 결국 자동화 도구로써 사용되기 때문에 내 프로젝트의 코드에 규칙을 한꺼번에 적용할 수 있도록 실행 명령어를 작성해야 해요. package.json의 scripts를 활용할게요!

{
  "scripts": {
    "lint": "biome lint --write",
    "format": "biome format --write",
    "check": "biome check --write",
    "reporter": "biome check --reporter=summary",
  },
}

설정은 끝났어요! 정말 간단하지 않나요?

lint 명령어는 linter와 관련된 규칙들을 확인하고 변경해 줘요. format 명령어는 formatter 관련된 규칙들을 확인하고 변경해 줘요. check 명령어는 linter, formatter, import 관련된 규칙들을 확인하고 변경해 줘요.

마지막 명령어는 reporter인데요. 다음과 같이 문제가 발생한 파일이 어떤 규칙을 위반했는지 보고서 형태로 알려줘요.

reporter 명령어 사용

총 5단계로 구성된 Biome 설정은 Prettier나 ESLint 설정 과정과 크게 다르지 않아요. 환경 설정 파일은 https://biomejs.dev/reference/configuration/, 린터 룰은 https://biomejs.dev/linter/rules/ 에서 참고하실 수 있어요.

제가 포스트를 작성하며 진행했던 템플릿도 공유해 드려요.

Biome를 도입하자

계기

저는 팀 내에서 개발하는 모노레포로 구성된 공통 라이브러리 프로젝트에 린팅과 포맷팅 규칙을 Prettier+ESLint를 사용해 코딩 컨벤션에 맞게 적용하려고 했어요.

적용 전에 문득 이런 생각이 들었어요. 이 2개의 조합을 사용하기 위해서는 충돌 이슈를 해결해야 하니, 플러그인을 2개(eslint-config-prettier, eslint-plugin-prettier)를 설치해야 하고, TypeScript를 사용하니 typescript-eslint 라이브러리도 설치해 줘야 하고, 그 외에도 다양한 서드파티 플러그인들을 따로 설치해 줘야만 하는 그림이 그려졌어요.

🤔
이 도구들을 알아보고 설치해서 테스트하고 적용을 완료하는데 너무 많은 시간을 투자하는 것이 아닌가?

너무 비효율적이라는 생각이 들었어요.

만약에, 모든 규칙을 적용하고 템플릿을 만든다고 하더라도 이미 많은 라이브러리를 설치했기 때문에 버전 관리에 더욱 신경 써야 해요. 유지보수가 더욱 힘들어져요.

설정하는데 걸리는 소요 시간도 문제지만 후에 "이 프로젝트를 처음 보는 개발자가 인지적 부담이 커지지 않을까?"라는 생각이 들었어요. 이 도구들이 제공하는 코드는 비즈니스 로직은 아니지만, 프로젝트 구성을 파악하는 데 있어서 꽤나 큰 부분을 담당하고 있다고 생각하고 있었어요.

저의 페인포인트는 "포맷팅과 린팅을 위해 개발자가 신경 써야 할 부분이 너무 많다" 였어요.

기억났다 Biome!

제가 이러한 고통을 느꼈듯이 이미 많은 개발자도 똑같은 니즈가 있다고 생각하여 리서치를 시작했어요. 그러다가 Biome라는 도구를 발견하게 되었어요. 그리고 아차 싶었어요.

저는 2024년 1월경 Rust로 만들어진 Biome 오픈소스에 기여했던 경험이 있어요. 시간이 꽤 흘러서 까먹고 있었는데 인제야 기억이 나더군요.

Biome 기여

당시 사이드 프로젝트를 통해서 처음 사용해 봤었는데 경험이 되게 좋았었어요. 간편한 설정에 biomejs/biome 라는 라이브러리 하나만 설치하면 몇십개의 플러그인들을 대체할 수 있었거든요. 그래서 팀 내 코딩 컨벤션을 적용할 수 있는지 biome에서 제공하는 모든 규칙을 하나하나 뜯어봤어요. 200+ 이상의 규칙을 확인하는데 이틀 정도 소요됐었고 컨벤션 적용에 문제가 없다고 판단했어요.

지긋지긋한 eslint-plugin 지옥에서 벗어난다고 생각하니 너무 기뻤어요. 또한 ESLint는 2020년부터 새로운 규칙이 추가되지 않고 있어요. Biome는 formatter와 linter rule이 200+이상 있고 현재도 계속 linter rule이 추가되고 있어요.

제가 항상 라이브러리를 사용하기 전에 확인하는 오픈소스 활성도도 충분히 만족했어요.

도입하면서 좋았던 점

개발 환경 단순화

Prettier+ESLint가 하나로 통합된 도구이기 때문에 개발 환경을 단순화할 수 있어요. 이를 통해 개발자들은 더는 여러 라이브러리를 따로 설정하고 관리할 필요가 없어, 설정 및 유지 관리에 드는 시간을 대폭 줄일 수 있었어요. 처음 ESLint로 설정할 때는 40분 정도 걸렸었는데, Biome로 설정할 때는 15분 정도 걸렸어요.

11개의 라이브러리가 단 1개의 라이브러리만으로 대체가 가능하게 되었어요. 개발자는 biome에만 집중하면 포맷팅과 린팅 모두를 간단하게 케어할 수 있게 되었어요.

분석 속도 향상

정적 분석에 걸리는 시간도 훨씬 빨라졌어요. 0.89초에서 0.22초로 약 75% 정도 감소하였어요. 사실 둘 다 1초 이내라서 속도의 차이를 사람이 쉽게 인지할 수는 없는 것 같아요.

캐싱 전략

Biome는 사용자 디렉토리에 캐싱 파일을 저장하여 이미 분석된 파일을 다시 처리하지 않고 변경된 부분만을 빠르게 처리할 수 있어요. Biome의 캐싱 파일은 다음 명령어로 알 수 있어요.

biome explain daemon-logs

ESLint도 --cache 명령어로 캐싱 전략을 지원해요. 하지만 typescript-eslint를 사용해야 한다면 --cache 기능은 추천하지 않는다고 공식 문서에서 말하고 있어요. 특히, 여러 파일에 걸쳐있는 의존성을 검사하는 규칙을 사용할 때는 캐싱이 제대로 동작하지 않을 수 있다고 해요.

이러한 장점들로 인해 개발자가 더욱 중요한 로직을 위해 고민할 수 있는 시간을 확보할 수 있었어요.

도입하면서 나빴던 점

부족한 규칙

200개 이상의 린트 룰이 있지만, 아직은 많은 개발자를 만족시키기 위해서는 부족한 게 현실입니다. 실제로 vite의 react 템플릿에서 자주 사용되는 eslint-plugin-react-refresh 같은 규칙의 경우에는 아직 개발 중이에요. 이 외에도 정밀하게 import를 설정하는 규칙이 아직 없어요.

상대적으로 부족한 레퍼런스

Biome는 레퍼런스 수가 현저히 적어요. 실제로 구글링을 해보시면 바로 알 수 있어요.

구글을 기준으로 'biomejs' 키워드로 검색을 해보면 2만+개 정도의 데이터를 확인할 수 있어요.

Biome 구글링 검색 결과 수

'ESLint' 키워드로 검색을 해보면 1천만+개 정도의 데이터를 확인할 수 있어요.

ESLint 구글링 검색 결과 수

둘의 레퍼런스 차이는 약 380배 정도인데요. 조금씩 변형된 키워드에 따라 차이가 있을 수 있어요. 그런데 저는 이러한 biome의 레퍼런스 수가 정상이라고 생각해요. 단순히 인지도가 낮아 레퍼런스 수가 적을수도 있겠지만, 저는 파편화된 라이브러리가 없기 때문에 가능한 수치라고 생각해요. ESLint는 파생된 라이브러리들이 원체 많다 보니 레퍼런스 수도 그만큼 늘어났다고 생각해요.

아래는 npm trends에서 이용률을 확인해 봤어요. 이용자 수도 약 50배 정도 차이가 나요. 아직은 biome가 ESLint의 아성을 따라잡기에는 먼 것 같아요

언어장벽

ESLint의 경우에는 JavaScript 언어로 개발되어 사용 시 오류를 발견했을 때, 오픈소스의 코드를 수정하여 PR을 쉽게 날릴 수 있었고 내부 구현체를 이해하는데도 어렵지 않았어요. 반면, Biome는 TypeScript, JSX에 친화적이지만 Rust 언어로 작성되어 JavaScript 진영의 개발자들이 쉽게 오픈소스에 접근할 수 없는 환경이에요. 이는 오픈소스의 성장 속도가 가파르지 않은 원인 중 하나라고 생각해요.

마무리

오늘은 코드 포매팅과 린팅 작업을 간단하고 쉽게 자동화해 주는 도구를 통해 개발자의 경험과 생산성을 향상시킨 경험을 공유해 드렸는데요. 만약 실무에서 Biome를 사용하고 싶으시다면 충분한 검토를 하시고 도입해야 한다고 말씀드리고 싶어요.

이 글을 읽는 모두 행복한 하루 보내세요!