무색
기술블로그
에세이
연구
프로덕트
소개

무색

소프트웨어로 비즈니스의 가능성을 만듭니다. 웹·앱 개발, 음성 AI, 자동화 콘텐츠 제작까지 — 기술이 필요한 곳에 무색이 있습니다.

연락처

[email protected]

사업자 정보

상호: 무색

대표: 배성재

사업자등록번호: 577-58-00836

인천광역시 연수구 인천타워대로 323, 에이동 8층 801-802호 AB-132 (송도동, 송도 센트로드)

© 2026 무색. All rights reserved.
개인정보처리방침·이용약관
INCHEON, KR
husky + lint-staged — 이중 검증 게이트
museck 만들기
2026. 1. 9.

CI에서 터지기 전에 잡자: husky + lint-staged 설정기

huskylint-stagedeslintprettiergit-hooks

문제: 린트 에러가 CI에서야 터진다

무색 홈페이지를 만들면서 Gitea Actions로 CI/CD를 붙여놨는데, 린트 에러가 CI 파이프라인에서 터지는 일이 종종 있었다. 로컬에서 eslint를 수동으로 돌리면 되긴 하는데, 바쁘면 까먹는다. 커밋하고 푸시하고 CI 실패 알림 받고 다시 고치고 푸시하는 왕복이 은근 짜증났다.

그래서 Git 훅으로 로컬에서 미리 잡기로 했다. 이른바 Shift-Left. 검증을 가능한 한 앞 단계로 당기는 거다.

구조: 커밋과 푸시, 두 단계로 검증

핵심 아이디어는 간단하다. 커밋할 때는 변경된 파일만 빠르게 린트하고, 푸시할 때는 전체 타입 체크를 돌린다.

  • pre-commit: lint-staged가 스테이징된 파일만 골라서 ESLint + Prettier 실행
  • pre-push: tsc --noEmit으로 프로젝트 전체 타입 체크

커밋은 자주 하니까 빠르게 끝나야 하고, 푸시는 좀 느려도 전체 정합성을 확인하는 게 낫다. 이 두 단계 분리가 포인트다.

설정

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이 핵심인데, 경고도 에러로 취급해서 커밋을 막는다. 경고를 허용하면 점점 쌓여서 결국 아무도 안 보게 되니까.

삽질 노트: ESM 프로젝트에서의 린트

husky + lint-staged 설정 자체는 별 탈 없었다. 근데 이 프로젝트가 "type": "module" ESM 환경이라서 ESLint 설정 파일 쪽에서 Docker 빌드 때 문제가 생겼다. 로컬에서는 잘 되는데 Docker에서만 터지는 전형적인 패턴이었고, 결국 eslint ignoreDuringBuilds로 우회했다. (이 이야기는 Docker 빌드 삽질기에서 더 자세히 다뤘다.)

제로 경고 정책

처음부터 경고 0개를 강제하는 게 좋다. 프로젝트 초반에는 경고가 적어서 괜찮아 보이지만, 방치하면 몇 주 안에 수십 개로 불어난다. 그렇게 되면 "어차피 다 경고니까" 하고 무시하게 되고, 정작 중요한 경고도 묻힌다.

--max-warnings 0 한 줄이면 이 문제를 원천 차단할 수 있다.

결론

설정에 30분도 안 걸렸는데 효과는 크다. CI에서 실패하고 왕복하는 시간이 사라졌고, 린트 에러가 있는 코드는 아예 커밋이 안 되니까 저장소가 깔끔하게 유지된다. 검증은 빠를수록 좋다.

자주 묻는 질문

husky와 lint-staged의 차이점은 무엇인가?
husky는 Git 훅(pre-commit, pre-push)을 관리하는 도구이고, lint-staged는 스테이징된 파일만 골라서 린터를 실행하는 도구다. 둘을 조합하면 커밋 시점에 변경된 파일만 빠르게 검증할 수 있다.
pre-commit과 pre-push 훅을 분리하는 이유는?
커밋은 자주 하므로 변경 파일만 빠르게 린트하고, 푸시는 덜 빈번하므로 전체 타입 체크를 돌린다. 빠른 피드백과 전체 정합성 검증을 두 단계로 나누는 전략이다.
ESLint --max-warnings 0 옵션은 왜 쓰나?
경고를 에러로 취급해서 커밋을 막는다. 경고를 허용하면 점점 쌓여서 아무도 안 보게 되므로, 처음부터 경고 0개를 강제하는 게 코드 품질 유지에 효과적이다.
museck 만들기(10/10)
Prev

CI/CD 빌드가 느려서 3곳을 동시에 고쳤다