DaleSchool

태그와 릴리스

입문20분

학습 목표

  • lightweight 태그와 annotated 태그의 차이를 이해할 수 있다
  • Semantic Versioning 규칙을 이해하고 적용할 수 있다
  • git tag로 버전을 관리하고 원격에 push할 수 있다
  • GitHub에서 릴리스를 만들 수 있다

동작하는 코드

예제 1: 태그 만들기

git init
echo "v1 코드" > app.py
git add app.py
git commit -m "feat: 초기 앱 구현"

# Lightweight 태그 (단순 포인터)
git tag v1.0.0
git tag

출력:

v1.0.0
# Annotated 태그 (권장, 메타데이터 포함)
git tag -a v1.0.0 -m "첫 번째 정식 릴리스"
git tag

출력:

v1.0.0

태그 상세 정보 확인:

git show v1.0.0

출력:

tag v1.0.0
Tagger: 홍길동 <hong@example.com>
Date:   Mon Jan 15 10:30:00 2024 +0900

첫 번째 정식 릴리스

commit a1b2c3d (HEAD -> main, tag: v1.0.0)
Author: 홍길동 <hong@example.com>
...

예제 2: 태그 목록 확인

echo "v2 코드" >> app.py && git add app.py && git commit -m "feat: 기능 추가"
git tag -a v1.1.0 -m "버그 수정 및 기능 추가"

echo "v3 코드" >> app.py && git add app.py && git commit -m "feat: 주요 업데이트"
git tag -a v2.0.0 -m "주요 업데이트, 하위 호환 불가"

git tag

출력:

v1.0.0
v1.1.0
v2.0.0
# 패턴으로 필터링
git tag -l "v1.*"

출력:

v1.0.0
v1.1.0

예제 3: 로그에서 태그 확인

git log --oneline --decorate

출력:

e4f5g6h (HEAD -> main, tag: v2.0.0) feat: 주요 업데이트
b2c3d4e (tag: v1.1.0) feat: 기능 추가
a1b2c3d (tag: v1.0.0) feat: 초기 앱 구현

직접 수정하기

Semantic Versioning (SemVer)

버전 번호는 MAJOR.MINOR.PATCH 형식입니다:

v2.1.3
  │ │ └── PATCH: 버그 수정 (하위 호환 유지)
  │ └──── MINOR: 새 기능 추가 (하위 호환 유지)
  └────── MAJOR: 하위 호환 불가 변경

언제 어떤 버전을 올리나?

| 변경 유형 | 예시 | 버전 변경 | | ------------ | ---------------- | ----------------- | | 버그 수정 | 로그인 버그 수정 | 1.0.01.0.1 | | 새 기능 추가 | 검색 기능 추가 | 1.0.11.1.0 | | 주요 변경 | API 구조 변경 | 1.1.02.0.0 |

PATCH를 0으로 초기화:

새 기능 추가: 1.2.3 → 1.3.0  (MINOR 증가, PATCH 0으로)
주요 변경:   1.3.0 → 2.0.0  (MAJOR 증가, MINOR, PATCH 0으로)

이전 커밋에 태그 추가

git log --oneline
# e4f5g6h 현재 커밋
# b2c3d4e 이전 커밋 (여기에 태그 추가하고 싶음)

git tag -a v1.1.0 b2c3d4e -m "v1.1.0 릴리스"
git log --oneline --decorate

원격에 태그 push

태그는 기본적으로 push되지 않습니다:

# 특정 태그 push
git push origin v1.0.0

# 모든 태그 push
git push origin --tags

# 특정 태그 삭제
git tag -d v1.0.0         # 로컬 삭제
git push origin --delete v1.0.0  # 원격 삭제

GitHub 릴리스 만들기

# GitHub CLI로 릴리스 생성
gh release create v1.0.0 \
  --title "v1.0.0 - 첫 번째 정식 릴리스" \
  --notes "## 변경 사항

### 새 기능
- 로그인/로그아웃 기능
- 사용자 프로필 페이지

### 버그 수정
- 세션 만료 오류 수정

### 주의사항
- Node.js 18 이상 필요"

GitHub 릴리스는 태그 + 설명 + 파일 첨부를 묶은 것입니다. 사용자들이 릴리스 노트를 보고 업그레이드 여부를 판단합니다.

"왜?" — 태그와 릴리스가 필요한 이유

태그 없이 버전 관리하면:

커밋 a1b2c3d → "이게 v1.0이었나?"
커밋 e4f5g6h → "언제 v2.0 배포했지?"

태그 있으면:

# 1.0.0 버전 코드 확인
git show v1.0.0

# 1.0.0 버전으로 체크아웃
git checkout v1.0.0

# 1.0.0과 2.0.0 차이 비교
git diff v1.0.0 v2.0.0

# 1.0.0에서 핫픽스 브랜치 생성
git checkout -b hotfix/1.0.1 v1.0.0

lightweight vs annotated 태그

| | Lightweight | Annotated | | --------- | -------------- | ----------------------------- | | 저장 내용 | 커밋 포인터만 | 태그 작성자, 날짜, 메시지 | | 명령어 | git tag v1.0 | git tag -a v1.0 -m "메시지" | | 서명 가능 | 불가 | 가능 (-s) | | 추천 상황 | 임시, 로컬용 | 공식 릴리스 |

공식 릴리스에는 annotated 태그를 사용하세요.

흔한 실수

실수 1: 태그를 push 하지 않기

git tag -a v1.0.0 -m "릴리스"
git push   # 태그는 push 안 됨!

# 확인
git push --tags   # 또는 git push origin v1.0.0

실수 2: 잘못된 커밋에 태그

# 잘못된 커밋에 태그를 만든 경우
git tag -d v1.0.0              # 로컬 삭제
git tag -a v1.0.0 올바른커밋해시  # 올바른 커밋에 재생성
git push origin --delete v1.0.0  # 원격에서 삭제
git push origin v1.0.0         # 새 태그 push

실수 3: 버전 번호 일관성 없음

# 나쁜 예
v1.0
1.0.0
V1.0.0
version-1

# 좋은 예: 항상 같은 형식
v1.0.0
v1.1.0
v2.0.0

팀에서 버전 형식을 미리 정해두세요.

심화 학습

태그에 서명 추가 (GPG)
# 서명된 annotated 태그 (GPG 설정 필요)
git tag -s v1.0.0 -m "서명된 릴리스"

# 서명 확인
git tag -v v1.0.0

오픈소스 프로젝트에서 배포 파일의 무결성을 보장하기 위해 사용합니다.

릴리스 파일 첨부
# 빌드 결과물을 릴리스에 첨부
npm run build   # dist/ 생성

gh release create v1.0.0 \
  ./dist/app-v1.0.0-linux.tar.gz \
  ./dist/app-v1.0.0-macos.tar.gz \
  --title "v1.0.0" \
  --notes "릴리스 노트..."
CHANGELOG.md 유지하기
# CHANGELOG

## [2.0.0] - 2024-01-15

### Added

- 새 기능 설명

### Changed

- 변경된 기능 설명 (하위 호환 불가)

### Fixed

- 수정된 버그

## [1.1.0] - 2024-01-01

### Added

- 또 다른 기능

keepachangelog.com 형식을 따르는 것이 좋습니다.

  1. 저장소를 만들고 커밋 3개를 생성하세요.
  2. 첫 번째 커밋에 git tag -a v0.1.0 -m "알파 버전", 두 번째 커밋에 v1.0.0, 세 번째 커밋에 v1.1.0 태그를 만드세요.
  3. git taggit log --oneline --decorate로 태그를 확인하세요.
  4. git show v1.0.0으로 태그 상세 정보를 확인하세요.
  5. git checkout v1.0.0으로 특정 버전으로 이동하고 git checkout main으로 돌아오세요.

Q1. Semantic Versioning에서 1.2.3 → 1.3.0으로 버전이 올라갔을 때 의미하는 것은?

  • A) 버그만 수정했다
  • B) 하위 호환을 깨는 큰 변경이 있었다
  • C) 하위 호환을 유지하면서 새 기능을 추가했다
  • D) 보안 패치만 적용했다