[CI/CD] 수동 태그에서 자동 릴리즈까지 — Git Flow와 Auto Release
🚀 우리 auto-release.yml 바로 보러가기 →
이 글에서 설명하는 워크플로우의 전체 코드를 바로 확인할 수 있다!
main에 머지만 하면 버전 태그부터 릴리즈 노트까지 알아서 생긴다
1. 우리의 Git 전략: Git Flow (경량 버전)
Finders 프로젝트는 Git Flow 전략을 사용하고 있다. 다만 hotfix나 release 브랜치 없이, 조금 가볍게 운영한다.
main ← 운영 서버 (prod) 배포 브랜치
develop ← 개발 서버 (dev) 배포 브랜치
feature/* ← 기능 개발 브랜치 (이슈 기반)
fix/* ← 버그 수정 브랜치

출처: A successful Git branching model — Vincent Driessen
흐름
이슈 생성 → 브랜치 생성 (
feat/signup-api-#14)개발 완료 → develop으로 PR → CI 통과 + 코드 리뷰 → 머지
develop 머지 → Dev 서버 자동 배포 (프론트와 연동 테스트)
테스트 완료 → develop → main PR → 승인 → 머지
main 머지 → Prod 서버 자동 배포 + 릴리즈 자동 생성 ← 여기!
2. 처음에는 수동이었다
처음에는 릴리즈를 전부 수동으로 만들었다.
# 1. main으로 이동
git checkout main
git pull origin main
# 2. 태그 생성
git tag v0.7.0
# 3. 태그 푸시
git push origin v0.7.0
그러면 .github/release.yml 설정에 의해 GitHub Release가 생성되었다.
뭐가 문제였을까?
매번 수동으로 해야 함 → 까먹기 쉬움
버전 번호를 직접 정해야 함 → "이번에 뭐였지... v0.7? v0.8?"
릴리즈 노트를 직접 작성해야 함 → 귀찮아서 대충 쓰게 됨
main 머지 후 태그 푸시를 깜빡하면 릴리즈가 안 생김
결국 issue #344를 만들었다:
"main 머지만 하면 태그 + 릴리즈가 자동 생성되도록 워크플로우를 개선합니다."
3. Auto Release 워크플로우 만들기
전체 흐름
핵심 코드
1단계: 버전 결정
# PR 제목에서 버전 추출 시도
if [[ "$COMMIT_MSG" =~ v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
VERSION="v${BASH_REMATCH[1]}"
# → PR 제목이 "[RELEASE] v0.9.2 배포" 이면 v0.9.2 사용
else
# 버전 없으면 PATCH 자동 증가
IFS='.' read -ra PARTS <<< "${LAST_TAG//v/}"
PATCH=$((PARTS[2] + 1))
VERSION="v${PARTS[0]}.${PARTS[1]}.${PATCH}"
# → v0.9.1 이 마지막이면 v0.9.2 자동 생성
fi
2단계: 릴리즈 노트 자동 생성
커밋 메시지의 prefix(feat, fix, docs...)를 분석해서 자동 분류한다:
# feat 커밋만 모아서 Features 섹션
FEATS=$(git log "${LAST_TAG}..HEAD" --pretty=format:"- %s (%h)" --grep="^feat")
# fix 커밋만 모아서 Bug Fixes 섹션
FIXES=$(git log "${LAST_TAG}..HEAD" --pretty=format:"- %s (%h)" --grep="^fix")
# docs, refactor, test도 각각 분류
3단계: GitHub Release 생성
gh release create "$VERSION" \
--title "Release ${VERSION}" \
--notes-file RELEASE_NOTES.md \
--latest
4. 실제 릴리즈는 이렇게 생긴다
📎 실제 릴리즈 목록: Finders BE Releases →
예를 들어 Release v0.9.5의 릴리즈 노트는 이렇게 자동 생성되었다:
# Release v0.9.5
## Changes since v0.9.4
### ✨ Features
- feat: 전용 terraform-ci SA 추가 및 Compute SA 역할 분리 (#396)
- feat: 공지 api 수정
### 🐛 Bug Fixes
- fix: swagger 문서 수정
- fix: 위치 미 동의시 현상소 조회의 경우 distanceKm가 반환되지 않게 수정
- fix: 배포 스크립트 서버 경로 통일 (#391)
### 📚 Documentation
- docs: 아키텍처 문서 업데이트 (#385)
### ♻️ Refactoring
- refactor: outputs.tf를 단일 jsonencode deploy_config로 통합 (#393)
사람이 한 일: PR 머지 버튼 클릭. 끝.
5. 두 가지 배포 시나리오
시나리오 A: 일반 배포 (PATCH 자동 증가)
develop → main PR
제목: "[FIX] 이미지 업로드 버그 수정 (#123)"
→ 마지막 태그 v0.9.4에서 자동으로 v0.9.5 생성
시나리오 B: 메이저/마이너 릴리즈 (버전 명시)
develop → main PR
제목: "[RELEASE] v1.0.0 배포"
→ PR 제목에서 v1.0.0 추출해서 그 버전으로 태그 생성
포인트: 특별한 릴리즈(메이저, 마이너 버전 업)일 때만 PR 제목에 버전을 적으면 되고, 일반적인 패치는 알아서 버전이 올라간다!
6. 버전 관리 규칙 (Semantic Versioning)
v MAJOR . MINOR . PATCH
↓ ↓ ↓
1 . 2 . 3
| 버전 | 올리는 시점 | 예시 |
| MAJOR | 호환 안 되는 큰 변경 | v1.0.0 → v2.0.0 |
| MINOR | 새 기능 추가 | v1.0.0 → v1.1.0 |
| PATCH | 버그 수정 | v1.0.0 → v1.0.1 |
현재 Finders는 아직 v0.x.x 대로, 빠르게 기능을 추가하는 단계다. v1.0.0은 정식 서비스 출시 시점에 찍을 예정!
7. Before vs After
| Before (수동) | After (자동) | |
| 태그 생성 | git tag v0.x.x 직접 입력 | PR 머지 시 자동 |
| 버전 결정 | "이번 버전 뭐였지?" | PATCH 자동 증가 |
| 릴리즈 노트 | 대충 적거나 안 적음 | 커밋 기반 자동 분류 |
| GitHub Release | 수동 생성 or 까먹음 | 100% 자동 |
| 실수 가능성 | 태그 안 찍음, 중복 태그 | 중복 체크 내장 |
8. 마치며
릴리즈 자동화는 "편해지자"가 아니라 "실수를 없애자"에 가깝다.
수동으로 하면:
까먹는다 (태그 안 찍는 건 일상)
대충 한다 (릴리즈 노트 "잡다한 수정" 한 줄)
틀린다 (중복 태그, 잘못된 버전)
자동으로 하면:
머지만 하면 끝 → 깜빡할 수가 없음
커밋 메시지가 곧 릴리즈 노트 → feat/fix 잘 쓰는 습관이 중요해짐
중복 체크 내장 → 같은 태그 두 번 생성 불가
결국 좋은 커밋 메시지 컨벤션 + Auto Release = 자동으로 정리되는 프로젝트 히스토리라는 걸 깨달았다. 커밋 메시지를 대충 쓰면 릴리즈 노트도 대충 나오니까, 팀원들의 커밋 습관도 자연스럽게 좋아졌다. 👍