DEVLOG|개발 블로그

Git Hooks를 내 프로젝트에 통합해주는 Husky

August 2, 2021 12:41 AM:git

Git Hooks

hooks라고 구글에 검색해보면 갈고리 모양의 이미지가 여러 개 나온다. 리액트에서도 hooks라는 용어를 사용하고 깃에서도 hooks라는 개념이 존재한다. Vue에서는 인스턴스 라이프 사이클 훅이라는 말로 hooks를 사용하는데, 각 컴포넌트에서 어떤 시점마다 이벤트가 발생하면 훅이 실행된다. 예를 들어 컴포넌트가 생성될 때의 시점을 뜻하는 created 훅이 있고 DOM에 부착되는 시점인 mounted가 있다. (Composition API에서는 onMounted)

각각의 훅에서는 전역적으로 지정된 어떤 함수가 실행되고 여기에 우리는 갈고리처럼 훅마다 실행되는 함수에 내 함수를 얹어 이 시점에 내 함수가 돌아가게끔 할 수 있다. 깃에서는 이런 hooks들이 여러 종류가 있다.

종류

모든 hooks의 종류는 여기에서 참고할 수 있고, 여기에서는 아주 간단한 그리고 매우 중요한 몇 가지의 hooks만 정리한다.

우리가 깃을 통해서 프로젝트의 버전 관리를 하고 있다면 프로젝트 루트 디렉터리 내 .git이라는 디렉터리가 존재한다. 에디터상에서는 안보이지만, explorer, terminal 혹은 finder를 통해 해당 디렉터리가 존재한다는 것을 확인할 수 있다. 해당 디렉터리의 기본적으로 hooks라는 디렉터리에 깃에 존재하는 여러 가지 클라이언트 훅에 대한 샘플이 존재하는데, ****.sample 형태로 작성되어 있고, 예제 및 해당 hooks가 실행됬을 때 어떠한 매개변수를 취할 수 있는지, 어떤 행위를 하는지에 대한 자세한 내용이 기술되어 있다.

기본적으로 쉘 스크립트 혹은 Perl 언어로 작성되어있는 스크립트인데, 개인적으로 익숙한 스크립트 언어를 사용해서 해당 hooks를 커스텀할 수 있다. .sample 이라는 확장자를 지우면 바로 사용할 수도 있다.

여기서 살펴볼 hookspre-commitpre-push가 되겠다. 각각 commit을 찍기 전과 원격 저장소에 push 하기 전에 대한 hooks인데, 해당 스크립트를 잘 짜면 커밋 메세지가 찍기 전 코드 스타일을 점검할 수 있고, 정적 타입 분석 도구를 통해 타입 오류를 검사할 수도 있다. 그렇게 하면 실제로 일어날 수 있는 실수를 덜기 쉽다.

Husky

간단히 스크립트 수정을 통해 Git Hooks를 제어할 수 있다면 왜 이런 오픈 소스가 필요할까?

이유는 어떤 작업을 수행하게 하도록 하기 위해서 일련의 스크립트를 수정하는 작업은 매우 지겨울 수 있다. 그리고 새로운 프로젝트를 시작할 때 이러한 템플릿을 구성하기가 귀찮다.

예제

개발을 할 때에 각 컴포넌트에 대해 테스트가 이미 존재하고 로직을 수정하고 개발할 때에 기존의 테스트가 실패하면 커밋을 찍고 저장소에 푸시하면 안된다. 하지만 이러한 과정은 꼭 필요한 작업이긴 하지만 놓칠 때가 있고 항상 확인하기가 어렵지 않으나 종종 실수를 한다.

export function add(a, b) {
  return a + b
}

// .... Test Case
describe('Test add function', () => {
  test('Add A and B', () => {
    expect(add(1, 2)).toBe(3)
    expect(add(2, 2)).toBe(4)
    expect(add(3, 2)).toBe(5)
    expect(add(3, 3)).toBe(6)
    expect(add(5, 2)).toBe(7)
  })
})

예를 들어서 두 개의 인자를 받아 더한 뒤 값을 반환하는 함수인 add 함수가 있다. 그리고 하단과 같이 해당 함수에 대한 테스트 케이스가 존재한다. (매우 극단적일순 있지만) 그런데, 수정의 수정을 거듭하여 인자로 오는 값이 number 타입이 아닌 어떤 객체로 변했다고 하자. 그러면 이 테스트 케이스는 실패할 수 밖에 없으며 해당 테스트 케이스를 수정하거나 로직을 다시 수정해야 한다.

언제나 사림이 완벽할 순 없는 법... 테스트를 직접 돌려보지 않고 그대로 커밋을 찍어 푸시했을 수도 있다. 물론 저장소에 푸시되면 web hook이 트리거 되어 자동으로 테스트를 진행하여 실패한 케이스에 대해서 알림이 전송되는 시스템이 구축되어 있다면 이해가 되지 않을 수도 있다. 그런 시스템을 구축해서 개발해야 되는 것이 맞다고 보지만, 만약 테스트 케이스가 몇 백개가 되고, 몇 천개가 되어 모든 테스트 케이스를 돌리기에 충분한 시간이 없어 미쳐 다른 의존성이 있는 테스트가 실패할 때를 생각하면 이해가 된다.

적용

위와 같은 예제에서 커밋 메세지를 찍기 전 어떠한 작업을 할 수 있게 도와주는 오픈 소스가 바로 Husky다. 바로 설치해보자.

npm i -D husky
npx husky install
npm set-script prepare "husky install"

husky 의존성을 설치하고 husky에서 기본적으로 git hooks를 제어하기 위한 코드를 생성하기 위해서 npx husky install 명령어를 쳐준다. 그리고 prepare라는 명령어를 지정한다. prepare에 대한 자세한 내용은 npm lifecycle을 참고한다.

이제 지정할 훅을 만들면 되는데 이 것 또한 명령어를 통해 손쉽게 만들 수 있다.

npx husky add .husky/pre-commit "npm test"
git add .husky/pre-commit

이제 pre-commit 훅에 대한 행위가 만들어졌고, 커밋 메세지가 찍기 전 npm test를 실행하고 테스트가 실패하면 커밋 메세지가 찍히지 않는다. 핵심적인 기능은 이게 다이고 여기에 여러 훅을 섞거나 아니면 코드를 lint하거나, 타입 분석을 하거나 테스트를 하는 등 여러 가지 명령을 묶을 수 있다.