시맨틱 버저닝
버전 번호에 뜻을 담자고 정해 놓은 규칙입니다. 번호를 세 자리로 적고, 자리마다 무엇이 바뀌었을 때 올리는지를 미리 약속해 둡니다. 그래서 번호만 보고도 올려 써도 되는지를 가늠할 수 있습니다.
쉽고 빠른 이해
시맨틱 버저닝은 버전 번호의 자리마다 무엇이 바뀌었는지 뜻을 정해 놓은 규칙입니다.
번호를 메이저·마이너·패치 세 자리로 적고, 1.9.0을 1.10.0으로 올리듯 자리마다
정해진 조건이 맞을 때만 그 자리를 올립니다.
이런 규칙 없이 번호만 매기면 의존성 지옥이라 부르는 상황에 빠지기 쉽습니다. 끌어다 쓰는 패키지가 늘수록, 새 판을 내도 그걸 올려 써도 안전한지 번호만 보고는 판단할 수 없어지기 때문입니다.
자리를 올리는 조건은 이렇습니다.
- 버그만 고쳤으면 패치 자리를 올립니다
- 기존 기능은 그대로 두고 새 기능만 더했으면 마이너 자리를 올립니다
- 기존 기능이 깨지는 변경을 넣었으면 메이저 자리를 올립니다
대가는 다 지키거나 아예 안 지키거나 둘 중 하나라는 것입니다. 규칙을 정확히 지키지 않고 비슷하게만 따르면, 번호가 의존성 관리에서 사실상 쓸모없어집니다.
상세
시맨틱 버저닝은 버전 번호를 MAJOR.MINOR.PATCH 세 자리로 적고 자리마다 올리는 조건을 못
박은 명세입니다. 명세 자신이 요약을 세 줄로 냅니다. 호환되지 않는 API(Application
Programming Interface, 응용 프로그램 인터페이스) 변경, 곧 기존 기능이 깨지는 변경을 하면
메이저를 올립니다. 하위 호환(기존에 되던 것은 그대로 되는 것)되는 방식으로 기능을 더하면
마이너를 올립니다. 하위 호환되는 버그 수정을 하면 패치를 올립니다.
프리릴리스와 빌드 메타데이터를 위한 라벨은 이 세 자리 형식의 확장으로 쓸 수 있습니다.
아래 규칙들은 낱말이 곧 강제력입니다. 명세는 MUST · MUST NOT · REQUIRED · SHALL · SHALL NOT · SHOULD · SHOULD NOT · RECOMMENDED · MAY · OPTIONAL 을 RFC(Request for Comments) 2119 에 적힌 대로 해석하라고 첫머리에 적어 둡니다.
공개 API 선언
시맨틱 버저닝을 쓰는 소프트웨어는 공개 API 를 선언해야 합니다(MUST). 이 API 는 코드 자체로 선언할 수도 있고 문서에만 존재할 수도 있습니다. 어느 쪽이든 정확하고 빠짐없어야 한다고 명세가 권고합니다(SHOULD).
번호의 형태
프리릴리스 라벨(정식 판이 되기 전에 붙이는 불안정 표시로, 아래 프리릴리스 절에서 다룹니다)이
안 붙은 판을 정상 판이라 부릅니다. 정상 판의 버전 번호는 X.Y.Z 꼴이어야
합니다(MUST). X · Y · Z 는 음이 아닌 정수이고, 앞자리에
0 을 붙이면 안 됩니다(MUST NOT). X 가 메이저, Y 가 마이너, Z 가 패치입니다. 각 자리는 수로
증가해야 합니다(MUST). 명세가 든 보기가 1.9.0 -> 1.10.0 -> 1.11.0 입니다.
한 번 낸 판은 그 판의 내용을 고치면 안 됩니다(MUST NOT). 고칠 것이 생기면 새 판으로 내야 합니다(MUST).
메이저 자리가 0 인 0.y.z 는 초기 개발용입니다. 무엇이든 언제든 바뀔 수 있습니다(MAY).
이때 공개 API 는 안정적이라고 보지 않는 것이 명세의 권고입니다(SHOULD NOT). 1.0.0 이 공개
API 를 정합니다. 이 판을 낸 뒤로 번호를 어떻게 올릴지는 그 공개 API 와 그것이 어떻게
바뀌는지에 달려 있습니다.
자리를 올리는 조건
아래 표기에서 대문자는 그 조건이 다루는 자리, 소문자는 어떤 값이든 올 수 있는 자리입니다. 다만 「x > 0」이나 「X > 0」이 따로 붙으면 그건 표기 규칙이 아니라 별도로 얹힌 조건입니다 — 메이저가 0 이 아닐 때만 그 행이 적용된다는 뜻으로, 메이저가 0 인 초기 개발 판은 이 표 밖입니다.
| 자리 | 조건 | 강도 |
|---|---|---|
패치 x.y.Z · x > 0 |
하위 호환되는 버그 수정만 들어갔을 때 | MUST |
마이너 x.Y.z · x > 0 |
공개 API 에 하위 호환되는 새 기능이 들어갔을 때 | MUST |
| 마이너 | 공개 API 의 어떤 기능이 폐기 예정으로 표시됐을 때 | MUST |
| 마이너 | 비공개 코드 안에 상당한 새 기능이나 개선이 들어갔을 때 | MAY |
메이저 X.y.z · X > 0 |
공개 API 에 하위 호환되지 않는 변경이 들어갔을 때 | MUST |
여기서 버그 수정은 잘못된 동작을 고치는 내부 변경으로 정의됩니다. 마이너 판에는 패치 수준의 변경이 함께 들어갈 수 있고, 메이저 판에는 마이너와 패치 수준의 변경이 함께 들어갈 수 있습니다.
자리를 올리면 아랫자리는 0 으로 되돌려야 합니다(MUST). 마이너를 올리면 패치가 0 이 됩니다. 메이저를 올리면 패치와 마이너가 모두 0 이 됩니다.
프리릴리스와 빌드 메타데이터
전체 버전 문자열에서 두 라벨이 자리 잡는 순서는 이렇습니다. 프리릴리스는 패치 자리 바로 뒤, 빌드 메타데이터는 패치 자리나 프리릴리스 자리 바로 뒤에 옵니다. 둘 다 선택이라 없어도 됩니다.
block-beta columns 5 core1["정상 판 · MAJOR.MINOR.PATCH"] hy["-"] pre["프리릴리스 · 선택"] plus1["+"] build1["빌드 메타데이터 · 선택"] core2["정상 판 · MAJOR.MINOR.PATCH"] plus2["+"] build2["빌드 메타데이터 · 선택"] space:2
첫째 줄은 프리릴리스와 빌드 메타데이터를 함께 단 자리 배치이고, 둘째 줄은 프리릴리스 없이 빌드 메타데이터만 패치 자리 바로 뒤에 단 자리 배치입니다. 하이픈은 프리릴리스가 붙을 때만 나옵니다.
프리릴리스 버전은 패치 자리 바로 뒤에 하이픈과 점으로 나뉜 식별자들을 이어 붙여 나타낼 수
있습니다(MAY). 식별자는 ASCII(American Standard Code for Information Interchange) 영숫자와
하이픈 [0-9A-Za-z-] 만으로 이루어져야 합니다(MUST). 식별자가 비어 있으면 안 됩니다
(MUST NOT). 숫자로 된 식별자는 앞자리 0 을 포함하면 안 됩니다(MUST NOT). 프리릴리스 버전은
짝이 되는 정상 판보다 우선순위(버전을 줄 세울 때 서로를 어떻게 견주는지를 정하는 순서,
아래에서 다룹니다)가 낮습니다. 프리릴리스가 붙었다는 것은 그 판이 불안정하고
짝이 되는 정상 판이 뜻하는 호환성 요구를 만족하지 못할 수도 있다는 표시입니다.
빌드 메타데이터는 패치나 프리릴리스 자리 바로 뒤에 더하기 기호와 점으로 나뉜 식별자들을
이어 붙여 나타낼 수 있습니다(MAY). 식별자는 ASCII 영숫자와 하이픈 [0-9A-Za-z-] 만으로
이루어져야 합니다(MUST). 식별자가 비어 있으면 안 됩니다(MUST NOT). 명세가 프리릴리스에
건 세 번째 조항인 「숫자로 된 식별자는 앞자리 0 을 포함하면 안 된다」(MUST NOT)는 빌드
메타데이터 항에 없습니다. 그래서 두 항의 식별자 규칙은 앞의 두 조항까지만 같습니다.
다만 빌드 메타데이터는 우선순위를 정할 때 무시해야 합니다(MUST). 그래서 빌드 메타데이터만
다른 두 버전은 우선순위가 같습니다.
우선순위
우선순위는 버전을 줄 세울 때 서로를 어떻게 견주는지를 가리킵니다. 우선순위는 버전을 메이저 · 마이너 · 패치 · 프리릴리스 식별자로 그 순서대로 갈라서 계산해야 합니다(MUST). 빌드 메타데이터는 이 계산에 들어가지 않습니다.
왼쪽부터 견주다가 처음 달라지는 자리가 순서를 정합니다. 메이저 · 마이너 · 패치는 언제나 수로 견줍니다. 셋이 같으면 프리릴리스가 붙은 쪽이 정상 판보다 낮습니다. 프리릴리스가 둘 다 없으면 그 둘은 우선순위가 같습니다. 이 단계까지를 흐름도로 그리면 이렇습니다.
flowchart TD
A[메이저가 다른가] -->|다르다| Z1[큰 쪽이 우선]
A -->|같다| B[마이너가 다른가]
B -->|다르다| Z1
B -->|같다| C[패치가 다른가]
C -->|다르다| Z1
C -->|같다| D[프리릴리스가 한쪽에만 있나]
D -->|한쪽에만 있다| Z2[프리릴리스 없는 쪽이 우선]
D -->|둘 다 없다| Z5[둘 다 정상 판 · 우선순위 같음]
D -->|둘 다 있다| E[다음 단계]
메이저 · 마이너 · 패치가 같은 두 프리릴리스끼리는 점으로 나뉜 식별자를 왼쪽부터 하나씩 견주다가 차이가 나는 자리에서 판정해야 합니다(MUST). 숫자로만 된 식별자는 수로 견줍니다. 글자나 하이픈이 든 식별자는 ASCII 정렬 순서를 따라 사전식으로 견줍니다. 숫자 식별자는 숫자가 아닌 식별자보다 언제나 낮습니다. 명세는 이 점으로 나뉜 식별자 하나하나를 프리릴리스 필드라고도 부릅니다. 앞 식별자가 모두 같다면 필드 수가 더 많은 쪽이 높습니다. 위 흐름도의 「다음 단계」를 이어 그리면 이렇습니다.
flowchart TD
E[다음 식별자를 왼쪽부터 견준다] --> F{둘 다 숫자인가}
F -->|그렇다| G[수로 견준다]
F -->|아니다| H[ASCII 사전식으로 견준다. 숫자 쪽이 항상 낮다]
G --> I{차이가 있나}
H --> I
I -->|있다| Z3[거기서 판정]
I -->|없고 식별자가 남았다| E
I -->|없고 한쪽 식별자가 다 떨어졌다| Z4[필드 수가 많은 쪽이 우선]
출처 문서
정본은 semver.org 가 내는 「Semantic Versioning 2.0.0」 입니다. RFC 처럼 별도 문서 번호를
받는 표준이 아니라 판 번호 자체가 문서를 가리킵니다. 인용 주소는 판이 박힌
https://semver.org/spec/v2.0.0.html 입니다.
| 항목 | 값 |
|---|---|
| 제목 | Semantic Versioning 2.0.0 |
| 정본 주소 | https://semver.org/spec/v2.0.0.html |
| 저자 | Tom Preston-Werner |
| 라이선스 | Creative Commons CC BY 3.0 |
| 피드백 | GitHub 이슈 |
판
사이트가 판 전환 목록으로 2.0.0 · 2.0.0-rc.2 · 2.0.0-rc.1 · 1.0.0 · 1.0.0-beta
다섯을 늘어놓습니다. 지금 정본은 2.0.0 이고 나머지 넷은 그 앞의 판입니다. 이전 판이
다음 판으로 이어지는 순서를 그리면 이렇습니다.
flowchart TD
A["1.0.0-beta"] --> B["1.0.0"] --> C["2.0.0-rc.1"] --> D["2.0.0-rc.2"] --> E["2.0.0 · 정본"]
각 판의 주소는 /spec/v{판}.html 꼴을 따릅니다. 1.0.0 은
https://semver.org/spec/v1.0.0.html 에 지금도 살아 있습니다. 판이 안 붙은
https://semver.org/ 는 그 시점의 판을 보여주는 자리라, 인용은 판이 박힌 주소로 답니다.
요구 강도
명세는 요구 강도 낱말을 스스로 정의하지 않고 RFC 2119 에 넘깁니다. 「Semantic Versioning Specification (SemVer)」 표제 바로 아래 문단이 그 위임입니다. 그러니 MUST 와 SHOULD 가 갈리는 자리를 읽으려면 RFC 2119 를 같이 봐야 합니다.
| 낱말 | RFC 2119 의 정의 |
|---|---|
| MUST | REQUIRED · SHALL 과 같은 뜻입니다. 그 정의가 명세의 절대적 요구라는 뜻입니다 |
| SHOULD | RECOMMENDED 와 같은 뜻입니다. 특정 상황에서 그 항목을 무시할 타당한 이유가 있을 수 있지만, 다른 길을 고르기 전에 그 함의를 전부 이해하고 신중히 따져야 한다는 뜻입니다 |
인용하는 자리
이 명세에는 절 번호가 없습니다. 번호 매긴 항목 열한 개로 되어 있어서 인용은 「명세 8항」 처럼 항 번호로 답니다. 위 상세에서 자리를 올리는 조건은 6항부터 8항, 프리릴리스는 9항, 빌드 메타데이터는 10항, 우선순위는 11항입니다.
구현에 맡긴 자리
공개 API 를 어떻게 선언하는지는 명세가 정하지 않습니다. 코드 자체로 선언하든 문서로만 두든 상관없다고 적습니다. 정확하고 빠짐없어야 한다는 권고만 걸어 둡니다.
예시
프리릴리스 값
1.0.0-alpha
1.0.0-alpha.1
1.0.0-0.3.7
1.0.0-x.7.z.92
1.0.0-x-y-z.--
명세 9항이 든 값들입니다. 하이픈 뒤가 프리릴리스 식별자입니다. 점으로 여럿을 이을 수 있습니다. 마지막 줄처럼 하이픈만으로 된 식별자도 규칙에 맞습니다.
빌드 메타데이터가 붙은 값
1.0.0-alpha+001
1.0.0+20130313144700
1.0.0-beta+exp.sha.5114f85
1.0.0+21AF26D3----117B344092BD
명세 10항이 든 값들입니다. 더하기 뒤가 빌드 메타데이터입니다. 첫 줄은 프리릴리스와 빌드
메타데이터를 함께 달았습니다. 그 001 은 앞자리가 0 입니다. 빌드 메타데이터라서 규칙에
맞습니다. 프리릴리스 식별자였다면 앞자리 0 금지에 걸렸을 값입니다. 둘째 줄
1.0.0+20130313144700 과 넷째 줄 1.0.0+21AF26D3----117B344092BD 는 우선순위가 같습니다.
정상 판이 둘 다 1.0.0 이고 빌드 메타데이터만 다르기 때문입니다.
우선순위 사슬
1.0.0 < 2.0.0 < 2.1.0 < 2.1.1
1.0.0-alpha < 1.0.0
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
명세 11항이 든 사슬입니다. 마지막 줄에서 1.0.0-beta.2 < 1.0.0-beta.11 이 눈에 걸립니다.
숫자로만 된 식별자는 수로 견주므로 11 이 2 보다 뒤에 섭니다. 사전식으로 견줬다면 11 이
2 앞에 왔을 것입니다.
v1.2.3
v1.2.3 은 시맨틱 버전이 아닙니다. 명세의 FAQ(Frequently Asked Questions, 자주 묻는 질문)가
그렇게 못 박습니다. 다만 시맨틱 버전 앞에 v 를
붙이는 것은 영어권에서 그것이 버전 번호임을 나타내는 흔한 방식입니다. version 을 v 로 줄이는
표기는 버전 관리에서 자주 보입니다. 명세가 든 보기가 이것입니다.
git tag v1.2.3 -m "Release version 1.2.3"
이 경우 v1.2.3 은 태그 이름이고 시맨틱 버전은 1.2.3 입니다.
npm 의 버전 범위
npm 공식 문서는 package.json 의 version 필드가 node-semver 로 파싱될 수 있어야 한다고
적습니다. node-semver 는 npm 에 의존성으로 함께 딸려 옵니다. 그리고 의존성에 적는 범위
표기를 이렇게 정합니다.
| 표기 | 뜻 |
|---|---|
version |
그 버전과 정확히 일치해야 합니다 |
>version |
그 버전보다 커야 합니다 |
>=version · <version · <=version |
각각 그 이상 · 미만 · 이하입니다 |
~version |
그 버전과 대략 동등한 범위입니다 |
^version |
그 버전과 호환되는 범위입니다 |
1.2.x |
1.2.0 · 1.2.1 등은 되고 1.3.0 은 안 됩니다 |
npm 자신도 ~·^ 이 정확히 어디까지 여는지는 이 표에 안 적고 「자세한 건 semver 를
보라」고 넘깁니다. 셋째 줄 1.2.x 만 경계를 구체적인 값으로 보여줍니다.
배경
명세의 Introduction 이 의존성 지옥이라는 이름부터 답니다. 시스템이 커지고 끌어다 쓰는 패키지가 늘수록 언젠가 이 구덩이에 빠지기 쉬워진다고 적습니다. 의존성이 많은 시스템에서는 새 패키지 판을 내는 일이 금세 악몽이 될 수 있습니다. 그 악몽이 갈라지는 두 방향에 각각 이름이 붙어 있습니다.
| 이름 | 언제 | 무엇이 막히나 |
|---|---|---|
| version lock | 의존 명세가 너무 빡빡할 때 | 딸린 패키지를 전부 새로 내지 않고는 그 패키지를 올릴 수 없습니다 |
| version promiscuity | 의존 명세가 너무 헐렁할 때 | 합리적인 범위를 넘어 앞으로 나올 판까지 호환된다고 가정하게 됩니다 |
version lock 이나 version promiscuity 때문에 프로젝트를 쉽고 안전하게 앞으로 옮기지 못하는 상태가 의존성 지옥입니다. 그 해법으로 명세는 버전 번호를 어떻게 매기고 올릴지 지시하는 간단한 규칙과 요구사항 한 벌을 제안합니다. 이 규칙들은 닫힌 소스와 오픈소스 양쪽에서 이미 널리 쓰이던 관행에 바탕을 두되 거기에 반드시 갇히지는 않는다고 적습니다. 먼저 공개 API 를 선언하고, 거기에 생긴 변화를 번호의 특정 자리를 올려 알린다 — 이 체계를 명세가 시맨틱 버저닝이라 부릅니다.
명세는 「Why Use Semantic Versioning?」 절에서 보기를 하나 듭니다. Firetruck 이라는 라이브러리가 시맨틱 버저닝을 따르는 Ladder 라는 패키지를 필요로 합니다. Firetruck 을 만들 당시 Ladder 는 3.1.0 이었고 Firetruck 은 3.1.0 에서 처음 들어온 기능을 씁니다. 그러면 Ladder 의존성을 3.1.0 이상 4.0.0 미만으로 안전하게 적을 수 있습니다. 뒤에 Ladder 3.1.1 과 3.2.0 이 나오면 그것들이 기존에 딸린 소프트웨어와 호환되리라는 것을 알고 패키지 관리 시스템에 내보낼 수 있습니다. 명세는 어떤 형식 규격도 지키지 않으면 버전 번호가 의존성 관리에서 사실상 쓸모없다고 적습니다. 비슷하게 하는 것으로는 충분하지 않다는 것입니다.
관련 항목
이 규칙을 이루는 개념
공개 API · 하위 호환성 · 폐기 예정 · 프리릴리스 · 빌드 메타데이터 · 우선순위
이 규칙을 정의하는 표준·문서
이 규칙이 속하는 상위 분류
이 규칙이 변화를 재는 대상
이 규칙이 실제로 맞물리는 생태계
npm · package.json · node-semver · 패키지 관리 시스템 · 의존성 지옥 · Git 태그
다른 이름: SemVer · Semantic Versioning