
무색 홈페이지를 만들면서 Gitea Actions로 CI/CD를 붙여놨는데, 린트 에러가 CI 파이프라인에서 터지는 일이 종종 있었다. 로컬에서 eslint를 수동으로 돌리면 되긴 하는데, 바쁘면 까먹는다. 커밋하고 푸시하고 CI 실패 알림 받고 다시 고치고 푸시하는 왕복이 은근 짜증났다.
그래서 Git 훅으로 로컬에서 미리 잡기로 했다. 이른바 Shift-Left. 검증을 가능한 한 앞 단계로 당기는 거다.
핵심 아이디어는 간단하다. 커밋할 때는 변경된 파일만 빠르게 린트하고, 푸시할 때는 전체 타입 체크를 돌린다.
커밋은 자주 하니까 빠르게 끝나야 하고, 푸시는 좀 느려도 전체 정합성을 확인하는 게 낫다. 이 두 단계 분리가 포인트다.
husky와 lint-staged를 설치하고 package.json에 lint-staged 설정을 넣으면 끝이다.
{
"lint-staged": {
"*.{ts,tsx}": ["eslint --max-warnings 0", "prettier --write"],
"*.{js,cjs,mjs,json,css,md}": ["prettier --write"]
}
}TypeScript와 TSX 파일은 ESLint와 Prettier 둘 다 거치고, 나머지 파일은 Prettier만 돌린다. --max-warnings 0이 핵심인데, 경고도 에러로 취급해서 커밋을 막는다. 경고를 허용하면 점점 쌓여서 결국 아무도 안 보게 되니까.
husky + lint-staged 설정 자체는 별 탈 없었다. 근데 이 프로젝트가 "type": "module" ESM 환경이라서 ESLint 설정 파일 쪽에서 Docker 빌드 때 문제가 생겼다. 로컬에서는 잘 되는데 Docker에서만 터지는 전형적인 패턴이었고, 결국 eslint ignoreDuringBuilds로 우회했다. (이 이야기는 Docker 빌드 삽질기에서 더 자세히 다뤘다.)
처음부터 경고 0개를 강제하는 게 좋다. 프로젝트 초반에는 경고가 적어서 괜찮아 보이지만, 방치하면 몇 주 안에 수십 개로 불어난다. 그렇게 되면 "어차피 다 경고니까" 하고 무시하게 되고, 정작 중요한 경고도 묻힌다.
--max-warnings 0 한 줄이면 이 문제를 원천 차단할 수 있다.
설정에 30분도 안 걸렸는데 효과는 크다. CI에서 실패하고 왕복하는 시간이 사라졌고, 린트 에러가 있는 코드는 아예 커밋이 안 되니까 저장소가 깔끔하게 유지된다. 검증은 빠를수록 좋다.