동작하는 코드
예제 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.0 → 1.0.1 |
| 새 기능 추가 | 검색 기능 추가 | 1.0.1 → 1.1.0 |
| 주요 변경 | API 구조 변경 | 1.1.0 → 2.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 형식을 따르는 것이 좋습니다.
- 저장소를 만들고 커밋 3개를 생성하세요.
- 첫 번째 커밋에
git tag -a v0.1.0 -m "알파 버전", 두 번째 커밋에v1.0.0, 세 번째 커밋에v1.1.0태그를 만드세요. git tag와git log --oneline --decorate로 태그를 확인하세요.git show v1.0.0으로 태그 상세 정보를 확인하세요.git checkout v1.0.0으로 특정 버전으로 이동하고git checkout main으로 돌아오세요.
Q1. Semantic Versioning에서 1.2.3 → 1.3.0으로 버전이 올라갔을 때 의미하는 것은?
- A) 버그만 수정했다
- B) 하위 호환을 깨는 큰 변경이 있었다
- C) 하위 호환을 유지하면서 새 기능을 추가했다
- D) 보안 패치만 적용했다