Skip to main content

Command Palette

Search for a command to run...

[CI/CD] 수동 태그에서 자동 릴리즈까지 — Git Flow와 Auto Release

Published
5 min read

🚀 우리 auto-release.yml 바로 보러가기 →

이 글에서 설명하는 워크플로우의 전체 코드를 바로 확인할 수 있다!

main에 머지만 하면 버전 태그부터 릴리즈 노트까지 알아서 생긴다


1. 우리의 Git 전략: Git Flow (경량 버전)

Finders 프로젝트는 Git Flow 전략을 사용하고 있다. 다만 hotfix나 release 브랜치 없이, 조금 가볍게 운영한다.

main        ← 운영 서버 (prod) 배포 브랜치
develop     ← 개발 서버 (dev) 배포 브랜치
feature/*   ← 기능 개발 브랜치 (이슈 기반)
fix/*       ← 버그 수정 브랜치

Git Flow 브랜치 전략

출처: A successful Git branching model — Vincent Driessen

흐름

  1. 이슈 생성 → 브랜치 생성 (feat/signup-api-#14)

  2. 개발 완료 → develop으로 PR → CI 통과 + 코드 리뷰 → 머지

  3. develop 머지 → Dev 서버 자동 배포 (프론트와 연동 테스트)

  4. 테스트 완료 → develop → main PR → 승인 → 머지

  5. 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.0v2.0.0
MINOR새 기능 추가v1.0.0v1.1.0
PATCH버그 수정v1.0.0v1.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 = 자동으로 정리되는 프로젝트 히스토리라는 걸 깨달았다. 커밋 메시지를 대충 쓰면 릴리즈 노트도 대충 나오니까, 팀원들의 커밋 습관도 자연스럽게 좋아졌다. 👍


📎 참고

More from this blog

[Elasticsearch] Elasticsearch 기본용어와 CRUD 명령어

-elastic ▶ 들어가며 이번 글에서는 Elasticsearch를 공부하면서 가장 먼저 익혀야 하는 기본 용어를 정리하고,직접 코드를 쳐가며 CRUD(Create / Read / Update / Delete) 명령어에 익숙해지는 시간을 가져보려고 한다. Elasticsearch는 처음 보면 생소한 용어가 많아서 막막할 수 있는데,사실 구조적으로는 우리가 익숙한 MySQL과 닮은 부분이 굉장히 많다. 둘 다 데이터베이스라는 큰 틀 안에서 데...

Feb 17, 20264 min read

Jpa N+1 문제, 우리는 이렇게 잡았다 — 1:1 문의 Api 실전 최적화기

코드 리뷰 한 줄에서 시작된 쿼리 최적화 여정 1. 시작 — "일단 돌아가게 만들자" Finders 프로젝트에서 1:1 문의(Inquiry) API를 맡았다. 현상소에 문의를 남기고, 답변을 받고, 목록을 조회하는 — 평범한 CRUD다. "JPA 쓰면 쿼리 안 짜도 되는 거 아니야?" 솔직히 처음엔 그렇게 생각했다. JpaRepository에 findAll, findById 쓰면 끝이니까. // 첫 번째 버전의 목록 조회 (QueryDS...

Feb 11, 20268 min read

[모니터링] Sentry 도입부터 Discord 에러 알림까지 — 서버 감시 시스템 구축기

"서버 죽었는데 아무도 몰랐다"에서 "에러나면 1분 안에 안다"까지 1. 모니터링을 시작한 계기 프론트엔드: "API 안 되는데요?"백엔드: "엥? 언제부터요?"프론트엔드: "...2시간 전부터요?"백엔드: 😱 어느 날 서버가 죽어있었는데 아무도 몰랐다. 그날 이후, 모니터링 시스템 구축을 결심했다. (Issue #102) 2. 모니터링 도구 비교: 뭘 쓸까? 처음에는 여러 도구를 비교했다. 도구무료 티어장점단점 Sent...

Feb 11, 20268 min read

[ci/cd] 대학생 팀의 배포 파이프라인 진화기

PR 하나면 끝나는 무중단 배포까지, 삽질의 기록 1. 시작은 단순했다 "서버에 올려야 하는데... 어떻게 하지?" Finders 프로젝트를 시작했을 때, 배포라는 걸 해본 적이 없었다. 그래서 처음에는 이렇게 했다. 1. SSH로 서버 접속 2. git pull 3. ./gradlew build 4. java -jar app.jar 당연히 문제가 생겼다. 빌드하는 동안 서버가 꺼져있음 (프론트: "API 왜 안 돼요?" 🔥) 빌...

Feb 11, 20267 min read
F

Finders Tech Blog

16 posts