
2018년, GitHub Actions가 처음으로 등장하였고, 시간이 흐름에 따라 다양한 GitHub Actions 활용 사례가 등장하고 있습니다.
SmartEditor FE 팀도 GitHub Actions를 업무에 도입했습니다. 흔히 많이 적용하시는 Unit 테스트, Lint 테스트를 가장 먼저 적용하였고, 최근에는 직접 Custom Actions를 만들어 업무에 활용하고 있습니다.
이 글에서는 저희 팀의 Custom Actions 활용 사례에 대해 소개하고, 간단한 Custom Actions을 함께 만들어 보도록 하겠습니다.
Custom Actions
GitHub에서 기본으로 제공하는 Action들이 있습니다. 예를 들면, Runner 인스턴스에 node를 셋업하거나 빌드 결과를 캐싱하는 것들입니다. 하지만, 이는 매우 일반적이어서 우리의 다양한 요구사항을 만족하지 못합니다.
Custom Actions란 직접 만든 Action을 말합니다. 기본 Action으로는 채울 수 없는 요구사항을 각자의 필요에 따라 직접 만든 Action입니다.
유저들은 자신이 만든 Custom Actions를 Marketplace에 올리기도 합니다.
활용 사례
SmartEditor 팀은 코드 리뷰에 진심입니다. 어떻게 하면 코드 리뷰를 더 잘할 수 있을지 고민합니다. 그렇게 코드 리뷰를 위한 2가지의 Custom Actions를 만들었습니다.
1. Request PR Review – Slack으로 코드 리뷰 요청하기

정해진 시간에 Slack으로 코드 리뷰 요청을 보내는 Action입니다. 일을 하다 보면 코드 리뷰 하는 것을 깜빡하기 쉬운데, 주기적으로 알림을 보내주어 놓치지 않도록 도와줍니다.
2. Mergeable – Merge 가능한 상태 확인하기

PR이 Merge 조건에 부합하는지 확인해 주는 Action입니다. GitHub에서 제공하는 n명 이상의 Approve
조건만으론 부족함을 느껴 만들게 되었습니다.
참고로 현재 FE 팀에서 사용하는 Custom Action에서는, 아래 조건을 모두 만족할 때 Mergeable Checks가 통과하게 됩니다.
- 만든 지 1일이 경과하였는가
- 참석자(Participants) 전원이 동의하였는가
- 최소 3명 혹은 절반 이상의 Reviewer가 동의하였는가
GitHub Actions 이해하기
GitHub Actions는 어떻게 동작하는 걸까요? 굉장히 간단합니다.
우선 GitHub은 여러 이벤트들을 제공합니다. PR이 생성되었음을 알리는 이벤트, 이슈에 코멘트가 생성되었음을 알리는 이벤트, Reviewer로 지정되었음을 알리는 이벤트 등 다양한 이벤트가 있습니다. (GitHub Actions에서 사용되는 이벤트)

이런 이벤트들의 발생에 따라 반응(실행)할 Action(행동)을 정의한 것이 바로 GitHub Actions입니다. 예를 들어 PR 관련 이벤트에 따라 코멘트를 남기고 싶다면 pull_request
이벤트를 구독하고, 그에 맞춰 실행할 Action을 만들면 됩니다.
다시 말해, 어떤 특정 이벤트에 맞춰 하고 싶은 일들을 정의해 둔 것이라 할 수 있죠.
직접 만들어 보기
그럼 Custom Actions를 직접 만들어 보겠습니다. 간단하게 PR이 올라오면 고맙다는 댓글(Thank you for your contribution!
)을 달아주는 Custom Actions를 만들어 보겠습니다. 목차는 다음과 같습니다:
- 기본 Action 만들기
- Action 적용하기
- Marketplace에 올리기
1. 기본 Action 만들기
우선 첫 걸음은 console을 찍는 것으로 시작해 보겠습니다.
Action이 될 GitHub Repo를 생성해 주세요. 저는 ygnoh/actions-thank-you-new
로 만들었습니다.
이제 Action 명세서를 작성할 거예요. 명세서는 이 Action이 어떠한 환경에서 실행되고, 이 Action을 가져가 사용하려면 어떤 인자를 전달해 줘야 하는지 등의 정보를 담는 곳입니다. GitHub Actions는 이러한 명세서를 action.yml
파일로서 정의합니다.
그럼 Repo 루트에 action.yml
파일을 생성하고 다음 내용을 작성해 주세요:
name: "Thank you"
description: "Replying to participants"
runs:
using: "node16"
main: "index.js"
이때 명세서의 각 필드가 의미하는 것이 무엇인지 살펴 볼까요:
name
: 만들고자 하는 Action의 이름입니다.description
: 어떠한 Action인지 설명하는 필드입니다.runs
: Action이 실행되는 환경과 그때 실행할 스크립트를 명시합니다.- 예시에서는 Node 16 버전에서 실행이 될 것이고,
index.js
파일을 실행한다고 명시한 것을 알 수 있습니다. (꼭 Node 혹은.js
파일일 필요는 없습니다.)
- 예시에서는 Node 16 버전에서 실행이 될 것이고,
다음은 Action의 핵심인 index.js
파일을 작성해 보겠습니다. 동일하게 루트에 index.js
파일을 생성하고 다음의 내용을 작성합니다:
console.log("Thank you for your contribution!");
내용은 워낙 간단해서 설명이 필요 없을 정도죠?
이렇게 아주 간단한 Action을 만들어 보았습니다. action.yml
에 Action에 대한 명세를 작성하였고, 이때 실행될 스크립트가 index.js
라고 명시해주었습니다. 다시 말해, 이 Action이 실행 되면 console.log
를 실행해 Thank you for your contribution!
이 출력되게 될 것입니다.
2. Action 적용하기
우리가 방금 만든 Thank you Action을 실행해 보도록 할게요.
우선 새로 Repo를 만듭니다. 저는 ygnoh/actions-tutorial-new
라고 만들었습니다.
이제 이 새로운 Repo에서 이벤트가 발생할 때마다, 우리가 만든 Thank you Action을 실행하도록 할 거예요. 그러기 위해서는 workflow 파일을 만들어야 합니다. workflow란 어떤 조건(이벤트)에 따라 행동할 것들(jobs)을 정의해 둔 것이라 보시면 됩니다.
.github/workflows/
하위에 thank-you.yml
파일을 다음과 같이 작성합니다:
name: Thank you
on:
workflow_dispatch:
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Thank you
uses: ygnoh/actions-thank-you-new@main
하나씩 살펴 볼게요:
name
: workflow의 이름입니다.on
: 어떤 이벤트에 반응하여 실행할 것인지 명시하는 부분입니다.- 지금 이 workflow는
workflow_dispatch
다시 말해, 수동으로 실행하는 경우에 반응하도록 명시하였습니다.
- 지금 이 workflow는
jobs
: workflow는 하나 이상의 여러 job들의 조합으로서 구현됩니다. 즉 어떠한 목표(workflow)를 달성하기 위해 해야 할 일들(jobs)인 셈이죠. 참고로 각각의 job은 기본적으로 병렬로 실행됩니다. 이들 job 간의 의존 관계를 주어 특정 job 수행 이후 다른 job이 수행되도록 할 수도 있습니다.main
: main이라는 이름을 가진 job입니다. 이 필드는 이름에 불과하니 마음대로 결정할 수 있습니다.runs-on
: 실행될 Runner의 이름입니다. Runner는 GitHub Actions를 실행할 머신이라고 볼 수 있습니다. 그럼 서버 구축이 필요한 것인가 생각하실 수도 있는데, 특별한 경우가 아니면 GitHub에서 기본으로 제공해주는ubuntu-latest
를 사용하셔도 무방합니다. (GitHub에서 제공하는 Runner로는ubuntu-latest
,windows-latest
,macos-latest
등이 있습니다.)steps
: 해당 job이 실행될 흐름입니다.name
: step의 이름입니다.Thank you
라고 명명했네요.uses
: 이 step이 사용할 Action입니다. 여기서 우리가 만든 Action을 설정해 주어야 합니다. 저 같은 경우는ygnoh/actions-thank-you-new
Action을 사용한다고 하였고, 이때@main
을 명시해main
브랜치의 코드를 사용한다고 명시하였습니다.
정리하면 수동 실행으로 ubuntu-lasted
머신 위에서 우리가 이전에 만들어 둔 Action을 실행한다는 것을 알 수 있습니다.
그럼 한번 수동으로 실행(workflow_dispatch
)해 볼까요?

Repo 상단의 Actions
탭으로 이동하고, 우리가 만든 Thank you
workflow를 선택, Run workflow
를 누릅니다.
그러면 workflow가 수동으로 실행되고, 다음과 같은 결과가 나타납니다:

우리가 만든 ygnoh/actions-thank-you-new
라는 Action이 실행되었음을 알 수 있고, 정상적으로 Thank you for your contribution!
로그가 찍힌 것을 볼 수 있습니다.
간단하죠?
하지만 이렇게 매번 수동으로 Action을 실행한다는 건 큰 의미가 없어요. 새로운 PR이 열리면 로그가 찍히도록 개선해 볼게요. 어디를 개선해야 할지 감이 오시죠? 언제 반응할지에 대한 이야기이니 Action 자체를 고치지는 않아도 돼요.
Action을 가져가 사용하는 Repo의 .github/workflow/thank-you.yml
파일을 고쳐봅시다:
name: Thank you
on:
pull_request:
types: [opened]
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Thank you
uses: ygnoh/actions-thank-you-new@main
구독할 이벤트를 명시하는 부분인 on
부분이 변경되었어요. 이제 pull_request
의 opened
이벤트에 대해서 구독하게 됩니다! 다양한 이벤트 종류에 대해서 이 곳에 나와 있으니 참고하시길 바랍니다.
그럼 이제 PR을 만들어 Action이 반응하는지 확인해 볼까요? 간단하게 아무런 파일이나 추가하거나 수정하고 PR을 만들어 봅니다.

Checks를 열어 보면 우리의 workflow가 실행되었음을 알 수 있습니다. 괄호 안에는 pull_request
이벤트를 통해 트리거 되었다는 것이 명시되어 있네요.
그럼 우측 Details를 눌러 Action까지 정상적으로 실행되었나 볼게요:

우리가 만든 Action까지 잘 호출된 것을 볼 수 있습니다. PR이 열림에 따라 workflow가 반응하고 액션을 호출하게 되었네요.
그럼 좀 더 개선해서 감사 인사를 로그가 아닌 새로 열린 PR 댓글에 남기도록 해 보죠. 이때 여러 라이브러리들의 도움을 받으면 편합니다.
우리가 만든 Action 프로젝트 루트 경로에 패키지 매니징 세팅을 하고 사용할 라이브러리들을 설치합니다:
npm init -y
npm install @actions/core
npm install @actions/github
@actions/core
라는 라이브러리는 workflow input, output 관리 및 exit 등을 편하게 도와주는 라이브러리이고, @actions/github
은 Octokit이란 인스턴스를 통해 Action 내에서 GitHub을 제어하기 쉽게 해주는 라이브러리입니다.
이제 바꿔야 할 파일이 조금 많습니다. 두 Repo의 파일들 모두 조금씩 변경해야 하는데요. 우선 우리가 만든 Action Repo 쪽 파일들 먼저 바꿔 볼게요:
name: "Thank you"
description: "Replying to participants"
runs:
using: "node16"
main: "index.js"
inputs:
token:
description: "github token"
required: true
처음으로 action.yml
즉, Action 명세서에 inputs
필드를 추가했습니다. 이 Action은 token
이란 값을 필수(required
)로 가져야 한다고 추가한 건데요. 이 값은 GitHub Repo에 접근하기 위해 필요한 토큰입니다.
다음으로는 index.js
를 수정해 볼게요:
const github = require("@actions/github");
const core = require("@actions/core");
async function run() {
const {GITHUB_REPOSITORY, GITHUB_REF} = process.env;
const prNum = GITHUB_REF.match(/^refs\/pull\/(.+)\/merge$/)[1];
const [owner, repo] = GITHUB_REPOSITORY.split("/");
const token = core.getInput("token");
const octokit = github.getOctokit(token)
await octokit.rest.issues.createComment({
owner,
repo,
issue_number: prNum,
body: "Thank you for your contribution!"
});
}
run();
process.env
를 통해 환경변수를 가져오고 이를 이용해 prNum
, owner
, repo
등을 추출하는 것을 볼 수 있습니다. 참고로 GITHUB_REPOSITORY
와 GITHUB_REF
는 기본으로 제공받는 환경변수입니다. 자세한 내용은 이 곳을 확인해 주세요.
또한 core.getInput
을 통해 외부로부터 Action에 전달된 token
인자를 꺼내오고, 이를 통해 octokit
인스턴스를 만드는 것을 볼 수 있습니다. octokit
은 좀 전에 말씀드린대로 GitHub을 제어하기 쉽게 도와주는 인스턴스입니다.
마지막으로는 createComment
를 통해 해당 prNum
에 코멘트를 남기는 것을 볼 수 있습니다. 그럼 commit을 하고 push를 하도록 할게요.
node_modules/
도 함께 commit에 포함되어야 합니다.
이제 가져가 사용하는 쪽의 Repo를 수정해 주면 끝입니다. .github/workflow/thank-you.yml
을 다음과 같이 수정해 주세요:
name: Thank you
on:
pull_request:
types: [opened]
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Thank you
uses: ygnoh/actions-thank-you-new@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
이전과 달라진 것은 GitHub 접근을 위해 with
로써 token
을 전달해 주고 있다는 점이에요. 이 토큰은 secrets.GITHUB_TOKEN
으로 GitHub에서 기본으로 제공해주는 토큰입니다.
그럼 이제 다시 PR을 만들어 동작을 확인해 볼까요?

Thank you for your contribution!
댓글이 달렸네요! 이로써 PR이 생성되면 감사 인사를 댓글로 남겨주는 Custom Action을 만들고, 가져가 사용하는 것까지 해 보았습니다.
3. Marketplace에 올리기
마지막으로, 우리가 만든 Action이 다른 사람들에게도 홍보가 되면 좋겠죠? 그러면 자연스레 사용자도 많아질 거고요. 이렇게 사용자들이 만든 Action을 올리는 곳이 Marketplace입니다.
그럼 Thank you Action도 올려볼까요?

Repo 상단의 Draft a release
버튼을 눌러 볼게요.

몇 가지 수정하거나 추가해야 할 내용이 보이네요. 내용을 수정하고 publish를 하도록 합니다.

정상적으로 publish가 되었다면 이렇게 Marketplace 검색 결과에서 우리 Action을 찾을 수 있습니다.

이제 우리가 만든 Action을 다른 사람들이 쉽게 가져가서 사용할 수 있게 되는 것이죠. README.md
를 잘 작성한다면 내용이 좀 더 알차 보이겠죠?
마치며
어떠셨나요? 굉장히 간단한 예시라서 따라오는 데에는 어려움이 없으셨을 거라 생각합니다. 하지만 예시가 간단한 것에 비해 다양한 활용 가능성을 보셨을 거라 생각합니다.
여러분 입맛에 맞춘 여러분만의 Custom Actions를 만들어 보는 건 어떨까요? 한번 도전해 보시죠!