첫 Slack-Ops 구축해 보기!
새로운 사이드 프로젝트를 시작하면서, 슬랙 채널에 깃허브 레포지토리 알림 연동작업을 진행하려던 차.
문득 이전 프로젝트들을 돌이켜보니, 슬랙에 노션 알림과 깃허브 알림이 남발하는 게 보기 싫었던 기억이 난다.
모든 알림 들을 다 받게 되면 슬랙은 마치 양치기 소년과 같아지는 것 같다.
한두 번은 알림 왔을 때 보게 되지만, 어느 순간부터는 _'아 또 노션이겠지~'_하고선 안 보게 되기 때문이다.
깃허브 알림 또한 마찬가지다. 슬랙 앱 내에 있는 Github Application을 설치해서 알림을 받게 되면 특정 채널에 모든 알림 들을 받게 되므로, 같은 채널에 속해있지만 나랑 관련 없는 알림까지 봐야 한다는 단점이 있다.
그래서 이번엔 특정 사용자에게 멘션이 된 알림만 뜨도록 Slack 자동화를 구현해 보았다.
이렇게 하면 평상시에는 해당 채널 알림을 비활성화하고, 나에게 멘션 되었을 때 필요한 알림만 받아볼 수 있게 된다.
구글링 하던 중, 해당 기능에 대한 좋은 글이 있어 참고하며 진행하였다.
참고 링크
파이프라인 구축 과정
전반적인 아키텍처는 다음과 같다.
- Assignees 지정과 함께 Pull Request 요청 시, Github Webhook이 요청 이벤트를 캐치한다.
- Github Webhook이 AWS API gateway로 해당 PR이 담긴 JSON Request를 보낸다.
- 요청을 받은 API gateway는 해당 메시지를 고대로 AWS Lambda함수에게 POST 요청을 보낸다.
- 건너 건너 결국엔 Lambda 함수에 PR 정보가 들어오게 되고, Lambda함수는 Slack Webhook API에 PR 알림 등록을 위한 POST 요청을 보낸다.
- PR 알림 요청을 받은 Slack Webhook이 해당 채널에 사용자들을 멘션함으로써 선택한 사용자만 알림을 볼 수 있게 된다.
순서대로 구현하기엔 API gateway, Lambda를 먼저 구축해야 하므로, 역순으로 구현해야 한다.
1. Slack Webhook 설정
Webhook이란?
A webhook in web development is a method of augmenting or altering the behavior of a web page or web application with custom callbacks.
-Wikipedia-
hook(갈고리)라는 단어에서 알 수 있듯이, Webhook은 지정해 놨던 이벤트가 발생했을 때 자동으로 일련의 행동들을 하게끔 Trigger해주는 기능이라고 비유할 수 있다.
정확히 말하자면, 웹페이지나 웹앱의 지정된 이벤트들을 커스텀 된 콜백으로 전환해 주는 기능을 Webhook이라고 한다.
기존의 관습대로 하면, 클라이언트가 서버에게 요청을 보내야만 서버의 응답을 받아낼 수 있었다.(Pooling)
하지만 Webhook은 서버에서 이벤트 발생 시 클라이언트에게 알려주고, 콜백 url을 통해 클라이언트가 서버에 요청을 보낼 수 있게 해주는 똑똑한 친구다!
그래서 Webhook은 _역방향 API_라고도 불린다고 한다.
서버 : 이벤트 발생!
- 서버 -> 클라이언트 : "[자동 알림] 나 이벤트 발생함!"
- 클라이언트 -> 서버 : "네가 보낸 알림 받고 요청한다! 응답값 있는 거 다 아니까 내.놔"
- 서버 -> 클라이언트 : 응답. 클라이언트는 원하는 결과 받음.
Incoming Webhook 설치
먼저, Slack에서 지원해주는 웹훅 프로그램인 'Incoming Webhook'을 설치해야한다.
알림받고 싶은 채널의 채널 상세정보 보기
-> 통합
-> 앱
에서 해당 프로그램을 검색 후 설치한다.
정상적으로 설치되었으면 위와 같은 페이지로 리다이렉트된다.
알림을 받을 서버와 이름, 아이콘을 자유롭게 커스텀한 후 저장
!
나는 토토로 아이콘으로 봇을 꾸며보았다.
아 벌써 귀엽다.
다음으로 넘어가기 전에, POSTMAN을 이용하여 Webhook URL에 샘플 페이로드를 POST 했을 때 알림이 정상적으로 뜨는 지를 테스트해보고 넘어가자.
2. AWS Lambda로 POST 요청 함수 만들기
Slack Webhook을 이용하여 일련의 이벤트가 발생했을 때 Slack으로 알림 POST 요청을 보낼 수 있다는 건 확인했다. 근데..Webhook은 어떻게 이벤트가 발생한걸 알지?
이에 대해 알려주는 기능을 AWS Lambda로 구현한 함수에서 해준다.
- 함수 이름 : 원하는 대로 작명하자.
- 런타임 : 자신이 코드 작성에 사용할 언어를 기반으로 하는 런타임을 선택해주자.
나는 Python이 젤 편하기에 Python 3.10 환경을 선택했다.
AWS Lambda는 Layers 추가를 통해 외부 라이브러리를 추가할 수도 있지만, 혹시 모를 과금 문제와 웬지 모를 복잡함때문에 최대한 지양하고, 기본 라이브러리로 작성하였다.
처음에는 lambda_function.py라는 Default 파일과 기본 코드가 적혀있을 것이다.
lambda_function.py 내에서 WebHook으로의 POST 요청 메서드를 작성해보자.
import json
import urllib.request
SLACK_SERVER_CH_URI = 'https://hooks.slack.com/services/[생략]'
GITHUB_SLACK_MAP = {
"SeungHo0422": "[SLACK memberID]",
"kaley0421": "[SLACK memberID]",
...
"Github ID": "Slack memberID"
}
Slack Mentioning의 핵심 과정
Request에는 해당 PR에 대한 정보들이 담겨 있다. 그 중에는 PR에 할당된 Asignees member들의 GithubID도 들어있는데, 코드의 GITHUB_SLACK_MAP
처럼 Github ID와 Slack ID를 Key:Value 쌍으로 매핑함으로써 슬랙 채널의 Asignee들을 멘션할 수 있게한다.
물론 좋은 코드라고 할 순 없다. (사용자가 추가/수정/삭제되면 하드코딩 해야 하므로..) 시간이 된다면 이 부분도 리팩토링 하고 싶다.
def lambda_handler(event, context):
pr_contents = event["pull_request"]
slack_message = ''
if not pr_contents['assignees']:
slack_message = f'''
[PR] Assignee: <@{GITHUB_SLACK_MAP[pr_contents['assignee']['login']]}>
'''
else:
slack_message += '[PR] Assignees: <@'
for i in range(len(pr_contents['assignees'])):
slack_message += GITHUB_SLACK_MAP[pr_contents['assignees'][i]['login']] + '>'
if i == len(pr_contents['assignees'])-1:
break
else:
slack_message += ', <@'
payload = {
"channel": "dev-server", #웹훅이나 슬랙에서 지정한 채널이 무엇이든, 여기 적힌 채널로 알림간다.
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": slack_message
},
}],
"attachments": [
{
"fallback": "[LINKIT] New Pull Request",
"color": "#00E000",
"author_name": '작성자: '+pr_contents['user']['login'],
"title": pr_contents['title'],
"title_link": pr_contents['url'],
"text": f'```{pr_contents["body"]}```',
"short": False,
}
]
}
headers = {
"Content-Type": "application/json"
}
try:
data = json.dumps(payload).encode('utf-8')
request = urllib.request.Request(SLACK_SERVER_CH_URI, data=data, headers=headers, method='POST')
with urllib.request.urlopen(request) as response:
response_text = response.read().decode('utf-8')
print('Message sent to Slack successfully:', response_text)
return {
'statusCode': response.status,
'body': 'Message sent to Slack successfully!\nevent: ' + str(event)
}
except Exception as e:
print('Error sending message to Slack:', e)
return {
'statusCode': 500,
'body': 'Error sending message to Slack'
}
코드를 보면 알 수 있듯이, Github PR을 등록했을 때의 정보들이 AWS Lambda로 전송된다면 Slack Webhook으로 보내주는 역할을 하고 있다.
하지만 문제점이 있다. 람다가 동작할 수 있는 '트리거'가 현재까지는 없다. 따라서 AWS Gateway를 통해 Lambda로 request할 수 있도록 만들어보자.
3. AWS API Gateway로 Github와 Lambda 연결해주기
AWS Lambda 함수 개요에는 사진과 같이 트리거를 추가할 수 있다. 해당 버튼을 눌러 API Gateway를 추가해주자.
트리거 구성은 어렵지 않다. REST API 타입으로 지정한 후, Security는 Open으로 지정했다. (우리 프로젝트는 보안적으로 굳이 IAM이나 다른 것들을 등록할 필요를 못느꼈다.) API name까지 지정해준 후 완료!
완료 후 해당 API Gateway 화면으로 넘어가보면, Default로 ANY 요청이 만들어져있는걸 확인할 수 있다.
우리는 Lambda에 POST요청을 보내야 하므로, 새로운 라우터를 만들어보자.
POST 요청 만들기
작업 -> 메서드 생성을 누른 후, 드롭박스에서 POST를 선택해준다.
POST 리소스 설정을 해줘야 하는데, 우리는 Lambda에 보내줄거니까 Lambda 함수를 지정해주고, 보내줘야하는 Lambda 함수의 리전과 ARN 주소를 잘 작성해준다. ARN 주소 정보는 Lambda 함수 정보에 있다.
이로써 API Gateway를 통해 POST 요청을 보낼 준비까지 완료되었다. 배포를 통해 Request URL을 만들어보자.
이렇게 작업 -> API 배포 버튼을 누른 뒤,
새로운 스테이지를 생성한 후, 해당 스테이지에 배포해보자. 나는 Version1 스테이지에 배포하였다.
그 후, 스테이지 탭으로 접속하고 나서 임의로 만든 스테이지를 클릭하면 호출 URL 정보를 조회할 수 있다. 후에 Github Webhook 등록 과정에서 쓰일 주소다.
거의 끝났다. 이제 만들어놓은 API를 마지막으로 Github PR 등록 시 Github Webhook을 통해 해당 Payload를 API Gateway로 보내주는 트리거를 만들어보자!
4. Github Webhook 설정
연결하고자 하는 Repository 페이지에 들어간 후, Settings -> Webhooks를 통해 Webhooks 설정 페이지로 접속한다. Add webhook을 통해 새로운 웹훅을 만들어보자.
- Payload URL : API Gateway에 보내기 위한 url이므로, API Gateway를 통해 배포한 url 주소를 넣어줘야 한다. 만약 에러가 난다면 이곳 주소를 제대로 적지 않았을 가능성이 매우 높다.
- Content type : 우리는 JSON 형식으로 보낼 것이니 application/json으로 지정
- Secret : 공백으로 처리한다. (혹시 여러분들의 프로젝트가 보안성을 가져야 한다면 토큰 처리를 해줘야한다.)
- Which events would you like to trigger this webwook?
: Webhook을 통해 어떤 이벤트를 전송하고 싶은 지 선택하는 곳이다. Let me select individual events를 클릭 한 후, 하위 항목 중Pull requests
만을 클릭한다. - 마지막으로 Active, 즉 트리거 활성화 여부을 체크한 후 Update Webhook을 누르면 완성!
회고
서버리스 서비스들을 직접 이용해보며 불편한 점을 뚫어내 본 경험은 처음이였는데, 이렇게도 쉽게 연결이 될 수 있구나..!라는 것을 느끼게 된 시간이였다. 다음에는 Cloudwatch 같은 로그 모니터링 서비스들을 통해 트리거 파이프라인을 정교하게 구현해보고 싶다.
혹여 위 순서대로 따라했는데 안되시는 분들은 댓글로 문의해주세요!