| name | code-convention |
| description | ESLint 10+ flat config, Biome, Prettier, Husky, lint-staged, commitlint 설정 및 선택 기준 |
코드 컨벤션 & 품질 도구 패턴
소스: https://eslint.org/docs/latest | https://biomejs.dev/docs | https://prettier.io/docs
소스: https://eslint.org/blog/2026/02/eslint-v10.0.0-released/ | https://eslint.org/version-support/ | https://biomejs.dev/guides/upgrade-to-biome-v2/
검증일: 2026-08-26 (최초 2026-03-27 · 08-26 freshness 재검증: ESLint 10.0.0(2026-02-06)에서 eslintrc 완전 제거·v9는 2026-08-06 EOL, Biome 2.5.x의 organizeImports→assist 이동 반영)
버전 기준 (2026-08-26): ESLint 10.x(현행, v9.x는 2026-08-06부로 EOL, v8.x는 2024-10-05 EOL) · Biome 2.5.x · Prettier 3.x · Husky 9 · lint-staged 최신.
ESLint 10은 .eslintrc.*·.eslintignore·ESLINT_USE_FLAT_CONFIG·--no-eslintrc·--rulesdir·--ignore-path 를 전부 제거했고 Node ^20.19.0 || ^22.13.0 || >=24 를 요구한다. eslint.config.* 탐색 기준도 cwd가 아니라 린트 대상 파일의 디렉토리로 바뀌었다(모노레포에서 패키지별 config가 잡히는 방식이 달라짐).
아직 ESLint 8 + .eslintrc를 쓰는 레거시 프로젝트는 이 스킬의 flat config 예시를 그대로 적용할 수 없다 — 경계 규칙 등 v8 분기 설정은 architecture/module-boundaries 스킬(ESLint 8·9 양쪽 예시)을 참조하고, v10 이행은 eslintrc→flat config 변환(@eslint/migrate-config)을 선행한다.
도구 선택 기준
린터/포매터 선택?
├─ 속도 최우선 + 설정 간소화 원함
│ └─ Biome (린트 + 포맷 통합, Rust 기반)
│
└─ 기존 ESLint 플러그인 생태계 필요 (ex. Next.js, Tailwind)
└─ ESLint 10+ flat config + Prettier
| 도구 | 역할 | 속도 | 플러그인 생태계 |
|---|
| Biome | 린트 + 포맷 통합 | ⭐⭐⭐⭐ 매우 빠름 | 제한적 |
| ESLint 10+ | 린트 전용 | ⭐⭐ | 풍부함 |
| Prettier | 포맷 전용 | ⭐⭐⭐ | - |
모노레포 권장: ESLint + Prettier (Next.js, eslint-config-next 호환성 때문)
ESLint 10+ Flat Config
기본 구조 (eslint.config.js)
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import reactPlugin from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import nextPlugin from '@next/eslint-plugin-next'
export default tseslint.config(
{ ignores: ['dist/**', '.next/**', 'node_modules/**'] },
js.configs.recommended,
...tseslint.configs.recommended,
{
plugins: { react: reactPlugin, 'react-hooks': reactHooks },
rules: {
...reactPlugin.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
},
settings: { react: { : } },
},
{
: [],
: { : nextPlugin },
: { ...nextPlugin... },
},
{
: {
: [, { : }],
: ,
: ,
},
}
)
모노레포 설정 패턴
monorepo/
├── eslint.config.js ← 루트 공통 설정
└── apps/web/
└── eslint.config.js ← 앱별 확장
import rootConfig from '../../eslint.config.js'
import nextPlugin from '@next/eslint-plugin-next'
export default [
...rootConfig,
{
plugins: { '@next/next': nextPlugin },
rules: { ...nextPlugin.configs['core-web-vitals'].rules },
},
]
공유 패키지 설정 (packages/eslint-config)
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
export const base = tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
{ ignores: ['dist/**'] }
)
export const react = tseslint.config(
...base,
)
export const nextjs = tseslint.config(
...react,
)
Biome (통합 린터+포매터)
설정
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnusedVariables": "error",
"useExhaustiveDependencies": "warn"
},
"suspicious": {
"noExplicitAny": "warn"
}
Biome vs ESLint+Prettier 비교
| 항목 | Biome | ESLint + Prettier |
|---|
| 설정 파일 수 | 1개 (biome.json) | 2개 이상 |
| 실행 속도 | 10-20x 빠름 | 기준 |
| next-eslint 지원 | ❌ (직접 없음) | ✅ |
| import 정렬 | ✅ 내장 | 플러그인 필요 |
| 성숙도 | v2 안정 (2025-06-17 v2.0 → 2.5.x, 린트 룰 500개) | 검증됨 |
| 타입 인지 린트 | v2부터 tsc 없이 자체 타입 추론으로 type-aware 룰 제공 (typescript 패키지 미설치도 가능) | typescript-eslint + parserOptions.projectService (tsc 프로그램 필요, 느림) |
Prettier
{
"semi": false,
"singleQuote": true,
"trailingComma": "es5",
"tabWidth": 2,
"printWidth": 100,
"plugins": ["prettier-plugin-tailwindcss"]
}
// .prettierignore
dist/
.next/
node_modules/
*.min.js
ESLint와 충돌 방지:
pnpm add -D eslint-config-prettier
import prettierConfig from 'eslint-config-prettier/flat'
export default [...기존설정, prettierConfig]
Husky + lint-staged (커밋 전 검사)
설치 및 설정
pnpm add -D husky lint-staged
npx husky init
npx lint-staged
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yaml}": [
"prettier --write"
]
}
}
commit-msg 훅 (커밋 메시지 검증)
npx --no -- commitlint --edit $1
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'test', 'chore', 'ci', 'perf', 'revert'
]],
'subject-max-length': [2, 'always', 72],
}
}
TypeScript 타입 체크
기본 명령어
tsc --noEmit
tsc --noEmit -p tsconfig.json
tsc --noEmit --watch
각 패키지에 typecheck 스크립트 추가
{
"scripts": {
"typecheck": "tsc --noEmit"
}
}
{
"scripts": {
"typecheck": "tsc --noEmit"
}
}
Turborepo 통합
{
"tasks": {
"typecheck": {
"dependsOn": ["^typecheck"],
"cache": true
},
"lint": {
"cache": true,
"outputs": [".eslintcache"]
}
}
}
{
"scripts": {
"typecheck": "turbo run typecheck",
"lint": "turbo run lint",
"format": "prettier --write \"**/*.{ts,tsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,json,md}\""
}
}
pre-commit에 typecheck 포함 여부 판단 기준
typecheck를 pre-commit에 넣어야 할까?
느리다 (프로젝트 규모 클수록) → 보통 CI에서만 실행
빠르다 (소규모 프로젝트) → pre-commit에 포함 가능
권장 패턴:
- pre-commit: lint + format (빠름, 변경 파일만)
- pre-push 또는 CI: typecheck (전체 타입 검사)
pnpm typecheck
ts-ignore vs ts-expect-error
const value = badFunction()
const value = badFunction()
const value = (badFunction() as unknown) as ExpectedType
strict 옵션별 의미
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
CI 통합 패턴
name: Code Quality
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- run: pnpm install --frozen-lockfile
- run: pnpm typecheck
- run: pnpm lint
- run: pnpm format:check
흔한 실수 패턴