GitHub Actions 강좌 #5 재사용: composite action과 reusable workflow
워크플로를 몇 개 만들다 보면 같은 스텝 뭉치가 복사되기 시작합니다. checkout과 언어 설치와 캐시 설정 세 스텝이 모든 워크플로 앞머리에 붙어 있고, 저장소가 여러 개면 저장소 수만큼 복제됩니다. 캐시 키 하나 고치려면 전부 찾아다녀야 하는 상황, 코드라면 함수로 뽑았을 신호입니다. GitHub Actions의 함수가 이번 편의 두 주인공, composite action과 reusable workflow입니다.
composite action: 스텝 묶음을 액션으로 #
#1부터 써 온 actions/checkout 같은 액션을 직접 만드는 가장 쉬운 방법이 composite action입니다. 스텝들을 action.yml 파일로 포장하는 것입니다. 저장소 안에 두는 로컬 액션부터 봅니다.
# .github/actions/setup-project/action.yml
name: setup-project
description: 체크아웃 + 파이썬 + 의존성 설치를 한 번에
inputs:
python-version:
description: 파이썬 버전
default: "3.13"
runs:
using: composite
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
cache: "pip"
- run: pip install -r requirements.txt
shell: bash쓰는 쪽은 경로로 참조합니다.
steps:
- uses: actions/checkout@v4 # 로컬 액션을 읽으려면 체크아웃이 먼저
- uses: ./.github/actions/setup-project
with:
python-version: "3.12"문법에서 걸리는 두 가지를 짚어 둡니다. composite의 run 스텝에는 shell: bash를 명시해야 하고(일반 워크플로와 달리 생략 불가), 로컬 액션은 저장소 파일이므로 checkout이 먼저 와야 읽을 수 있습니다. 입력은 inputs로 선언해 ${{ inputs.이름 }}으로 쓰며, outputs도 선언할 수 있어 #2의 스텝 출력과 같은 방식으로 값을 돌려줍니다.
reusable workflow: 잡 전체를 호출 #
composite가 스텝 묶음이라면, reusable workflow는 잡 구성 전체를 재사용합니다. 워크플로에 workflow_call 트리거를 달면 다른 워크플로가 호출할 수 있는 부품이 됩니다.
# .github/workflows/deploy-template.yml (공용 저장소)
on:
workflow_call:
inputs:
environment:
type: string
required: true
secrets:
deploy-role:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
ROLE: ${{ secrets.deploy-role }}호출하는 쪽은 잡 자리에 uses를 씁니다.
# 각 서비스 저장소의 워크플로
jobs:
deploy-prod:
uses: my-org/workflows/.github/workflows/deploy-template.yml@v1
with:
environment: production
secrets:
deploy-role: ${{ secrets.PROD_DEPLOY_ROLE }}스텝의 uses가 아니라 잡의 uses라는 위치 차이가 두 방식의 본질 차이를 그대로 보여 줍니다. reusable workflow는 러너 지정, environment, 여러 잡 구성까지 통째로 가져오므로, 호출하는 쪽은 값만 넣는 얇은 껍데기가 됩니다. secrets는 자동으로 넘어가지 않아 명시적으로 전달해야 하며(secrets: inherit로 전부 넘길 수도 있지만 필요한 것만 넘기는 쪽이 #9의 최소 권한 원칙과 맞습니다), 호출 깊이 제한 같은 제약이 있으니 부품을 부품으로 겹겹이 감싸는 설계는 피합니다.
선택 기준과 조직 표준화 #
둘의 선택 기준은 한 줄로 정리됩니다. 잡 안의 반복이면 composite, 잡 구성 자체의 반복이면 reusable workflow입니다. “체크아웃과 설치” 같은 준비 뭉치는 composite, “우리 회사 표준 배포 절차” 같은 파이프라인은 reusable workflow가 맞습니다. 둘은 배타적이지 않아서, reusable workflow의 스텝이 composite action을 쓰는 조합이 자연스럽습니다.
조직 차원의 표준화는 공용 저장소 하나로 시작합니다. my-org/workflows 같은 저장소에 reusable workflow와 composite action을 모으고, 각 서비스 저장소는 @v1처럼 태그로 버전을 고정해 가져다 씁니다. 공용 부품을 고치면 태그를 올리고, 각 저장소는 준비될 때 버전을 올리는 구조라서 테라폼 기초 강좌 #8의 모듈 버전 고정과 완전히 같은 원리입니다. 반복이 실제로 두 번 이상 나타났을 때 부품화한다는 도입 시점의 기준까지 같습니다.
정리 #
이번 글에서 다룬 내용입니다.
- 반복되는 스텝 뭉치는 composite action(action.yml)으로 포장합니다. run 스텝에 shell 명시, 로컬 액션은 checkout 선행이 문법 함정입니다
- 잡 구성 전체의 재사용은 workflow_call 트리거의 reusable workflow이며, 호출은 잡 자리의 uses입니다
- reusable workflow에 secrets는 자동으로 넘어가지 않습니다. 필요한 것만 명시 전달이 기본입니다
- 기준은 “잡 안의 반복이면 composite, 잡 구성의 반복이면 reusable"이고 둘의 조합이 자연스럽습니다
- 조직 표준화는 공용 저장소 + 태그 버전 고정입니다. 테라폼 모듈 운영과 같은 원리입니다
다음 글(#6 Docker 빌드와 GHCR 푸시)부터는 부품들을 실전 파이프라인으로 조립합니다. 첫 번째는 컨테이너 이미지를 빌드해 GitHub의 레지스트리에 올리는 파이프라인입니다.