11. 배포 관리와 태그(Tag)

🎯 이 장의 목표
  • 태그가 무엇이고 왜 배포(릴리스)에 쓰이는지 이해한다
  • annotated 태그와 lightweight 태그의 차이를 안다
  • 특정 커밋에 태그를 달고, 태그로 체크아웃한다
  • 태그를 원격에 공유·동기화하고, 수정·삭제한다

11.1 태그란 무엇인가?

브랜치가 "계속 움직이는 이름표"였다면, 태그(Tag)는 특정 커밋에 박아두는 "움직이지 않는 이정표"입니다. 주로 "이 커밋이 버전 1.0.0이다"처럼 의미 있는 시점을 표시할 때 사용합니다.

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C" tag: "v1.0.0"
    commit id: "D"
    commit id: "E" tag: "v1.1.0"
    commit id: "F" tag: "v2.0.0"
브랜치(main)는 커밋이 쌓일 때마다 계속 움직이지만, 태그는 한번 박으면 그 커밋에 영원히 고정됩니다.
  • 브랜치는 커밋이 쌓일 때마다 앞으로 이동하지만, 태그는 한번 달면 그 커밋에 영원히 고정됩니다.
  • "릴리스(배포)한 버전"을 기록하는 데 가장 많이 쓰입니다.
🏷️ 책갈피 비유
책(저장소)을 읽다가 중요한 페이지(커밋)에 책갈피(태그)를 꽂아둡니다. 책을 더 읽어 나가도(커밋 추가) 책갈피는 그 페이지에 그대로 있습니다. 나중에 "1장 끝부분"을 바로 펼칠 수 있죠.

11.2 버전 태그와 시맨틱 버저닝

태그 이름으로 가장 널리 쓰이는 규칙이 시맨틱 버저닝(Semantic Versioning)입니다.

CODE
v  MAJOR . MINOR . PATCH
v   2    .   1   .   3
자리이름언제 올리나
MAJOR주 버전기존과 호환 안 되는 큰 변경
MINOR부 버전호환되는 기능 추가
PATCH수 버전호환되는 버그 수정

예: v1.0.0 → 버그 수정 → v1.0.1 → 기능 추가 → v1.1.0 → 대규모 개편 → v2.0.0

💡 팁
관례상 태그 앞에 v를 붙입니다(v1.0.0). GitHub의 Releases 기능도 이 태그를 기준으로 동작합니다.

11.3 태그의 두 종류

Git의 태그는 두 가지입니다. 실무에서는 annotated 태그를 권장합니다.

Annotated (주석 태그)Lightweight (가벼운 태그)
저장 방식별도 객체로 저장커밋을 가리키는 이름표만
작성자/날짜기록됨없음
메시지있음없음
서명(GPG)가능불가
용도공식 릴리스(권장)임시·개인용 북마크
💡 팁
GPG(GNU Privacy Guard)는 데이터에 디지털 "도장"을 찍어, 그것이 정말 내가 만들었고 위조되지 않았음을 누구나 검증할 수 있게 해주는 무료 보안 도구입니다. 커밋이나 태그에 GPG 서명을 하면 GitHub에 "Verified(검증됨)" 배지가 붙어, 다른 사람이 사칭하지 않았음을 증명할 수 있습니다.

11.4 Annotated 태그 만들기 (권장)

-a(annotated)와 -m(메시지) 옵션을 씁니다.

BASH
# 현재 커밋(HEAD)에 annotated 태그 달기
git tag -a v1.0.0 -m "First stable release"

작성자, 날짜, 메시지가 함께 기록됩니다. 상세 정보를 확인해봅시다.

BASH
git show v1.0.0
CODE
tag v1.0.0
Tagger: Hong Gildong <hong@example.com>
Date:   Mon Jun 23 15:00:00 2026 +0900

First stable release

commit a1b2c3d...
    (해당 커밋 내용)

11.5 Lightweight 태그 만들기

아무 옵션 없이 이름만 주면 가벼운 태그가 됩니다.

BASH
git tag v1.0.0-beta

메시지·작성자 정보가 없어, 잠깐 표시해두는 개인 북마크 용도로 적합합니다.

11.6 태그 목록 보기

BASH
# 모든 태그 보기
git tag

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

# 태그와 메시지를 함께 보기
git tag -n
CODE
v1.0.0
v1.1.0
v2.0.0

11.7 특정(과거) 커밋에 태그 달기

깜빡하고 태그를 안 달았던 과거 커밋에도 나중에 태그를 달 수 있습니다. 커밋 해시를 지정하면 됩니다.

BASH
# 과거 커밋 해시 확인
git log --oneline
CODE
a1b2c3d (HEAD) Latest work
9f8e7d6 Release-worthy commit    ← 여기에 태그를 달고 싶다
c3d4e5f Initial commit
BASH
# 해당 커밋에 태그 달기
git tag -a v0.9.0 9f8e7d6 -m "Pre-release version"

소스트리에서는 원하는 커밋을 우클릭 → "Tag..."를 선택해 이름과 메시지를 입력하면 됩니다. 어느 커밋에 다는지 그래프로 확인하며 작업할 수 있어 직관적입니다.

11.8 태그 중복 방지

이미 있는 이름으로 태그를 또 달려고 하면 Git이 막아줍니다.

BASH
git tag v1.0.0
CODE
fatal: tag 'v1.0.0' already exists

정말 덮어써야 한다면 -f(force)를 쓰지만, 이미 공유한 태그에는 위험하므로 권장하지 않습니다.

BASH
git tag -f -a v1.0.0 -m "..."     # 강제 재생성 (주의)

11.9 태그 삭제

BASH
# 로컬 태그 삭제
git tag -d v1.0.0
CODE
Deleted tag 'v1.0.0' (was a1b2c3d)

원격 태그 삭제는 11.12에서 다룹니다(로컬 삭제만으로는 원격에서 사라지지 않습니다).

11.10 태그로 체크아웃하기

태그가 가리키는 시점의 코드를 그대로 보고 싶을 때:

BASH
git checkout v1.0.0
# 또는
git switch --detach v1.0.0

태그는 고정된 지점이라, 체크아웃하면 detached HEAD 상태(6장)가 됩니다. 그 버전을 기반으로 작업을 이어가려면 새 브랜치를 만드세요.

BASH
git switch -c hotfix-v1.0.0 v1.0.0     # v1.0.0 시점에서 새 브랜치 시작
💡 팁
이는 "출시된 버전에서 발견된 버그를 그 버전 기준으로 긴급 수정(hotfix)"할 때 흔히 쓰는 패턴입니다.

11.11 태그 공유(동기화) — 원격에 올리기

⚠️ 흔한 실수
중요: git push태그를 자동으로 올리지 않습니다. 태그는 따로 push해야 합니다. 이걸 모르면 "분명 태그를 달았는데 GitHub에 안 보인다"고 당황하게 됩니다.

특정 태그 하나만 올리기

BASH
git push origin v1.0.0

모든 로컬 태그를 한 번에 올리기

BASH
git push origin --tags

커밋과 태그를 함께 올리기

BASH
git push --follow-tags        # annotated 태그만 커밋과 함께 push

GitHub에 태그를 올리면 Releases 페이지에서 그 태그를 기반으로 릴리스 노트와 다운로드 파일을 만들 수 있습니다. 이것이 오픈소스 배포의 표준 방식입니다.

원격 태그 가져오기

BASH
git fetch --tags              # 원격의 모든 태그 받기

11.12 원격 태그 수정과 삭제

원격 태그 삭제

로컬에서 지워도 원격엔 남아있으므로, 원격에서도 따로 지워야 합니다.

BASH
# 로컬 삭제
git tag -d v1.0.0

# 원격 삭제
git push origin --delete v1.0.0
# 또는
git push origin :refs/tags/v1.0.0

원격 태그 수정(이동)

이미 공유한 태그를 다른 커밋으로 옮기는 것은 권장되지 않습니다. 동료가 받은 태그와 어긋나기 때문입니다(리베이스 황금률과 같은 이유). 부득이하면 로컬에서 강제 재생성 후 강제 push 합니다.

BASH
git tag -f v1.0.0 <새커밋>
git push origin -f v1.0.0       # 주의: 협업 시 사전 합의 필요
💡 팁
원칙: 태그는 "한번 배포하면 고정"이 이상적입니다. 잘못 배포했다면 옮기기보다 새 버전 태그(v1.0.1)를 새로 발행하는 편이 안전하고 명확합니다.

11.13 태그 생성 원리 (참고)

annotated 태그는 내부적으로 .git/refs/tags/에 저장되며, 별도의 태그 객체(작성자·날짜·메시지·가리키는 커밋)를 만듭니다. lightweight 태그는 그냥 커밋 해시를 담은 파일 하나일 뿐입니다. 깊이 알 필요는 없지만, "annotated는 진짜 객체, lightweight는 단순 포인터"라는 점만 기억하면 충분합니다.

11.14 실습: 릴리스 전체 흐름

BASH
# 1. 안정 버전을 main에 병합 완료한 상태에서
git switch main

# 2. annotated 태그 발행
git tag -a v1.0.0 -m "First public release"

# 3. 태그 확인
git tag
git show v1.0.0

# 4. 원격(GitHub)에 태그 올리기
git push origin v1.0.0

# 5. (GitHub) Releases 페이지에서 릴리스 노트 작성

# === 이후 버그 발견 → 긴급 수정 ===
git switch -c hotfix v1.0.0     # 출시 버전 기준 브랜치
# ... 버그 수정, 커밋 ...
git switch main
git merge hotfix
git tag -a v1.0.1 -m "Fix critical bug"
git push origin v1.0.1

11.15 이 장에서 배운 것 (요약)

  • 태그 = 특정 커밋에 고정되는 이정표 (브랜치는 움직이고 태그는 고정)
  • 버전 이름은 시맨틱 버저닝(vMAJOR.MINOR.PATCH) 권장
  • Annotated 태그(-a -m, 권장) vs Lightweight 태그(이름만)
  • 과거 커밋에도 해시를 지정해 태그 가능
  • 태그 체크아웃 시 detached HEAD → 이어 작업하려면 새 브랜치
  • git push는 태그를 안 올린다git push origin <태그> 또는 --tags
  • 원격 태그 삭제는 git push origin --delete <태그>
  • 태그는 옮기기보다 새 버전 발행이 안전

✍️ 확인 문제

  1. annotated 태그와 lightweight 태그 중 공식 릴리스에 권장되는 것은?
  2. 태그를 달고 git push만 했는데 GitHub에 안 보입니다. 무엇을 빠뜨렸나요?
  3. 출시된 v1.0.0에서 버그를 그 버전 기준으로 고치려면 어떻게 시작하나요?
축하합니다! 본문 11개 섹션을 모두 마쳤습니다. 마지막으로 전체를 한 장에 정리한 부록을 확인하세요. → 99_부록-치트시트.md