Dev & Cloud

GitHub Actions란 무엇인가

workflow, job, step, runner를 실제 CI로 이해하기

GitHub Actions가 PR 같은 이벤트에서 무엇을 어떤 순서로 실행하는지, workflow·job·step·action·runner의 차이를 실제 CI 설정으로 읽고 첫 workflow를 만드는 법까지 설명합니다.

왼쪽의 입력 블록이 작은 받침에 닿자 중앙 자동 작업대의 네 개 모듈이 가운데 블록을 처리하고, 오른쪽 받침에 완료된 블록이 놓인 3D 일러스트

목차
  1. PR 하나가 검사되는 과정
  2. 여섯 가지 말을 한 번에 구분하기
  3. 실제 ci.yml을 읽어 봅니다
  4. CI와 CD는 따로 볼 수 있습니다
  5. GitHub Actions로 할 수 있는 일
  6. 쓸 만한 경우와 아직 필요 없는 경우
  7. 첫 workflow는 테스트 하나로 시작합니다
  8. 처음부터 지켜 둘 것
  9. 사용량과 비용이 궁금해지면

weftware-web 저장소에서는 pull request를 열어도 사람이 터미널에서 검사 명령을 하나씩 실행하지 않습니다. GitHub Actions가 시작되어 GitHub가 제공하는 Ubuntu 머신에서 저장소 코드를 가져오고, lint, typecheck, test, build, check를 순서대로 돌립니다. PR 화면의 checks 목록에 check가 통과로 표시되면 이 작업이 끝까지 성공했다는 뜻입니다.

이 workflow는 배포를 하지 않습니다. 저장소 README는 GitHub Actions를 PR 검사에만 쓰고, 배포는 Cloudflare Workers Builds가 맡는다고 적고 있습니다. GitHub Actions를 쓴다고 해서 배포까지 맡겨야 하는 것은 아니라는 점이 이 구성에서 먼저 보입니다.

GitHub Actions를 한 문장으로 말하면, GitHub 저장소에서 어떤 일이 생겼을 때 미리 적어 둔 작업을 자동으로 실행하는 기능입니다. 공식 문서는 CI/CD 플랫폼이라고 소개하면서도, 거기에 그치지 않고 저장소의 다른 이벤트에도 반응할 수 있다고 설명합니다. 예를 들어 누가 새 issue를 만들면 알맞은 label을 자동으로 붙이는 workflow를 만들 수 있습니다.

PR 하나가 검사되는 과정

PR 하나가 열려서 검사 결과가 돌아오기까지는 다섯 단계를 거칩니다.

  1. PR을 엽니다. GitHub에 “pull request가 열렸다”는 이벤트(event, 저장소에서 일어난 일)가 생깁니다.
  2. GitHub가 저장소의 .github/workflows 폴더에서 이 이벤트에 맞는 workflow 파일을 찾습니다. 파일의 on에 pull_request가 있으면 workflow 실행(run)이 만들어집니다.
  3. workflow 안의 job이 runner(job을 실제로 실행하는 머신)에서 시작됩니다. weftware-web의 job은 check 하나입니다.
  4. job 안의 step 8개가 위에서 아래로 차례로 실행됩니다.
  5. 결과가 PR에 통과 또는 실패로 표시됩니다.
PR 하나가 GitHub Actions에서 실행되는 순서위에서 아래로 이어지는 그림입니다. pull request가 열리면 pull_request 이벤트가 생기고, 이 이벤트에 맞는 workflow인 ci.yml이 시작됩니다. workflow 안의 job인 check가 ubuntu-latest runner에서 실행되고, 그 안의 step 여덟 개가 순서대로 실행됩니다. 여덟 step은 checkout, setup-node, npm ci, lint, typecheck, test, build, check입니다. 마지막으로 결과가 PR에 통과 또는 실패로 표시됩니다.pull request가 열린다이벤트: pull_requestworkflow 시작ci.yml · on: pull_requestjob: checkrunner: ubuntu-latest에서 실행step 8개, 위에서 아래로1 checkout2 setup-node3 npm ci4 lint5 typecheck6 test7 build8 check결과가 PR에 표시된다통과 또는 실패
PR 하나가 검사되는 순서(WEFTWARE 개념도). weftware-web의 CI를 기준으로 그렸습니다.

PR에 커밋을 더 올리면 같은 검사가 다시 돕니다. 공식 문서에 따르면 pull_request는 활동 유형을 따로 정하지 않았을 때 PR이 열릴 때(opened), 새 커밋이 올라올 때(synchronize), 다시 열릴 때(reopened) 실행됩니다. 반대로 이 workflow는 push에는 반응하지 않습니다. on에 pull_request만 적혀 있기 때문입니다.

저장소 Actions 탭의 CI workflow 실행 목록. 위쪽에 CI와 ci.yml 링크가 있고 84 workflow runs가 표시돼 있다. 목록은 위에서부터 CI #84와 #83이 Pull request #41 synchronize, #82가 Pull request #41 opened, #81이 Pull request #40 synchronize, #80이 Pull request #40 opened, #79가 Pull request #39 opened, #78·#77·#76이 Pull request #38 synchronize이다. 모두 초록색 성공 표시이고 실행 시간은 46초에서 58초이다. 실행한 사용자 이름은 회색 상자로 가렸다.
Actions 탭에서 CI workflow를 고른 실행 목록(운영자 계정에서 직접 캡처). 같은 PR에서 PR을 열 때(opened)와 커밋을 올릴 때(synchronize)마다 실행이 하나씩 생겼습니다. 실행한 사용자 이름은 가렸습니다.

PR에는 Cloudflare Workers Builds의 체크도 함께 나옵니다. 이 체크는 Actions가 아니라 Cloudflare가 만듭니다. PR의 checks 목록에 있다고 모두 GitHub Actions의 결과는 아닙니다.

PR 화면의 checks 영역. All checks have passed, 3 successful checks가 표시돼 있고, 목록에는 CI / check (pull_request)가 45초 만에 성공, GitGuardian Security Checks가 1초 만에 성공(No secrets detected), Workers Builds: weftware-web이 성공으로 보인다.
pull request 화면의 checks 목록(운영자 계정에서 직접 캡처). CI / check (pull_request)는 workflow 이름 CI, job 이름 check, 시작한 이벤트 pull_request가 차례로 보이는 이름이고 Actions의 결과입니다. Workers Builds는 Cloudflare가, GitGuardian은 별도 서비스가 만든 체크입니다.

여섯 가지 말을 한 번에 구분하기

Actions 화면과 문서에는 비슷해 보이는 말이 여섯 개 나옵니다. 위 흐름에 나온 순서대로, 이 저장소의 값과 함께 구분합니다.

용어뜻weftware-web에서는
event(이벤트), trigger저장소에서 일어난 일. workflow를 시작시키는 조건pull_request
workflow하나 이상의 job을 실행하는 자동화 절차. .github/workflows의 YAML 파일 하나로 정의ci.yml(이름 CI)
job같은 runner에서 실행되는 step의 묶음check
stepjob 안의 한 단계. 명령(run)을 실행하거나 action(uses)을 사용8개
actionworkflow에서 가져다 쓰는, 미리 만들어진 재사용 단위actions/checkout, actions/setup-node
runnerjob을 실제로 실행하는 서버GitHub가 제공하는 ubuntu-latest

가장 많이 헷갈리는 것은 이름입니다. GitHub Actions는 이 기능 전체의 이름이고, action은 workflow 안에서 가져다 쓰는 부품 하나입니다. “GitHub Actions를 쓴다”와 “action을 쓴다”는 다른 말입니다. 이 저장소의 CI도 GitHub Actions를 쓰지만, 그 안에서 가져다 쓰는 action은 둘뿐이고 나머지 step은 npm 명령을 직접 실행합니다.

job과 step은 실행되는 방식이 다릅니다. 공식 문서에 따르면 job은 기본적으로 서로 의존하지 않고 병렬로 실행되며, 각 job은 자기 runner에서 돕니다. 반대로 한 job 안의 step은 같은 runner에서 순서대로 실행되므로, 앞 step이 만든 결과를 뒤 step이 쓸 수 있습니다. 이 저장소에서는 npm run build가 만든 결과물을 바로 뒤의 npm run check가 읽어서 링크와 SEO를 검사합니다. 둘이 같은 job의 step이라서 가능한 구조입니다.

runner는 job을 실제로 실행하는 서버입니다. 공식 문서는 GitHub가 Ubuntu Linux, Windows, macOS runner를 제공하고(GitHub-hosted runner), 직접 서버를 두는 self-hosted runner도 쓸 수 있다고 설명합니다. runner 하나는 한 번에 job 하나를 실행하고, GitHub-hosted runner를 쓰면 각 job은 runs-on으로 지정한 runner image의 새 인스턴스에서 실행됩니다. 이 저장소의 ubuntu-latest job도 실행할 때마다 새 가상 머신에서 시작합니다. 새 머신이라 저장소 코드가 처음에는 없으므로, 이 저장소의 첫 step이 actions/checkout입니다. 요금과 사용 시간 계산은 이 글에서 다루지 않습니다.

실제 ci.yml을 읽어 봅니다

아래는 weftware-web의 .github/workflows/ci.yml에서 핵심 구조만 발췌한 것입니다. 2026-10-09 main의 파일에서 몇 줄을 뺐을 뿐 나머지는 그대로입니다. 이 저장소의 workflow 파일은 이 하나뿐입니다.

yaml
name: CI

on:
  pull_request:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version-file: .nvmrc
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test
      - run: npm run build
      - run: npm run check

처음 보는 독자가 YAML 문법을 다 알 필요는 없습니다. 어디를 보면 무엇을 알 수 있는지만 짚습니다.

  • name: Actions 탭에 보이는 workflow 이름입니다. 생략하면 파일 경로가 대신 보입니다.
  • on: 언제 실행할지입니다. 여기서는 PR이 열리거나 새 커밋이 올라올 때입니다.
  • jobs: 실행할 job의 묶음입니다.
  • check: job ID입니다. PR의 checks 목록에 이 이름으로 보입니다.
  • runs-on: 어떤 runner에서 실행할지입니다.
  • steps: job 안에서 순서대로 실행할 단계입니다.
  • uses: 이미 만들어진 action을 사용합니다. actions/checkout은 저장소 코드를 runner로 가져오고, actions/setup-node는 Node.js 환경을 준비합니다. 바로 아래의 with는 그 action에 넘기는 값으로, 여기서는 Node 버전을 적어 둔 .nvmrc 파일과 npm 의존성 캐시를 지정합니다.
  • run: 셸 명령을 직접 실행합니다. npm ci로 의존성을 설치한 뒤 lint(코드 검사), typecheck(타입 검사), test(단위 테스트), build(사이트 빌드), check(빌드 결과의 링크·SEO 검사)를 차례로 돌립니다.
CI 실행의 check job 화면. 왼쪽 All jobs 아래에 check가 선택돼 있고 Run details에 Usage와 Workflow file이 있다. check job은 45초 만에 성공했다. 오른쪽 step 목록에는 Set up job 1초, Run actions/checkout@v4 1초, Run actions/setup-node@v4 9초, Run npm ci 9초, Run npm run lint 5초, Run npm run typecheck 10초, Run npm test 2초, Run npm run build 4초, Run npm run check 0초, Post Run actions/setup-node@v4 0초, Post Run actions/checkout@v4 1초, Complete job 0초가 모두 성공으로 표시돼 있다. 위쪽 Annotations에는 1 warning and 1 notice가 있다.
실행 한 건의 check job 화면(운영자 계정에서 직접 캡처). ci.yml에 적은 step 8개가 순서대로 보이고, 그 앞뒤로 Set up job, 두 action의 Post 단계, Complete job이 함께 보입니다. 시간은 이 실행 한 건의 값이며 실행마다 다릅니다. 위쪽 Annotations는 이 글에서 다루지 않습니다.

step 하나가 실패하면 job은 실패로 표시됩니다(step에 continue-on-error를 쓰지 않는 한). PR에서 빨간 표시가 나오면 Actions 탭에서 그 실행을 열고, 실패한 step을 펼쳐 로그를 읽는 것이 시작입니다.

첫 항목인 Set up job을 펼치면 이 job이 어떤 runner에서 어떤 설정으로 시작했는지 볼 수 있습니다.

check job의 Set up job 단계를 펼친 로그. 1번 줄에 Current runner version '2.337.0'이 있고, 접힌 항목으로 Runner Image Provisioner, Operating System, Runner Image, GITHUB_TOKEN Permissions가 있다. 이어서 Secret source: Actions, Cache mode: write, Prepare workflow directory, Prepare all required actions 줄과, actions/checkout@v4와 actions/setup-node@v4를 SHA와 함께 내려받는 줄, 마지막에 Complete job name: check가 보인다.
Set up job을 펼친 로그(같은 실행, 운영자 계정에서 직접 캡처). runner 버전과, 접힌 항목으로 운영체제와 runner image 정보가 있습니다. 공식 문서는 Runner Image 항목을 펼치면 그 runner에 설치된 도구 목록 링크가 나온다고 안내합니다.

원본에는 발췌에서 뺀 설정이 세 가지 더 있습니다. 같은 PR의 이전 실행을 취소하는 concurrency, 토큰 권한을 읽기로 줄이는 permissions, 실행 시간 상한인 timeout-minutes입니다. 개념을 따라가는 데 필요하지 않아 뺐고, 이런 설정이 사용 시간과 어떻게 이어지는지는 GitHub Actions 비용 줄이는 방법에서 다룹니다.

CI와 CD는 따로 볼 수 있습니다

CI(continuous integration)는 코드를 합치기 전에 빌드, 테스트, 검사를 자동으로 반복해서 확인하는 과정입니다. CD(continuous delivery)는 검사를 통과한 변경을 배포 과정까지 자동으로 이어가는 방식입니다.

weftware-web에서는 CI를 GitHub Actions가, 배포를 Cloudflare Workers Builds가 맡습니다. 한 저장소 안에서도 두 일을 서로 다른 도구가 나눠 맡을 수 있다는 예입니다. 물론 GitHub Actions로 배포까지 이어갈 수도 있습니다. 공식 문서는 release가 만들어질 때마다 애플리케이션을 배포하는 workflow를 예로 듭니다. 어느 쪽이 낫다는 이야기가 아니라, GitHub Actions가 곧 배포 도구는 아니라는 점을 구분하자는 것입니다.

GitHub Actions로 할 수 있는 일

공식 문서가 드는 예를 이벤트별로 옮기면 이렇습니다.

  • PR이 열릴 때: 빌드와 테스트를 돌립니다. 위에서 읽은 CI가 이 경우입니다.
  • release가 만들어질 때: 애플리케이션을 배포합니다.
  • issue가 열릴 때: 알맞은 label을 붙입니다.
  • 정해진 시각마다: schedule에 cron 형식으로 시각을 적으면 그 시각에 실행됩니다. 정기 실행은 기본 branch에서만 동작합니다.
  • 사람이 직접 누를 때: workflow_dispatch로 수동 실행 버튼을 둘 수 있습니다.

공통점은 사람이 명령을 입력해서가 아니라 GitHub의 이벤트나 시각이 작업을 시작시킨다는 것입니다. 목록 전체는 공식 문서의 “Events that trigger workflows”에 있습니다.

쓸 만한 경우와 아직 필요 없는 경우

아래는 공식 문서의 설명을 바탕으로 한 판단 기준이고, 정답이 정해져 있는 것은 아닙니다.

쓸 만한 경우:

  • PR마다 같은 검사를 반복하고, 사람이 하면 빠뜨릴 수 있습니다.
  • 빌드와 테스트 결과가 코드 변경 옆에 기록으로 남아야 합니다.
  • release나 배포처럼 반복되는 작업이 있습니다.
  • GitHub의 특정 이벤트에 자동으로 반응해야 합니다.

아직 필요 없을 수 있는 경우:

  • 혼자 하는 작은 실험이고 자동화할 반복 작업이 거의 없습니다.
  • 한 번만 실행하면 되는 작업입니다.
  • 저장소 이벤트와 상관없는 별도 시스템 작업입니다.

필요 없을 수 있다는 말이 쓰지 말라는 뜻은 아닙니다. 자동으로 돌려 두면 같은 검사를 매번 사람이 떠올리지 않아도 된다는 점은 규모와 상관없이 같습니다.

첫 workflow는 테스트 하나로 시작합니다

처음부터 배포까지 자동화하려고 하면 어디서 실패했는지 찾기 어렵습니다. PR에서 테스트 하나를 돌리는 정도로 시작하는 편이 낫습니다. 아래는 Node.js 프로젝트에 package-lock.json과 test 스크립트가 있다고 가정한 개념 예시이고, weftware-web의 파일이 아닙니다.

yaml
name: Test

on:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm test

저장소에 만드는 순서입니다.

  1. GitHub의 저장소 첫 화면에서 Add file(파일 추가)을 눌러 Create new file을 고르고, 파일 이름을 .github/workflows/test.yml처럼 씁니다. 슬래시를 쓰면 폴더가 함께 만들어집니다. workflow 파일은 반드시 .github/workflows 폴더에 있어야 GitHub가 찾습니다. 확장자는 .yml이나 .yaml입니다.
  2. 위 내용을 붙여 넣고 Commit changes(변경 사항 커밋)를 누릅니다. 새 branch를 만들어 PR을 시작하는 쪽을 고릅니다.
  3. 그 branch로 PR을 열면 workflow가 실행됩니다.
  4. 저장소 이름 아래의 Actions를 열고, 왼쪽에서 workflow를 고르고, 실행 목록에서 실행 한 건을 엽니다. 왼쪽 Jobs 아래의 job을 누르면 step별 로그가 나옵니다. 성공이면 PR의 checks에 통과로 표시됩니다.

저장소에 Actions 탭이 보이지 않으면 그 저장소에서 Actions가 꺼져 있을 수 있다고 공식 quickstart가 안내합니다. 이 경우 저장소 설정에서 Actions 사용 여부를 먼저 확인합니다.

action의 버전 표시(@v4)는 계속 바뀝니다. 이 저장소는 @v4를 쓰고, 공식 quickstart의 예시는 actions/checkout@v6을 씁니다. 복사해서 쓰기 전에 각 action 저장소의 안내에서 쓸 버전을 확인합니다.

테스트 하나가 PR마다 통과하는 것을 확인했다면, 그다음에 lint, typecheck, build를 step으로 하나씩 더합니다. 배포는 그 뒤에 필요할 때 따로 정합니다.

처음부터 지켜 둘 것

YAML을 그대로 복사해서 쓰는 일이 많으므로, 공식 보안 문서에서 초보자가 바로 적용할 수 있는 세 가지만 옮깁니다.

  • 외부 action은 가려서 씁니다. 공식 문서는 workflow에서 쓰는 action 하나가 침해되면 저장소에 설정된 secret에 접근하고 토큰으로 저장소에 쓰기까지 할 수 있어서 위험이 크다고 설명합니다. 가장 안전한 방법은 action을 전체 길이 commit SHA로 고정하는 것이고, 태그로 쓸 때는 만든 사람을 신뢰할 수 있을 때만 쓰라고 합니다. Marketplace의 “Verified creator” 표시는 참고 신호입니다.
  • 필요한 권한만 줍니다. workflow가 쓰는 GITHUB_TOKEN에는 필요한 최소 권한만 주는 것이 좋다고 공식 문서가 권합니다. 이 저장소의 CI도 원본에서 permissions로 읽기 권한만 줍니다.
  • secret 값을 workflow 파일에 직접 쓰지 않습니다. 비밀 값은 저장소의 secret으로 저장하고, 파일에서는 이름으로만 참조합니다.

이 글은 보안 가이드가 아니므로 여기까지만 다룹니다. 자세한 내용은 공식 “Secure use reference”에 있습니다.

사용량과 비용이 궁금해지면

GitHub Actions의 사용 시간과 요금은 이 글의 범위 밖입니다. 숫자가 바뀔 수 있어서 이 글을 숫자에 묶어 두지 않았습니다. 사용 시간이 갑자기 늘어서 원인을 찾아야 한다면 GitHub Actions 사용량이 갑자기 늘어났을 때를, 내 플랜에서 Actions 사용량이 충분한지 보려면 GitHub Free vs Pro를 읽으면 됩니다.

출처 및 참고자료

마지막 확인

출처 8개 더 보기출처 접기