지키지 않는 정책은 없으니만 못하다 !
- 이 말에 누구나 다 고개를 끄덕이겠지만, 이 세상에 정책을 준수하는 것 만큼 어려운 일도 없다.
- 팀원 모두가 머리를 맞대어 개발 문화와 정책을 논의할 때만 해도 개발 프로세스만 제대로 확립하면 개발에서 테스트, 배포까지 물 흐르듯 흘러가고 세상 모든 에러와 영원히 이별할 수 있을거라 기대하지만 우리를 기다리는건 무수한 고통의 연속이다
어떤 고통이 따를까?
- 코딩 스타일 안 따르기
- 테스트 코드 작성 안하기
- 테스트 실패하기
- 코드 커버리지 낮추기
- master에 직접 밀어넣기
- 그 밖에 기상천외한 일들
- 코드 리뷰를 할 때마다 매번 지켜지지 않은 정책들을 나열하면서 왜 정책을 지켜야 하는가? 에 대한 장황한 설명을 할 수 없는 노릇이며 결국 사람이 하는 일이라 고의로든 실수로든 정책을 부분 부분 빠드리는 일도 있다.
- 그렇다면 정책을 강제할 순 없을까? master에 직접 밀어 넣으려고 하면 작업을 중단해버리고 MR을 사용하라고 메시지를 띄워주는 마법같은 방법
- 만약에 팀이 Git을 사용하고 있다면 Git Hooks를 사용하여 쉽고 간단하게 정책을 강제할 수 있다 !
Git Hooks?
- Git Hooks는 Git과 관련한 어떤 이벤트가 발생했을 때 특정 스크립트를 실행할 수 있도록 하는 기능이다.
- 크게 클라이언트 훅과 서버 훅으로 나뉘는데 클라이언트 훅은 커밋, 머지가 발생하거나 push가 발생하기 전 클라이언트에서 실행하는 훅이다. 서버 훅은 Git Repository로 Push가 발생했을 때 서버에서 실행하는 훅이다.
- 여기서는 클라이언트 훅을 사용하며 그 이유는 서버를 관리할 수 있는 권한이 있다면 서버 훅을 활용하는게 더 유용할 수 있다. Git Repository 서버에는 모든 프로젝트에 대해 Push 정책을 설정할 수 있기 때문이다.
클라이언트 훅
- 클라이언트 훅은 커밋 워크플로 훅, 이메일 워크플로 훅, 그외 다양한 훅으로 분류할 수 있다.
- 커밋 워크플로 훅은 git commit 명령으로 커밋을 할 때 실행하는 훅이다.
- 이메일 워크플로 훅은 git am 명령으로 이메일을 통해 patch 파일을 적용할 때 실행하는 훅이다.
- 기타 훅은 Rebase, Merge, Push와 같은 이벤트를 실행할 때 실행하는 훅을 포함한다.
분류 | 훅 | 설명 |
커밋 워크플로 훅 | pre-commi | commit을 실행하기 전에 실행 |
ㅤ | prepare-commit-msg | commit 메시지를 생성하고 편집기를 실행하기 전에 실행 |
ㅤ | commit-msg | commit 메시지를 완성한 후 commit을 최종 완료하기 전에 실행 |
ㅤ | post-commit | commit을 완료한 후 실행 |
이메일 워크플로 훅 | applypatch-msh | git am 명령 실행 시 가장 먼저 실행 |
ㅤ | pre-applypatch | patch 적용 후 실행하며, patch 를 중단시킬 수 있음 |
ㅤ | post-applypatch | git am 명령에서 마지막으로 실행하며, patch를 중단시킬 수 없음 |
기타 훅 | pre-rebase | Rebase 하기 전 실행 |
ㅤ | post-rewrite | git commit -amend, git rebase 와 같이 커밋을 변경하는 명령을 실행한 후 실행 |
ㅤ | post-merge | merge가 끝나고 나서 실행 |
ㅤ | pre-push | git push 명령 실행 시 동작하며 리모트 정보를 업데이트 하고 난 후 리모트로 데이터를 전송하기 전에 실행 .push를 중단시킬 수 있다. |
Git Hooks를 적용해보자
- Git Hooks는 .git/hooks 디렉토리 안에 저장한다.
- hook은 실행 가능한 스크립트이며, 설정하고자 하는 훅 이름을 확장자 없이 파일 명으로 지정하면 Git Hooks를 적용할 수 있다.
- 예를 들어 pre-commit 훅을 적용하여 commit 직전에 Hello Commit을 출력하고 싶다면 다음과 같이 파일을 생성하면 된다.
.git/hooks/pre-commit
#!/bin/sh echo "Hello Commit!" exit 0 # Exit 코드가 0이 아니면 커밋이 취소된다.
master로 직접 push 방지하기
- 실제로 유용한 훅을 적용해보자. 원본 프로젝트의 Master로 직접 Push 하려고 하면 push를 중단시키는 훅이다.
- push를 실행하는 경우 동작해야 하므로, pre-push 훅을 사용하면 된다. pre-push 훅은 리모트 이름과 주소를 파라미터로 전달받으며 stdin 을 통해 업데이트할 해시 리스트를 전달받는다.
- 우리는 리모트 주소와 브랜치명을 조사하여 push를 통과시키거나 중단시키면 된다.
- 스크립트는 다음과 같으며 아래 처럼 스크립트를 작성한 후 master에 push하려고 하면 실패하게 된다.
#!/bin/sh # insert your remote url (https) FORBIDDEN_HTTPS_URL="https://github.com/gabia/git-hooks-study.git" # insert your remote url (ssh) FORBIDDEN_SSH_URL="git@github.com:gabia/git-hooks-study.git" FORBIDDEN_REF="refs/heads/master" remote="$1" url="$2" if [ "$url" != "$FORBIDDEN_HTTPS_URL" -a "$url" != "$FORBIDDEN_SSH_URL" ] then exit 0 # Forked Project 에서는 제한하지 않음 fi if read local_ref local_sha remote_ref remote_sha then if [ "$remote_ref" == "$FORBIDDEN_REF" ] then echo "DO NOT PUSH it master" exit 1 # 금지된 ref 로 push 를 실행하면 에러 fi fi exit 0
Git Hooks를 공유하려면?
- Git Hooks는 .git 디렉토리에 저장한다. 그런데 .git 디렉토리는 버전 관리 대상이 아니므로 repositories에 올라가지 않는다. 기본적인 Git 체계 하에서는 Git Hooks를 공유할 수 없다는 뜻이다.
- Git Hooks를 공유하는 효과적인 방법에는 무엇이 있을까? 다양한 방법이 있겠지만 주로 다음과 같은 방법을 사용한다.
- Git Hooks를 설정하는 스크립트 공유
- Git Template을 활용
- husky 사용하기
Git Hooks를 설정하는 스크립트 공유하기
- Git Hooks를 설정하는 스크립트 공유는 Git Hooks를 공유하는 방법 중 가장 단순한 접근이다.
- Git Hooks를 별도 디렉토리에 넣어 버전 관리를 하고, 이 훅을 .git/hooks로 복사하는 스크립트를 함께 공유하는 방식이다.
- 우선 프로젝트 내에 githooks라는 디렉토리를 만들고 hooks를 넣어둔더
- ./githooks/
- pre-commit
- pre-push
- 그리고 다음 스크립트를 함께 공유한다
#!/bin/sh cp githooks/*.git/hooks
- 위의 스크립트는 단순히 githooks 디렉토리에 있는 모든 파일을 .git/hooks 디렉토리로 복사할 뿐이다.
- 이 방법을 사용하면 프로젝트 별로 hooks를 관리하고 공유할 수 있다. 하지만 작업자는 git clone 후에 반드시 ./setup_hooks.sh 위의 스크립트 파일을 실행해야한다 그렇지 않으면 훅을 적용할 수 없다.
Git Template 활용하기
- Git Template 활용은 git clone 시 —template 옵션을 통해 .git 디렉토리를 초기화 할 수 있다.
- 우선 프로젝트와 독립된 경로에 Template 디렉토리를 구성하고 Git Hooks를 넣어둔다.
- /home/user/git_templates/
- hooks
- pre-commit
- pre-push
- 그리고 프로젝트를 clone 할 때 —template 옵션 위에서 생성한 Template 디렉토리를 경로로 지정한다.
- git clone —template=/home/user/git_templates 깃주소
- 이제 ./git/hooks 디렉토리를 확인해보면 정상적으로 적용되어있는걸 확인할 수 있다.
- 이 방법은 훅스를 별도 경로에서 관리하고자 할 때 유용하지만 템플릿을 미리 공유해야하며 옵션을 빠뜨리는 경우 문제가 될 수 있다.
husky를 사용하자
- 위의 두가지 방식은 유용하지만 치명적인 문제가 있다 작업자가 실수로든 고의로든 훅을 적용하지 않을 가능성이 크다는 것이다.
- 또한 팀에서만 공유하는 프로젝트가 아니라 불특정다수에게 공개한 프로젝트라면 작업자가 Git Hooks를 적용했는 지 관리 감독하기가 더욱 어려워진다.
- Git Hooks를 반드시 적용하게끔 강제하는 방법은 만약 프로젝트가 모듈 의존성을 관리하기 위해 npm을 사용하고 있다면 husky는 좋은 선택이 될 수 있다.
husky?
- husky는 Git Hooks를 보다 쉽게 적용할 수 있는 npm 모듈이다. 심지어 Git Hooks에 대해 자세히 알지 못하더라도 commit, push 정책을 관리하고 공유할 수 있다.
- husky설치
- npm install —save-dev husky
- 다음으로 commit 정책을 정의한다.
- .huskyrc 파일에 정의하고자 하는 훅과 실행할 명령어를 지정하면 된다.
- 정책은 package.json에 정의하여도 무방하다 .huskyrc에 정의하는건 순전히 별도 파일로 관리하고 싶어서이다.
.huskyrc 파일
{ "hooks": { "pre-commit" : "echo 'hello commit!'" } }
- husky를 사용하면 의존 모듈을 설치하는 것만으로 적용이 되기 때문에 hooks를 적용하지 못하는 상황은 발생하지 않는다.
husky 동작방식
- 단지 모듈을 설치하고 정책을 정의한 것 뿐인데 Git Hooks처럼 동작을 잘 하고 있는데 어떻게 이게 가능할까?
- 설치한 husky 모듈의 package.json을 확인해보면 install 스크립트를 정의하고 있음을 알 수 있다.

- install 스크립트는 npm 스크립트 중 하나로서 npm install 명령을 통해 해당 모듈을 설치하면 자동으로 실행하는 스크립트이다. husky를 설치하면 install 스크립트인 node husky install 를 실행한다.
- node husky install을 따라가다보면 최종적으로는 자체적으로 구현해놓은 Git Hooks를 .git/hooks 디렉토리에 쓴다.