코드 품질을 관리하기 위해서는 Code Formatting과 Code Linting 작업이 필요합니다.
Code Formatting: 띄어쓰기, 줄 바꿈, 따옴표 등의 시각적 스타일을 일관성 있게 맞추어 가독성을 높이는 작업
Code Linting: 코드를 정적 분석하여 잠재적인 버그, 오류, 코드 품질 문제, 안티 패턴 등을 식별하고 알려주는 작업

Code Formatting의 대표적인 도구에는 Prettier가, Code Linting의 대표적인 도구에는 ESLint가 있습니다. 전체 흐름은 위와 같습니다. 일단 눈에 발라놓죠.
일반적으로 Prettier라는 포맷터로 코드 스타일을 정돈하여 일관성을 보장하고, ESLint로 잠재적 오류에 집중하여 전반적인 코드 품질을 관리합니다.
Prettier 공식 문서: https://prettier.io/docs/options

Prettier 설정 옵션은 공식 문서에서 찾아볼 수 있습니다. 모든 설정을 다 기술하는 것은 불필요하다고 생각합니다.
Prettier는 대표적인 Code Formatting 도구이고, Code Formatting이란 코드의 시각적 스타일을 일관성 있게 맞추어 가독성을 높이는 작업이라는 사실을 아는 것이 더 중요하겠죠?
프로젝트의 루트에서 .prettierrc 파일을 생성한 뒤 아래의 코드를 작성하면 됩니다.
{
// 문자열을 작은따옴표(')로 작성합니다
"singleQuote": true,
// 문장 끝에 세미콜론(;)을 추가합니다
"semi": true,
// 여러 줄일 때 마지막 요소 뒤에 쉼표를 추가합니다
"trailingComma": "all",
// 탭 대신 스페이스로 들여쓰기합니다
"useTabs": false,
// 들여쓰기 너비를 2칸으로 설정합니다
"tabWidth": 2,
// 한 줄의 최대 길이를 80자로 제한합니다
"printWidth": 80,
// 객체 리터럴의 중괄호 안쪽에 공백을 추가합니다
"bracketSpacing": true,
// 화살표 함수의 매개변수를 항상 괄호로 감쌉니다
"arrowParens": "always",
// JSX 태그의 닫는 꺾쇠 괄호를 다음 줄에 배치합니다
"bracketSameLine": false,
// 특정 파일 패턴에 대해 다른 설정을 적용합니다
"overrides": [
{
"files": "*.json",
"options": {
"printWidth": 100
}
}
]
}
ESLint 공식 문서: https://eslint.org/docs/latest/use/configure

ESLint의 configuration은 위 문서를 참고하시면 됩니다.
ESLint는 대표적인 Code Linting 도구이고, Code Linting이란 코드를 정적 분석하여 잠재적인 버그, 오류, 코드 품질 문제, 안티 패턴 등을 식별하는 작업이라는 정의를 아는 것이 더 중요합니다.
프로젝트의 루트에서 eslint.config.js 파일을 생성한 뒤 아래의 코드를 작성하면 됩니다.
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
export default tseslint.config(
// 빌드 결과물 디렉토리는 린트 검사에서 제외합니다
{ ignores: ['dist'] },
{
// JavaScript와 TypeScript 권장 규칙을 확장합니다
extends: [js.configs.recommended, ...tseslint.configs.recommended],
// TypeScript 파일(.ts, .tsx)에만 적용합니다
files: ['**/*.{ts,tsx}'],
languageOptions: {
// ECMAScript 2020 문법을 사용합니다
ecmaVersion: 2020,
// 브라우저 전역 변수를 사용할 수 있도록 설정합니다
globals: globals.browser,
},
// React Hooks와 React Refresh 플러그인을 사용합니다
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
// React Hooks 권장 규칙을 적용합니다
...reactHooks.configs.recommended.rules,
// 컴포넌트만 export하도록 경고합니다 (상수 export는 허용)
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
);
Husky 공식 문서: https://typicode.github.io/husky

Husky는 Git Hooks를 쉽게 관리할 수 있게 도와주는 도구입니다.
Git Hooks란 commit, push 등과 같은 Git 작업이 실행되기 전이나 후에 자동으로 특정 스크립트를 실행할 수 있는 기능입니다. Husky를 사용하면 커밋 전에 자동으로 lint 검사나 테스트를 실행하여 문제가 있는 코드가 저장소에 올라가는 것을 방지할 수 있습니다.
초기화를 완료하면 프로젝트 루트에 .husky 폴더가 생성됩니다.
# Husky 설치
npm install -D husky
# Husky 초기화
npx husky init
.husky/pre-commit 파일을 생성하고 아래 내용을 작성합니다.
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged
이제 git commit을 실행할 때마다 자동으로 코드 포맷팅과 린트 검사가 실행되어, 문제가 있는 코드는 커밋되지 않습니다.
그런데 마지막에 npx lint-staged가 있죠? lint-staged는 무엇일까요?
lint-staged 공식 문서: https://github.com/lint-staged/lint-staged

lint-staged는 Git에 staged 상태인 파일들에만 lint 작업을 실행하는 도구입니다.
모든 파일을 검사하는 대신 커밋하려는 파일만 검사하기 때문에 시간을 절약할 수 있습니다. Husky와 함께 사용하면 커밋 전에 변경된 파일만 자동으로 포맷팅하고 린트 검사를 할 수 있습니다.
# lint-staged 설치
npm install -D lint-staged
package.json 파일에 아래 내용을 추가합니다.
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,css,scss,less,md,html,yaml}": [
"prettier --write"
]
}
}
이제 커밋할 때 staged된 JavaScript, TypeScript 파일은 ESLint로 자동 수정되고 Prettier로 포맷팅되며, 나머지 파일들은 Prettier로만 포맷팅됩니다.

Prettier와 ESLint를 대체할 수 있는 Biome라는 도구도 있다는 점을 알았습니다. 다만 오늘 글에서는 Biome를 다루지 않았습니다.
Weekly Downloads가 394만으로 결코 적은 숫자는 아니지만, npm trends를 확인해 보니 아직 Biome가 ESLint의 아성을 뛰어넘기는 쉽지 않겠다는 판단을 했습니다. 그리고 Rust로 작성되어 JavaScript 개발자들이 오픈소스에 접근하기 어려워 성장 속도가 가파르지 않을 것 같다는 생각도 들었습니다.
하지만 Biome는 Performance 측면에서 Prettier에 비해 35배 빠르다는 점은 간과할 수 없습니다. Biome를 이해하는 것이 오히려 Prettier와 ESLint에 대한 이해를 더 견고하게 할 수도 있습니다. 생태계가 더 성장하면 다루어 보도록 하겠습니다.