Canvas 1 Layer 1

깃허브 웹훅을 활용해 슬랙에 이벤트 전달하기

들어가며: 깃허브(GitHub)의 웹훅

hook을 지정하면 특정 이벤트가 발생했을 때 자동적으로 다른 스크립트를 실행할 수 있습니다. 깃Git에서도 훅 기능을 지원하고 있습니다. 저장소 폴더의 .git/hooks에서 샘플 스크립트와 사용할 수 있는 훅 이벤트들을 확인해볼 수 있습니다.

깃허브Github에서도 훅 기능을 지원하고 있으며, 깃허브와 연동 가능한 이벤트를 추가적으로 지원하고 있습니다. 서비스 훅 기능을 통해서 손쉽게 다른 서비스와의 통합이 가능하며, 흥미롭게도 다른 서비스와 통합하는 부분은 공개가 되어있어 관심이 있으시면 실제 코드를 확인해볼 수도 있습니다. 깃허브에서 직접 연동가능하지 않더라도, 웹훅Webhook 기능을 사용해 특정 API를 호출하는 것도 가능합니다.

웹훅은 API와는 반대 방향으로 작동합니다. 예를 들어 보통 API를 호출하면 어떤 정보를 되돌려줍니다만, 웹훅에 등록하면 어떤 이벤트가 발생할 때 거꾸로 깃허브에서 등록한 URL을 호출합니다. 여기서는 깃허브 훅 API를 조작하는 방법에 대해서 간략히 살펴보고 깃헙에서 보내주는 웹훅 알림을 처리할 수 있는 간단한 파드리노Padrino 서버를 만들고 슬랙Slack으로 알림을 보내는 과정을 다뤄보겠습니다.

깃허브(Githbu) 훅 API

API의 자세한 사항은 깃허브 문서에서 확인할 수 있습니다. 먼저 깃허브에서 지원하는 이벤트 종류와 웹훅를 추가했을 때 어떤 이벤트들이 추가되는지 살펴보겠습니다.

지원하는 훅 이벤트 종류

이벤트가 발생했을 때 깃허브에서 보내주는 내용은 이벤트 타입에서 자세히 확인할 수 있으며, 배포에 관한 부분은 배포(Deployments) API를 확인하시기 바랍니다.

저장소에 웹훅 등록하고 상태 확인하기

깃허브에서 저장소에 웹훅나 서비스 훅를 등록하기 위해서는 저장소의 관리 권한이 있어야합니다. 먼저 저장소의 오른쪽에 보이는 세팅Settings 메뉴에 들어갑니다.

웹훅 설정을 위해 저장소에서 세팅 메뉴에 들어간다
웹훅 설정을 위해 저장소에서 세팅 메뉴에 들어간다

세팅 메뉴의 왼쪽에서 서비스 훅Service Hooks 메뉴를 찾을 수 있습니다. 서비스 훅을 누르면 깃헙에서 바로 통합 가능한 서비스 리스트를 확인할 수 있습니다. 채팅 서비스인 힙챗Hipchat을 비롯해, 빌드 서비스인 서클CICircleCI, 트래비스TravisCI, 코드 매트릭스 관리 서비스인 코드 클라이메이트Code Climate, 테스트 커버리지 리포트 서비스 커버럴스Coveralls등 다양한 서비스를 지원하고 있습니다. 여기서는 맨 위에 있는 웹훅 URLsWebhook URLs를 사용하겠습니다.

깃허브에서는 다양한 서비스훅을 지원한다. 이 예제에서는 웹훅을 사용한다
깃허브에서는 다양한 서비스훅을 지원한다. 이 예제에서는 웹훅을 사용한다

웹훅 URLsWebhook URLs를 클릭하면 이벤트가 발생했을 때 정보를 받을 URL을 지정할 수 있습니다. 아직 훅 메시지를 받아 처리할 수 있는 서버가 없으므로 여기서는 서버를 등록하면 어떤 식으로 메시지가 오는 지, 어떤 이벤트들을 등록되어있는지 보여드리도록 하겠습니다. 웹훅 URLs에 notifier.nacyot.com라는 슬랙에 깃허브 저장소의 변경사항을 전달해주는 어플리케이션을 등록했다고 가정해보죠.

깃허브에서 저장소에 등록된 훅 정보를 확인하는 API URL은 다음과 같습니다.

https://api.github.com/repos/<계정이름>/<저장소이름>/hooks

이제 curl을 통해서 Github에 Hook가 어떻게 등록되어있는지 요청을 보내보도록하겠습니다. -u 플래그 뒤로는 인증을 할 계정이름을 넣어줍니다.

Commend > curl -u nacyot https://api.github.com/repos/nacyot/slack_notifier/hooks
Enter host password for user 'nacyot':
[
  {
    "url": "https://api.github.com/repos/nacyot/bbapi/hooks/1829382",
    "test_url": "https://api.github.com/repos/nacyot/bbapi/hooks/1829382/test",
    "id": 1829382,
    "name": "web",
    "active": true,
    "events": [
      "push",
    ],
    "config": {
      "url": "notifier.nacyot.com",
      "content_type": "form",
      "insecure_ssl": "1"
    },
    "last_response": {
      "code": 504,
      "status": "timeout",
      "message": "Service Timeout"
    },
    "updated_at": "2014-01-28T01:29:02Z",
    "created_at": "2014-01-26T02:27:38Z"
  }
]

다음과 같은 응답이 되돌아 옵니다. 이는 현재 등록된 훅의 URL과 어떤 이벤트가 등록되어있는지를 비롯한 여러가지 정보를 담고 있습니다. 특히 주목할 부분은 idevents그리고 url 부분입니다. 웹에서 지정한대로 정상적으로 등록이 된 걸 알 수 있습니다. 단, 웹에서 등록을 하면 기본 이벤트인 push 이벤트밖에 등록이 되지 않습니다. 그렇다면 이슈에 관련된 이벤트나 위키가 수정되었을 때 알림을 받고자 한다면 어떻게 해야할까요?

curl을 사용해 새로운 이벤트를 등록해보겠습니다. 여기서 등록할 이벤트는 issue, issue_comment, 그리고 위키 업데이트를 알려주는 gollum 이벤트입니다.

curl -u nacyot https://api.github.com/repos/nacyot/slack_notifier/hooks/1829382 -X PATCH -d '{"add_events": ["issue", "issue_comment", "gollum"] }'

위에서 훅 정보를 조회했던 curl 정보를 참고해 적당한 형태로 바꿔주시기 바랍니다. /hooks/ 뒤에 앞서서 조회했던 훅의 id 값을 넣어줘야합니다. 다시 처음 URL로 훅 정보를 조회해보면 정상적으로 이벤트들이 추가된 것을 알 수 있습니다.

...
    "events": [
      "push",
      "issue",
      "issue_comment",
      "gollum"
    ],
...

이제 저장소에서 이슈와 관련 이벤트가 발생하거나 위키에 페이지가 추가되거나 업데이트될 때 알림이 오게 됩니다. 이번엔 실제로 알림이 오는 것을 확인해보겠습니다. 위키 페이지를 하나 생성해보겠습니다.

깃허브 위키에서 새로운 페이지를 생성한다
깃허브 위키에서 새로운 페이지를 생성한다

이번엔 notifier.nacyot.com에 요청이 들어오는지 확인해보겠습니다.

07:15:35 web.1  |   DEBUG -      POST (1.6938s) / - 200 OK
07:15:35 web.1  | 192.30.252.54 - - [30/Jan/2014 07:15:35] "POST / HTTP/1.1" 200 - 1.6956

정상적으로 들어오네요. 네, 앞서서 이야기했듯이 이 서버가 해주는 역할은 깃허브로부터 훅를 받고 이를 슬랙이라는 채팅 서비스에 연결해주는 역할을 합니다.

슬랙에 위키 페이지 생성 알림이 온다
슬랙에 위키 페이지 생성 알림이 온다

채팅방에도 메시지가 잘 들어오는 것을 알 수 있습니다.

슬랙Slack과 깃허브 저장소 이벤트 통합

여기서부터는 위에서 다룬 슬랙Slack에 메시지를 전달하는 서버에 대해서 설명하겠습니다. 위에서 다룬 이벤트의 종류만 봐도 알 수 있습니다만, 의외로 깃헙Github에서는 다양한 이벤트들을 지원하고 있습니다. 깃허브에서 서비스 훅으로 바로 통합할 수 있는 서비스라면 두 서비스 간의 통합 기능을 이용하는 게 가장 편리합니다. 하지만 원하는 기능을 직접 구현하고 싶거나 아직 서비스 훅이 갖춰지지 않은 서비스와 통합을 하려는 경우엔 직접 깃허브에서 보내는 메시지를 처리할 서버를 만들어야 합니다.

예를 들어서 제가 속해있는 리모티Remotty 팀에서는 지금까지 커뮤니케이션을 위해 힙챗Hipchat이라는 협업, 채팅 툴을 사용해왔습니다만, 최근에 슬랙으로 서비스로 갈아탔습니다. 하지만 이 과정에서 깃허브과 통합을 하는데 어려움이 있었습니다. 얼마 전까지만 해도 웹훅을 통해서 슬랙 서비스를 연동하면 커밋이 푸쉬되는 알림만 왔습니다. 힙챗 같은 경우는 깃허브에서 다루는 거의 모든 이벤트를 전달해줍니다. 특히 이슈와 관련된 부분도 필수적이고, 위키를 적극 사용하고 있었기에 이런 알림이 비활성화되어있는 것은 치명적인 단점이었습니다.

이러한 문제에 대해서 슬랙 쪽 통합 방식이 최근에 변경되면서 현재는 이슈와 풀리퀘스트 부분의 알림을 보내주고 있습니다. 원래는 슬랙의 훅 URL을 직접 추가하는 방식으로 통합을 했습니다만, 최근에는 깃허브 인증을 하면 슬랙에서 훅 URL을 추가해주고 이슈와 풀리퀘스트 관련 이슈들을 더해줍니다. 이러한 변화가 다시 슬랙으로 넘어가자는 의견에 힘을 실어주었습니다. 하지만 여전히 위키를 지원해주지 않는 문제가 남아있었습니다.

이 기능이 당장 필요했던 관계로 직접 알림을 보내주는 서버를 만들기로 했습니다. 현재 루비의 파드리노Padrino*로 만든 깃허브에서 위키 변경 사항 알림 서버를 slack_notifier라는 이름으로 올려두었습니다. 이 서버가 하는 일은 정말 딱 위키 알림을 깃허브에서 받고 슬랙으로 전달해주는 일뿐입니다.

* 파드리노는 시나트라Sinatra를 확장한 경량 웹프레임워크입니다.

로직도 정말 단순합니다. hook_controller에 다음과 같은 내용이 들어가있을 뿐입니다.

Slack::Post.configure(
                      subdomain: params[:subdomain],
                      token: params[:token],
                      username: "Github"
                      )
Slack::Post.post "#{login}#{action} <#{url}|#{title}>", "#" + params[:room]

내부적으로 slack-post라는 젬을 사용해 깃허브에서 서버의 특정 API를 호출하면, 슬랙에 메시지를 보내주는 방식입니다. 현재는 서버에 데이터를 저장하는 기능이 없어서 토큰과 메시지를 URL 인자로 받아서 사용하고 있습니다.

도커(Docker)로 slack_notifier 서버 실행하기

도커를 사용해 slack_notifier 서버를 실제로 사용하는 방법에 대해서 소개하겠습니다. 애플리케이션을 클론하고 docker build를 수행합니다.

$ git clone https://github.com/nacyot/slack_notifier
$ docker build -t nacyot/slack_notifier:latest -q=true ./slack_notifier
...
$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
nacyot/slack_notifier   latest              d35792687e8c        4 days ago          751.1 MB
ubuntu                  12.04               8dbd9e392a96        9 months ago        128 MB
ubuntu                  12.10               b750fe79269d        10 months ago       175.3 MB

docker imaegs 명령어로 정상적으로 빌드된 것을 확인할 수 있습니다. 이제 생성한 이미지로부터 실제 어플리케이션 컨테이너를 실행시킵니다.

$ docker run -d -p 4000:4000 nacyot/slack_notifier:latest
7c42ae39691c
$ docker ps
CONTAINER ID        IMAGE                         COMMAND                CREATED             STATUS              PORTS                    NAMES
7c42ae39691c        nacyot/slack_notifier:first   /bin/sh -c bundle ex   4 days ago          Up 2 days           0.0.0.0:4000->4000/tcp   slack_notifier  

docker ps를 통해서 컨테이너가 정상적으로 실행되고 있는 것을 확인할 수 있습니다. 이제 깃허브에 웹훅을 등록할 차례입니다. 그 전에 먼저 슬랙 쪽에서 서비스 등록을 하고 토큰을 생성할 필요가 있습니다.

http://<SLACK_SUBDOMAI>.slack.com/services/new/incoming-webhook

위 주소로 접속하시면 인커밍 웹훅incoming-webhook을 바로 등록할 수 있습니다. Add integration 버튼을 누르면 오른쪽에 토큰 정보가 출력됩니다. 토큰 정보를 가지고 아래 URL을 완성합니다.

http://<서버주소>:4000/hook?subdomain=<SLACK_SUBDOMAIN>&token=<SLACK_TOKEN>&room=<SLACK_ROOM>

SLACK_ROOM에는 알림을 전달할 채널 이름을 지정합니다. 이제 이 URL을 원하시는 깃허브 저장소의 웹훅에 등록만 해주면 모든 준비는 완료됩니다. 하지만 여기까지 설정하고 위키를 수정해도 알림은 가지 않습니다. 앞서서 깃허브 API에 대해서 다룬 바 있습니다만, 기본 훅으로는 push밖에 등록이 되지 않기 때문입니다. 이 서버에서 인식할 수 있는 이벤트는 위키를 다루는 gollum밖에 없습니다.

curl -u <USER_NAME> https://api.github.com/repos/<USER_NAME>/<REPO_NAME>/hooks/<HOOK_ID> -X PATCH -d '{"add_events": ["gollum"] }'

앞서 다룬 것과 마찬가지 방법으로 gollum 이벤트를 추가해줍니다. 네 이걸로 모든 설정이 끝났습니다. 이제 해당하는 저장소의 위키를 수정해보면 서버에 알림이 가고 서버가 슬랙으로 위키를 수정했다는 알림이 갑니다.

정상적으로 메시지가 가지 않을 경우엔 깃허브 웹훅 등록 페이지에서 테스트 훅 버튼을 누르고 docker logs <CONTAINER_ID> 명령어를 통해서 요청이 정상 전달되는지부터 확인할 필요가 있습니다. 이 방법을 통해서 리모티에서는 무사히 슬랙으로 이전을 마쳤습니다.

정리

이 글에서는 깃허브 훅 API 서버를 다루고 중간에서 깃허브이 전달해주는 메시지를 처리하는 서비스를 소개해보았습니다. 팀의 필요에 기반해서 만들어진 정말 간단한 예제입니다만, 필요하다면 좀 더 복잡하고 고도화된 서버를 개발할 수도 있을 것입니다.