🚀 요약
SUMMARY
CI라면 Jenkins·Drone CI·GitLab CI만 써봤는데, Deck을 GitHub에서 하며 GitHub Actions를 처음 만졌다. 이후 사내 여러 리포 CI도 Actions로 옮기며 이벤트 트리거·job/step·재사용 워크플로우(
workflow_call)·매트릭스·make위임·릴리스/배포 트리거가 손에 익었다. 로직은make로 밀어 로컬과 CI를 일치시키고, 공통 본체는workflow_call로 한 번만, 보안 스캔은 상시 잡으로 둔 게 골자다.
Jenkins·GitLab CI에서 하던 걸 Actions 어휘로 다시 배우는 과정이었다.
1. 기존 CI와 다르게 느낀 점
먼저 감을 잡느라 알던 것과 대응부터 시켰다.
- GitLab CI는 리포 하나에
.gitlab-ci.yml하나,stages로 순서를 잡는다. GitHub Actions는.github/workflows/아래 파일 여러 개를 두고 각각을 이벤트로 켠다. - Jenkins는 서버(컨트롤러+에이전트)를 내가 세우고 관리해야 했다. Actions는 GitHub 호스티드 러너가 있어
runs-on: ubuntu-latest면 실행 환경이 그냥 주어진다. (self-hosted도 되지만, 관리 부담 없이 시작할 수 있는 게 컸다.) - 제일 낯설었던 건 이벤트 기반이라는 점이다. “push되면”, “PR 열리면”, “태그 밀면”, “매일 밤”, “수동 버튼”이 각각 별도 트리거고, 하나의 워크플로우가 그중 무엇에 반응할지를
on:에 적는다.
# 이벤트가 워크플로우를 켠다 — PR/브랜치별로 다른 파일을 둘 수 있다
on:
push:
branches: [main]
pull_request:
workflow_dispatch: # 손으로 실행하는 버튼2. job과 step, 그리고 make로 몰기
워크플로우는 jobs로 나뉘고, 각 job은 격리된 러너에서 돈다. job 안은 steps의 나열이고, step은 액션(uses:)이나 셸 명령(run:)이다.
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5 # 리포 체크아웃 — 재사용 액션
- uses: actions/setup-go@v5 # Go 설치
- run: make build
- run: make lint
- run: make verify-generated # 생성물(문서 등)이 최신인지여기서 원칙을 하나 정했다. 실제 로직은 전부 make로 밀었다. run:에 긴 스크립트를 박지 않고 make build, make lint, make vuln처럼 부른다. CI YAML이 얇아져 읽기 쉽고, 같은 명령을 로컬에서도 그대로 돌릴 수 있어 “CI에서만 되고 내 노트북에선 안 되는” 상황이 준다. Jenkins Groovy에 로직을 몰아넣었다가 로컬 재현이 안 돼 고생했던 게 반면교사였다.
3. 재사용 워크플로우로 중복 없애기
Deck에는 트리거가 여럿이다. main push용(ci-main.yml), PR용(ci-pr.yml). 둘이 하는 일(빌드·린트·검증·보안)은 거의 같아서, 복붙하면 한쪽만 고치는 사고가 난다. 그래서 재사용 워크플로우(workflow_call)로 공통 본체를 빼고, 트리거 파일은 그걸 호출만 한다.
# ci-reusable.yml — 공통 본체
on:
workflow_call: # 다른 워크플로우가 호출할 수 있게 노출
jobs:
checks: { ... } # build / lint / verify-generated
security: { ... } # make vuln
# ci-pr.yml — 트리거는 얇게, 본체는 호출
on: { pull_request: }
jobs:
ci:
uses: ./.github/workflows/ci-reusable.ymlGitLab CI의 include:/extends:로 하던 재사용을 Actions에선 workflow_call로 한다. 트리거별로 파일은 나뉘어도 진짜 CI 내용은 한 곳에만 있다.
4. 매트릭스와 보안 잡
매트릭스. 같은 job을 값만 바꿔 병렬로 돌린다. OS·버전 조합을 검증할 때 좋다.
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}보안 잡. CI에 취약점 스캔을 상시 스텝으로 넣었다. Deck에선 security job이 make vuln(Go 코드·의존성 스캔)을 돌린다. 취약점 점검을 “가끔 수동으로”가 아니라 PR마다 자동으로 돌게 한 게 포인트다. (스캔 도구와 대응 방식은 따로 정리했다.)
5. 릴리스와 문서 배포 트리거
이벤트가 다양하다는 걸 제일 체감한 건 릴리스·배포였다.
- 릴리스(태그 트리거).
on: push: tags: ['v*'].v1.2.3을 밀면 릴리스 워크플로우가 돈다. 스모크 테스트 후 goreleaser로 바이너리·패키지·brew formula를 낸다. - 문서 배포(Pages).
docs/가 바뀌면 Docusaurus를 빌드해 GitHub Pages로 배포한다.permissions: pages: write와concurrency(진행 중 배포는 안 끊기게)를 처음 써봤다. - 야간 e2e(스케줄).
on: schedule:로 매일 밤 종단 테스트를 돌려, 커밋이 없어도 회귀를 잡는다.
6. 여러 리포에 짜면서 종합한 것
Deck 말고도 사내 리포 CI를 Actions로 옮기면서 패턴이 굳었다(구체 내용은 사내라 생략).
- 제품 리포: 빌드·테스트 매트릭스 + 컨테이너 이미지 빌드·푸시.
- 차트 리포: 헬름 차트 lint·템플릿 렌더 검증 + 사용 이미지 취약점 스캔.
- 인프라 리포: Terraform
fmt/validate.
리포가 달라도 굳은 습관은 셋이었다. 로직은 make로 밀어 로컬과 CI를 일치시키고, 공통 본체는 workflow_call로 한 번만 쓰고, 보안 스캔은 상시 잡으로 파이프라인에 박는다. 결국 낯설었던 건 도구가 아니라 이벤트 기반·호스티드 러너·재사용 워크플로우라는 Actions 특유의 어휘였다.