UX Engineer 이야기

Github Actions를 이용한 Slack App(Bot) 만들기

hyejun.lee 2024. 5. 16. 07:50

들어가며


현재 진행하고 있는 프로젝트는 Github을 이용해 형상 관리 중입니다.

프로젝트에서 작업한 브랜치는 PR(Pull Request)을 통해 팀원들에게 코드 리뷰를 받고, 이후에 master 브랜치에 합치는 방식으로 진행하고 있습니다. 여기서 내가 PR의 코드 리뷰어로 선정되었는지 알 수 있는 방법은 회사 메신저인 Slack의 Github 봇을 통해 알림을 받을 수 있었습니다.

하지만 PR의 코드 리뷰어로 선정되었다는 정보 말고 PR 관련 모든 정보가 함께 와서 필요 없는 정보로 Slack 채널의 오염도가 높아지는 걸 느꼈습니다.

원하지 않는 pr 내용까지 보여주고 있다.

심지어 Slack을 창 모드로 작게 사용하면 단 2개의 알림으로도 채팅창이 꽉 차는 걸 볼 수 있습니다.

함께 보는 팀 채널에 올라온다면 내가 코드 리뷰어로 선정되었다는 글은 이미 저 위로 없어지고 난 이후 일 테죠.

이런 부분을 개선해 볼 수 있지 않을까 싶어서 찾아보다가 커스텀 Slack 알림 봇을 만들어보기로 했습니다.

 

원하는 봇의 형태는?


일단 어떤 형태로 알림이 오면 좋을지 생각해 보았습니다.

피로감을 쌓이게 했던 많은 정보를 없애고 필요한 정보만 메시지로 오게 설정하고 싶었습니다.

  1. PR의 reviewer을 선정한다.
  2. 프로젝트 채널에 PR 제목, 선정된 reviewer 멘션, PR 링크가 기재된 메시지가 온다.
  3. reviewer는 링크를 눌러서 리뷰를 하러 간다.
PR 리뷰어로 선정되었습니다!
제목: pr 제목
리뷰어: @리뷰어1, @리뷰어2
링크: 리뷰하러 가기

 

어떻게 하는지 찾아보자


필자는 봇에 대해 어떠한 지식도 없었기 때문에 일단 구글에 Slack Bot 만드는 방법을 검색해 보았습니다.

검색해 보면 Incoming Webhook이라는 용어를 굉장히 많이 볼 수 있었는데요.

Incoming Webhook 이란 Slack이 원하는 형식으로 데이터를 보내면 Slack에 메시지를 보낼 수 있는 기능을 말합니다. 이를 사용하면 외부 앱에서 Slack으로 메시지를 보낼 수 있습니다.

이를 활용한 여러 가지 다양한 방법(서버 연결을 통한 상호작용, 구글 스프레드 활용)으로 봇을 만들 수 있었습니다. 이미 프로젝트는 진행 중이어서 빠르게 작업할 수 있는 방법을 찾다 보니 상대적으로 쉬워 보였던 Github Actions를 사용하기로 결정했습니다.

Gihtub Actions는 GitHub에서 제공하는 지속적인 통합(Continuous Integration, CI) 및 지속적인 배포(Continuous Deployment, CD) 서비스입니다. YAML 구문으로 정의되어 있으며 이를 사용하면 코드 변경 사항(Issue 생성, PR 생성 등)을 자동으로 테스트하고, 빌드하고, 배포할 수 있습니다.

Incoming Webhook, Github Actions 이 두 가지를 이용하여 만드는 방법으로 바로 넘어가 보겠습니다.

 

Slack 봇부터 만들어보자


  1. 일단 Slack에 로그인된 상태에서 https://api.slack.com/apps에 접속합니다.
  2. 아래 화면에서 Create New App 버튼을 클릭합니다.
  3. From scratch를 선택합니다.
    • From scratch: 처음부터 새로운 앱을 만드는 것을 의미합니다.
    • From an app manifest: 기존에 정의된 앱의 설정과 기능이 포함된 템플릿을 사용하여 앱을 만드는 것을 의미합니다.
  4. 생성하려는 봇 이름을 입력하고 봇을 사용하고자 하는 워크스페이스를 선택합니다. 그 후 Create App 버튼을 클릭합니다.
  5. 아까 위에서 알아본 Incoming Webhooks라는 메뉴를 눌러줍니다.
  6. Incoming Webhooks을 사용하기 위해 off 버튼을 눌러 on으로 바꿔줍니다.
  7. 아래로 내리면 있는 Add New Webhook to Workspace 버튼을 눌러줍니다. 워크스페이스에 새로운 Webhook을 추가하는 버튼입니다.
  8. 기본적으로 봇을 사용할 채널을 하나 선택해 줘야 합니다. 그 후 허용을 눌러줍니다.
  9. 그러면 Sample curl 코드가 생성되는데 copy를 눌러서 터미널에 붙여 넣으면 선택한 채널에 ‘Hello, World!’가 찍히는 걸 확인할 수 있습니다.
  10. 제가 필요한 기능은 리뷰어로 선정된 사람이 Slack 채널에 멘션 되는(@리뷰어) 기능입니다. 그러려면 추가로 권한 설정을 해야 합니다.
    • chat:write - 채널 또는 개별 사용자에게 텍스트 메시지를 보낼 수 있습니다.
    • chat:write.customize - 특정 채널이나 메시지를 강조하거나 스타일링할 수 있는 기능을 제공합니다.
      권한이 바뀌면서 봇을 재설치 하라는 배너가 뜨는데 reinstall_your_apps 텍스트를 눌러서 채널에 다시 추가해 줍니다.
  11. 이렇게 하면 채널에 자동으로 추가되는 줄 알았지만 Slack에서 한 가지 더 필요한 부분이 있었습니다. 채널 통합 탭에서 앱 추가로 만들었던 봇을 추가해야지 정상 작동됩니다. (제가 설정을 빠트린 부분일 수도 있을 것 같은데 다른 설정이 있다면 댓글로 알려주세요.)
  12. Slack에서 설정한 부분은 끝났으니 이제 Github으로 넘어가 볼까요?

 

Github Actions 만들어보자


  1. 사용할 Github 레파지토리의 상단 메뉴 중에 Actions를 클릭합니다. 아래 Workflow 생성 버튼을 클릭합니다. GitHub Workflow는 GitHub Actions를 사용하여 어떤 작업을 수행할지 정의하는 파일입니다. 이 Wrokflow를 통해 우리가 원하는 메시지를 보낼 수 있습니다. root 폴더에서. github/workflows 폴더 안에 원하는 workflow .yml 파일을 만들면 됩니다. 물론 여기에서 작성해도 되지만 저는 vscode에서 파일을 생성해서 작성해 보겠습니다.
  2. pr_reviewer.yml라는 이름으로 파일을 하나 생성했습니다.
  3. PR에 리뷰어로 등록되면 Slack으로 알림이 오는 코드를 작성해 봅시다. 저는 slackapi/slack-github-action 을 사용해서 구현해 보겠습니다.
    • slackapi/slack-github-action 는 일종의 라이브러리라고 생각하면 될 것 같은데요, GitHub Actions에서 Slack과 상호 작용할 수 있도록 도와주는 공식 Slack 액션 중 하나입니다.
    # pr_reviewer.yml
    name: Pull Request #workflow 이름을 설정합니다.
    
    on: # workflow가 실행될 이벤트를 설정합니다.
    	pull_request: # PR 이벤트에 대해 설정합니다.
    		types: [review_requested] # 리뷰 요청된 이벤트를 감지합니다.
    
    jobs: # workflow에서 실행할 작업을 정의합니다.
    	specific_review_requested: # 작업의 이름을 지정합니다.
    		runs-on: ubuntu-latest # 작업이 실행될 환경을 설정합니다.
    		steps: # 작업 단계들을 정의합니다.
    		  - name: pr reviewer 되면 slack 알림 보냄 # PR에 리뷰어가 할당되면 Slack에 알림을 보내는 단계의 이름을 설정합니다.
    		    uses: slackapi/slack-github-action@v1.24.0 # Slack 액션을 사용합니다.
    		    with: # 입력으로 전달할 값들을 설정합니다.
    		      channel-id: ${{ secrets.TEST_CHANNEL }} # Slack 채널 ID를 비밀로 설정합니다.
    		      payload: | # Slack에 보낼 메시지 페이로드를 설정합니다.
    		        {
    		          "text": "pr review request", # 메시지의 본문을 설정합니다.
    		          "blocks": [ # 메시지의 블록을 설정합니다.
    		            {
    		              "type": "section", # 블록의 타입을 설정합니다.
    		              "text": { # 텍스트 타입의 블록을 설정합니다.
    		                "type": "mrkdwn", # 텍스트의 마크다운 타입을 설정합니다.
    		                "text": "리뷰어로 할당되었습니다.\\\\n • 제목: PR 제목\\n • 리뷰어: @리뷰어 \\n • 링크: 리뷰 링크" # 메시지의 내용을 설정합니다.
    		              }
    		            }
    		          ]
    		        }
    		    env: # 환경 변수를 설정합니다.
    		      SLACK_BOT_TOKEN: ${{ secrets.TEST_BOT_TOKEN }} # Slack 봇 토큰을 비밀로 설정합니다.
    
    여기서 secrets. 이 부분이 궁금하실 수 있으실 것 같은데요. GitHub Actions에서 사용하는 비밀 변수입니다. 예를 들어, Slack 채널의 ID나 API 토큰과 같은 민감한 정보를 비밀 변수에 저장하여 workflow에서 사용할 수 있습니다.
    1. 비밀 변수를 설정하는 방법은 Settings > Secrets and variables > Actions > New repository secret 버튼으로 설정할 수 있습니다.
    2. 비밀 변수명과 값을 입력합니다.
    3. TEST_CHANNEL - 채널의 ID 값으로 채널 환경 맨 아래에서 확인할 수 있습니다.
    4. TEST_BOT_TOKEN - 아까 만들어둔 Slack 토큰 값을 입력합니다.
    5. 이제 PR의 리뷰어를 선정하는 순간 채널에 알림이 옵니다!
  4. 이제 고정 텍스트가 아닌 각 PR의 제목과 리뷰어 그리고 링크를 가져와 볼까요?
    아래는 최종 코드입니다.
    # pr_reviewer.yml
    name: Pull Request #workflow 이름을 설정합니다.
    
    on: # workflow가 실행될 이벤트를 설정합니다.
    	pull_request: # PR 이벤트에 대해 설정합니다.
    		types: [review_requested] # 리뷰 요청된 이벤트를 감지합니다.
    
    jobs: # workflow에서 실행할 작업을 정의합니다.
    	specific_review_requested: # 작업의 이름을 지정합니다.
    		runs-on: ubuntu-latest # 작업이 실행될 환경을 설정합니다.
    		steps: # 작업 단계들을 정의합니다.
    			- uses: actions/checkout@v3 # GitHub 저장소를 체크아웃하는 액션을 사용합니다
    			
    			- name: Get Reviewers List # 리뷰어 목록을 가져오는 단계입니다.
    				id: reviewers # 이 단계의 ID를 설정합니다.
    				uses: actions/github-script@v6 # GitHub 스크립트 액션을 사용합니다.
    				with: # 입력으로 전달할 값들을 설정합니다.
    				script: | # 리뷰어 목록을 가져오는 JavaScript 스크립트입니다.
    						const fs = require('fs'); # 파일 시스템 모듈을 가져옵니다.
    						const workers = JSON.parse(fs.readFileSync('.github/workflows/reviewers.json')); # .github/workflows/reviewers.json 파일을 읽어와 JSON 형식으로 파싱합니다.
    						const mention = context.payload.pull_request.requested_reviewers.map((user) => { # PR에서 요청된 리뷰어 목록을 순회하며 Slack 멘션 문자열을 생성합니다.
    							const login = user.login; # 리뷰어의 로그인 ID를 가져옵니다.
    							const mappedValue = workers[login]; # 리뷰어의 로그인 ID를 키로하여 .github/workflows/reviewers.json 파일에서 해당하는 값(슬랙 멘션 ID)을 가져옵니다.
    							return mappedValue ? `<@${mappedValue}>` : `No mapping found for ${login}`; # 만약 슬랙 멘션 ID가 존재하면 슬랙 멘션 문자열을 반환하고, 그렇지 않으면 'No mapping found for'와 리뷰어의 로그인 ID를 포함한 문자열을 반환합니다.
    						})
    						return mention.join(', '); # 생성된 슬랙 멘션 문자열을 쉼표로 구분하여 반환합니다.
    				result-encoding: string # 결과를 문자열로 인코딩합니다.
    				
    			- name: pr reviewer 되면 slack 알림 보냄 # PR에 리뷰어가 할당되면 Slack에 알림을 보내는 단계의 이름을 설정합니다.
    				uses: slackapi/slack-github-action@v1.24.0 # Slack 액션을 사용합니다.
    				with: # 입력으로 전달할 값들을 설정합니다.
    					channel-id: ${{ secrets.TEST_CHANNEL }} # Slack 채널 ID를 비밀로 설정합니다.
    					payload: | # Slack에 보낼 메시지 페이로드를 설정합니다.
    						{
    							"text": "pr review request", # 메시지의 본문을 설정합니다.
    							"blocks": [ # 메시지의 블록을 설정합니다.
    								{
    									"type": "section", # 블록의 타입을 설정합니다.
    									"text": { # 텍스트 타입의 블록을 설정합니다.
    										"type": "mrkdwn", # 텍스트의 마크다운 타입을 설정합니다.
    										"text": "리뷰어로 할당되었습니다.\\n • 제목: ${{ github.event.pull_request.title }}\\n • 리뷰어: ${{ steps.reviewers.outputs.result }} \\n • 링크: <${{ github.event.pull_request.html_url }}|리뷰하러 가기>" # 메시지의 내용을 설정합니다.
    									}
    								}
    							]
    						}
    				env: # 환경 변수를 설정합니다.
    					SLACK_BOT_TOKEN: ${{ secrets.TEST_BOT_TOKEN }} # Slack 봇 토큰을 비밀로 설정합니다.
    
    github. 이 부분은 Github Actions의 trigger 부분으로 Github Actions 문서를 통해서 해당 기능을 확인해보세요
    • github.event.pull_request.title - PR의 제목을 가져옵니다.
    • github.event.pull_request.html_url - PR의 링크를 가져옵니다.
    여기서 많이 헤맸던 부분이 바로 Slack 멘션을 거는 부분입니다. github.event.pull_request.requested_reviewers를 통해 GitHub 프로필 이름은 가져왔지만, 이것이 Slack의 프로필 이름과 일치하지 않아서 멘션을 걸지 못한 것입니다.
    • Github 프로필 이름 - test
    • Slack 프로필 이름 - 테스트
    • ➡️ 🙅‍♀️불일치
    그래서 두 값을 매칭시켜줄 수 있는 reviewers.json 파일을 생성했습니다. 이 작업에서 시간이 많이 소요된 이유는 간단히 Slack 프로필 이름으로 매칭시키면 될 거라고 생각하여 "test": "@테스트", 이렇게 작성했지만 원하는 Slack의 멘션 형태가 아닌 단순 텍스트(@테스트)로 표현되어서 찾아보니 Slack member ID로 매칭시켜야 한다는 것을 알게 되었습니다.
    Profile 메뉴에서 Copy member ID 값을 reviewers.json에 넣어봅시다.
    // reviewers.json
    // "Github 프로필 이름": "Slack member ID"
    {
    	"test": "U123456789",
    	"test2": "U123456789",
    	"test3": "U123456789",
    }
    
  5. 우리가 원했던 형태의 알림으로 오게 되었습니다! 🙌 (짝짝짝)

 

마치며


이 글을 작성한 이유는 당시에 검색해도 정확히 원하는 내용의 글은 없어서 여러 글들을 읽으면서 힘들게 찾아보고 만들었지만 정작 결과를 정리한 문서가 없는 것 같아서 블로그 글을 통해 히스토리를 남겨보려 했습니다.

다른 분들에게도 도움이 되었으면 좋겠고 더 좋은 방법을 찾게 되거나 다른 용도의 봇을 만들게 되면 또 다른 글로 찾아오겠습니다.

읽어주셔서 감사합니다.

이 글은 pxd XE Group Blog에서도 보실 수 있습니다.