최근 수정 시각 : 2026-04-15 20:03:17

시맨틱 버전

SemVer에서 넘어옴

파일:관련 문서 아이콘.svg   관련 문서: 버전
#!if 문서명2 != null
, [[]]
#!if 문서명3 != null
, [[]]
#!if 문서명4 != null
, [[]]
#!if 문서명5 != null
, [[]]
#!if 문서명6 != null
, [[]]
<colcolor=#fff,#fff><bgcolor=#000,#000> Semantic Versioning
SemVer
파일:SemVer.png
<colbgcolor=#000,#000> 종류 버전 형식 표준
최초 개발자 톰 프레스턴 워너
출시 2011년 6월 (1.0.0-beta)
2011년 9월 (1.0.0)#@
안정 버전 2.0.0(2013년 6월 18일)
2.0.0-ko2(2013년 6월 18일)
라이선스 CC BY 3.0문서#
링크 파일:홈페이지 아이콘.svg 파일:GitHub 아이콘.svg파일:GitHub 아이콘 화이트.svg
1. 개요2. 역사3. 명세
3.1. 구성3.2. 버전 증가3.3. 태그 표준3.4. 문법
4. 버전 선택자5. 활용
5.1. 구현체
6. 주의
6.1. 메이저 버전 올리기를 기피하는 인식
7. 기타8. 관련 문서9. 외부 링크

1. 개요

Semantic Versioning, SemVer

종속 관계를 형성하는 소프트웨어버전자동화된 처리를 위해 체계화한 표준 중 하나.

이름과 같이 소프트웨어의 버전을 명명함에 있어 엄격한 의미(semantics)를 부여하는 것이 목적으로, 덕분에 어떤 버전이 더 최신인지, 어떤 버전으로 업그레이드 해야 하는지, 어떤 버전이 서로 호환되는지 등의 유용한 의미적 정보를 버전명만 보고 기계적으로 유추해 낼 수 있다. 이러한 특성 덕분에 언어, 기술 무관하게 수많은 패키지 저장소, 패키지 관리자, 프로젝트 관리 도구 등에 도입되며 현재 소프트웨어 개발에서 가장 널리 쓰이는 버전 체계 중 하나이다.

현업에서는 대부분 원어 그대로 시맨틱 버전, semver라고 불리며, 경우에 따라 의미적 버전, 유의적 버전 등으로도 불린다.

'두드러진'이라는 의미의 고전 그리스어 σημαντικός와 '변화'를 뜻하는 라틴어 versiō를 합성한 말이다.

2. 역사

2011년 GitHub의 공동창업자이기도 한 톰 프레스턴 워너가 최초 표준을 만들었다.[1][2] 2011년 6월 1.0.0-beta 버전을 공개했고, 당해 9월 1.0.0을 발표했다.

2013년 6월 18일, semver 2.0.0 스펙이 발표되었다.##

3. 명세

3.1. 구성

[math(\large \underbrace{\tt \overset{\overset{\textsf{Major}}{\uparrow}}{2}.\overset{\overset{\textsf{Minor}}{\uparrow}}{13}.\overset{\overset{\textsf{Patch}}{\uparrow}}{1}}_{{\textsf{Version Core}}}\!\overset{\sf Optional}{\overleftrightarrow{\text{\tt -}\underbrace{\tt alpha.1B}_{{\textsf{Pre-release} }}\text{\tt +}\underbrace{\tt sha\text{\tt -}1a2b3c4}_{{\textsf{Build}}}}})]

크게 <메이저 버전>.<마이너 버전>.<패치 버전>의 세 부분으로 이루어진다. 각각의 영역에는 음이 아닌 정수가 들어가며, . 문자로 구분한다.
  • 메이저 버전: 하위 호환성을 보장하지 않는 API 변경사항(breaking change)를 하나라도 포함한 버전에 해당한다. 가령 기존 API를 삭제하거나, 완전히 새로운 API로 통합하는 경우 등. 이러한 경우, 메이저 버전을 올려야 한다. 유일한 예외로, 메이저 버전이 0인 동안은 어떤 불안정한 API 변경사항이 발생하더라도 꼭 메이저 버전을 올리지 않아도 된다.[3] 이 시기를 개발 단계라고 하며, 최초의 메이저 버전(1.0.0)을 공개한 이후로는 일반적인 메이저 버전 원칙을 따른다.[4]
  • 마이너 버전: 하위 호환성이 있는 API 변경사항을 의미한다. 가령 기존 기능을 전부 남기고 새로운 기능을 추가하는 경우, 기존 API를 deprecate하는 등의 경우,[5][6]@# 신규 버전을 기존 소프트웨어에서 그대로 사용할 수 있으므로 업그레이드를 해도 안전함이 보장된다. 이런 경우 마이너 버전을 올린다.
  • 패치 버전: 단순 버그를 수정하는 경우나 리팩토링[7] 등, 표면상의(public surface) API 변경사항이 없으면서 업그레이드가 권장되는 경우 등이 해당한다.
  • 사전 릴리즈: 각 버전을 정식으로 배포하기 전에 공개되는 버전으로, 일반적인 버전 뒤에 -와 식별자를 붙여 나타낼 수 있다. 가령 1.2.3-alpha.7의 경우 -alpha.7이 사전 릴리즈 번호이다. 공백을 쓸 수 없기 때문에 .으로 각 식별자를 구분하며, 1.1.1-a.12.b.34와 같이 .으로 여러 식별자를 원하는 만큼 연결할 수 있다. 이때 순서는 각각의 식별자를 앞에서부터 순서대로 비교하며, 각각의 식별자는 숫자로 이루어진 경우 일반적인 정수 순서를, 문자열일 경우 일반적인 사전식 순서에 따른다. 가령 1.0.0-pre.31.0.0-pre.0보다 신규 버전이다. 비슷하게 1.0.0-rc.11.0.0-beta.9와 비교했을 때 문자 rb보다 크므로 신규 버전이다. 숫자와 문자열이 같은 위치에 있는 경우 항상 문자열이 더 큰 것으로 간주한다. 가령 1.0.0-alpha.2.tp171.0.0-alpha.2.338보다 신규 버전이다.
  • 빌드 번호: 버전 뒤에 + 기호와 함께 붙은 문자열. 가령 1.2.3+sha.8a4c9fd의 경우 +sha.8a4c9fd 부분이, 4.11.4+20251008.0의 경우 +20251008.0 부분이 빌드 번호이다. 단순히 메타데이터를 넣기 위한 용도이기 때문에 버전 선택 등 과정에서 완전히 무시되며, 빌드 번호를 표시하는 것은 선택 사항이다. 이진 실행 파일인 경우 명령어 집합도 이곳에 명시하기도 한다.

버전의 대소를 비교할 때는 앞에서부터 순서대로 비교한다. 즉, 메이저 버전을 먼저 비교하고 메이저 버전이 같다면 마이너 버전을 비교한다. 마이너 버전까지 같다면 패치 버전을 비교하며, 셋 모두 같다면 사전 릴리즈 번호를 비교한다. 빌드 번호는 순서에 아무 영향을 미치지 않는다.

가령 1.6.123.0.0의 경우 3.0.0이 메이저 버전이 높으므로 더 신규 버전이고, 2.5.52.5.1의 경우 메이저, 마이너 번호가 각각 같고 2.5.5의 패치 버전이 더 높으므로 2.5.5가 더 신규 버전이다. 사전 릴리즈는 항상 일반 버전보다 이전 버전이며, 사전 릴리즈끼리 비교하는 경우 상술한 규칙에 따라 결정한다. 가령 6.9.7(정식 버전)은 6.9.7-alpha.4(사전 릴리즈)보다 높은 버전이며, 1.1.9-rc.11.1.9-rc.5보다 이전 버전이다.

3.2. 버전 증가

기존(latest)의 버전과 비교해 추가적인 변경사항을 포함한 새 버전을 명명하는 행위이다. 프로그래밍에서 주로 bump라고 한다. 가령 마이너 버전을 증가시킨다면 minor bump라고 하는 식.

각각의 버전을 올릴 때는 한 번에 한 버전만 올릴 수 있다. 가령 1.2.3에서 마이너, 패치 버전을 올려 1.3.4가 될 수는 없다. 각각의 버전 요소는 아래의 요소를 포함할 수 있으므로, 만약 여러 종류의 변경사항이 한 번에 담겨있다면 가장 큰 버전을 올리면 된다. 가령 신규 마이너 버전에는 패치 요소가 포함될 수 있고, 신규 메이저 버전에는 패치 요소나 마이너 요소 등이 포함될 수 있다.

한 버전이 증가하면 그 아래의 버전 요소들은 다시 0부터 시작한다. 가령 1.2.3에서 마이너 버전을 올리면 1.3.3이 아닌 1.3.0이 되며, 메이저 버전을 올리면 2.0.0이 된다.

3.3. 태그 표준

SemVerTag

Git 등의 버전 관리 시스템에서 개별 시맨틱 버전에 대응하는 태그 작성 시 사용되는 형식. 기본적인 내용은 일반 semver 문자열 앞에 v접두사로 붙히는 것이다. 가령 1.2.3이 실제 버전이라면 해당 semver와 대응되는 태그는 v1.2.3이 된다. 조금 엄밀하게 보면 코어 스펙으로만 보았을 때 1.2.3만이 올바른 버전이며 v1.2.3은 시맨틱 버전이 아닌, 올바른 SemVerTag에 해당한다.[8]#

2011년 11월 2일 2.0.0 스펙 공개에 앞서 제거되었다.#

다만 SemVer 1.0.0 시절부터 흔히 혼용되어 쓰였다 보니 하위 호환성을 위해 v를 붙혀도 버전으로 인식되거나 파서에서 지원하기도 하는 편.

3.4. 문법

배커스-나우르 표기법으로는 다음과 같이 표현할 수 있다. 직관성과 편의를 위해 일부 EBNF 문법을 차용했다. 대문자로 쓰여진 non-terminal은 편의를 위한 의사 심볼이다.
<semver> ::= <major> "." <minor> "." <patch> ( "-" <pre> )? ( "+" <build> )?
<major> ::= <UNSIGNED INT>
<minor> ::= <UNSIGNED INT>
<patch> ::= <UNSIGNED INT>
<pre> ::= <DOT SEPARATED TEXT>
<build> ::= <DOT SEPARATED TEXT>

위 BNF를 보면 알겠지만 정규 문법 형태이기에 정규 표현식으로 쉽게 파싱할 수 있다. PCRE 기준, named capture group을 지원하지 않는 언어 기준으로는 다음과 같다.

^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

4. 버전 선택자

범위 선택자, 범위 지정자, 버전 제한자 등으로도 불린다.

현재 semver 2.0.0 스펙에선 표준화되지 않았으나,#205#113 사실상 표준인 몇몇 표준이 존재한다.
<rowcolor=#fff,#fff> 문법 예시 설명
= =1.2.3
1.2.3
해당 버전과 정확히 일치하는 버전을 선택한다. = 기호는 생략할 수 있다.
> >1.2.3해당 버전보다 큰 버전을 선택한다. 예시의 경우, 1.2.5, 2.1.1 등을 선택할 수 있다.
>= >=1.2.3해당 버전보다 크거나 같은 버전을 선택한다.
< <1.2.3해당 버전보다 작은 버전을 선택한다. 예시의 경우, 0.0.0, 1.1.12 등을 선택할 수 있다.
<= <=1.2.3해당 버전보다 작거나 같은 버전을 선택한다.
(공백) >=1.2.3 <4.5.6두 조건문을 AND로 결합한다. 예시의 경우 1.2.3보다 같거나 크고 동시에 4.5.6보다 작은 버전, 이를테면 1.2.3, 3.3.3, 4.5.18 등을 선택할 수 있다.
{{{}}} {{{<1.2.3  >=4.5.6}}}두 조건문을 OR로 결합한다. 예시의 경우 1.2.3보다 작거나 4.5.6보다 크거나 같은 모든 버전, 이를테면 1.2.2, 0.6.2, 4.8.2 등을 선택할 수 있다.
*
x
X
1.2.*
1.2
1.x
1
*
(공문자열)
해당 위치에서의 와일드카드, 즉 앞선 버전 숫자와 일치하면서 해당 위치와 이후에는 어떤 값이든 들어갈 수 있음을 의미한다. 이 때 x, X, * 중 어떤 문자를 써도 똑같다. 가령 1.2.x>=1.2.0 <1.3.0-0, 1.*>=1.0.0 <2.0.0-0, 그리고 *은 모든 버전과 일치한다. 1.*.3과 같이 중간에 위치하는 것은 불가능하며, 1.*과 같이 항상 끝 부분에 위치해야 한다. 이 때문에 문맥상 1.2.* 등을 1.2로 줄일 수 있다. *의 경우 생략하면 공문자열이 된다. 즉, 기본적으로 빈 문자열은 모든 버전과 매칭된다.
- 1.2.3 - 2.3.4
1.2 - 2.3.4
1.2.3 - 2.3
inclusive한 범위 선택자로, 두 버전 사이에 위치하거나 같은 버전을 선택한다. 맨 위 예시의 경우 의미는 >=1.2.3 <=2.3.4와 의미가 같다. 전체 버전의 앞부분 일부만 적는 식으로 하위 버전 범위를 생략할 수 있다. 두번째와 세번째 예시의 경우 각각 >=1.2.0 <=2.3.4, >=1.2.3 <2.4.0-0과 의미가 같다.
~ ~1.2.3
~1.2
~1
기본적으로 *와 같으나, 패치 버전이 명시될 경우 패치 수준의 업데이트도 허용한다. 예시에서 ~1>=1.0.0 <2.0.0-0(1.x), ~1.2>1.2.0 <1.3.0-0(1.2.x)와 동일하나 패치 버전까지 명시된 ~1.2.3>=1.2.3 <1.3.0-0과 같다. 즉, *에서 패치 버전의 하한을 추가한 문법이라고 볼 수 있다.
^ ^1.2.3
^1.2.x
^0.2.3
^0.2.x
^0.0.x
^0.x
0이 등장하는 위치에 따라 동작이 바뀐다. 기본적으로 메이저 버전이 0일 때(개발 중)는 패치 업데이트만 허용하며, 메이저 버전이 1 이상이면 마이너, 패치 업데이트 모두 허용한다. 메이저 버전과 마이너 버전 모두 0인 경우 사전 릴리즈를 명시하지 않는 한 어떤 업데이트도 허용하지 않는다. 예를 들어, ^0.0.3=0.0.3과 동일하다. 다만 메이저(또는 마이너 포함) 버전이 0이더라도 와일드카드가 사용되었다면 이미 모든 마이너(또는 패치) 업데이트가 허용되므로 일반 와일드카드와 같은 동작을 한다. 가령 ^1.2.3, ^1.2.x, ^0.x는 모두 마이너 및 패치 업데이트만 허용하므로 상한의 메이저 버전을 올려 각각 >=1.2.3 <2.0.0-0, >=1.2.0 <2.0.0-0, <1.0.0-0과 같으나, ^0.2.3, ^0.2.x, ^0.0.x 등은 모두 메이저 버전이 0이므로 상한의 마이너 버전만 올려 각각 >=0.2.3 <0.3.0-0, >=0.2.0 <0.3.0-0, <0.1.0-0로 해석된다.

사전 릴리즈 버전의 경우, 일반적으로 범위 지정자에 선택되지 않는다. 가령 >=1.2.31.2.3-alpha를 선택하지 않는다. 사전 릴리즈를 명시적으로 범위에 포함시키고 싶은 경우 >=1.2.3-alpha와 같이 지정자에도 사전 릴리즈 번호를 포함해야 하며, 이 경우 일반 버전(메이저, 마이너, 패치) 번호가 모두 일치하는 사전 릴리즈만 선택된다. 가령 선택자로 >=1.2.3-alpha.2를 사용할 경우 1.2.3-alpha.2, 1.2.3-alpha.5.2, 1.2.3-beta.0은 범위에 포한되지만 1.2.4-alpha.2는 선택되지 않는다.

5. 활용

상당수의 패키지 저장소패키지 관리자 시스템에서 개별 패키지의 버전을 자동으로 처리하기 위해 활용된다. 여기서 활용된다는 말은, 패키지 공개 및 사용 과정에서 SemVer 원칙이 강제된다는 의미이다.

패키지 매니저 및 레지스트리에서 이런 원칙을 강제하는 이유는 다름이 아니라 종속성 지옥(dependency hell)을 막기 위해서인데, 가장 대표적인 문제가 같은 패키지의 서로 다른 두 버전이 한 프로젝트에 동시에 사용되는 경우이다. 일반적으로 하나의 프로젝트는 단 하나의 종속성 버전만 가지는 게 강제되나, 그 종속성 또한 또다른 버전의 종속성을 가질 수 있어서 문제가 발생한다. 이런 식으로 종속성 트리를 짜 내려갔을 때 같은 패키지(가령 log)의 서로 다른 버전([email protected], [email protected])이 사용될 때, 이 각 버전별로 log 패키지를 따로 사용하면 공간이 낭비되는 것은 둘째치고 패키지 경계를 넘어 메모리 주소를 참조하는 코드가 있을 때 서로 다른 주소로 인식되어 오류나 버그가 발생할 수 있다.

이 때 SemVer의 의미론을 활용한다면 해당 공유 종속성(log)의 버전을 하나로 결정하며 문제를 해결할 수 있다. 우선 [email protected][email protected]은 마이너 버전만 차이나므로 [email protected]을 사용하는 종속성에 [email protected]을 대신 집어넣어도 호환성 문제가 발생하지 않음을 알 수 있다. 이제 [email protected]만 빌드하고 log 종속성을 사용하는 모든 곳에 버전 무관하게 [email protected]을 링크하면 전체 종속성 트리에 log는 결과적으로 단 하나만 존재하게 되므로 문제가 해결된다.

이때 위와 같은 모든 판단 과정을 사람의 분석 없이 버전 분석만으로 자동화할 수 있기 때문에, 패키지 매니저에 SemVer를 도입하면 매번 수동으로 deps를 체크할 필요 없이 장점을 누릴 수 있다.

SemVer를 활용하는 대표적인 레지스트리/패키지 매니저로 npm#@, PyPI, crates.io, composer(PHP) 등이 있다.

5.1. 구현체

  • node-semver# - npm에서 사용되는 라이브러리이다. 별도의 CLI로도 사용할 수 있으며, npm CLI에도 내장되어 있다.

6. 주의

종속 관계를 형성하지 않는 도메인에서 SemVer는 전혀 쓸모가 없다. 대표적인 예시로 엔드 유저용 앱, 웹 등이 있다. 이러한 경우 어차피 사용자는 버전을 선택할 기회가 없거나 무의미하기 때문에 다른 스키마를 고려하는 것이 적절하다. 종속 관계라는 것이 꼭 라이브러리여야 한다는 것은 아니나, 해당 소프트웨어가 배포된 환경에서 버전에 따른 소프트웨어의 차이가 올바른 시맨틱을 따르지 않거나, 실행시 고려되지 않는다면 이는 유의미한 종속 관계가 아니라고 할 수 있다.

예를 들어 사용자 애플리케이션에 UI 업데이트가 이루어졌다고 해보자. 이는 breaking change일까? 아니면 minor change일까? 둘 다 아니다. 사용자 인터페이스는 이름과 같이 사용자가 상호작용하는 요소이므로 종속 관계나 완벽한 호환성 등을 따지거나 판단할 수도, 고려할 필요도 없다. 이런 경우에 semver는 대체로 불필요하다.

물론 이런 경우에도 semver를 아예 써서는 안 되는 것은 아니며, 원한다면 얼마든지 사용할 수 있다. 다만 이렇게 할 경우 아래 문단에서 나오는 메이저 버전 기피 문제에 빠지기도 쉬우며, 무엇보다 semver 스펙에서 정의하는 각 버전별 시맨틱을 잃고 자신만의 독자적인 시맨틱을 정의하게 될 수도 있다. 즉, x.y.z라는 형식만 남고 의미적으론 semver와는 관련이 미미한 다른 버전 체계가 될 수도 있다는 것.

이러한 경우 다른 버전 스키마를 고려해 보는 것도 좋은 방안이다. 이미 몇몇 프로젝트들은 아예 SemVer에서 CalVer 같은 다른 버전 형식으로 갈아타기도 했다.

6.1. 메이저 버전 올리기를 기피하는 인식

With software releases at an all-time high, the consensus has never been clearer: Major versions are over.
ZeroVer 소개 웹사이트 @
Somehow we need a less horrible SemVer or a less horrible social contract around SemVer.
ZeroVer에 대한 Y Combinator 유저의 반응. # @
시맨틱 버전 체계에서는 단 하나의 breaking change도 메이저 버전 증가로 이어지기 때문에 무의식적으로, 또는 강박적으로 메이저 버전 올리기를 기피하는 개발자들도 있다. 특수 규칙에 과도하게 의존해 마이너 버전에서 호환성을 깨트리는 것이 가능한 0.x 버전에 평생 머무르며 몇 년이 지나도 1.0.0버전에 도달하지 못하는 경우가 대표적이다. 이런 프로젝트들을 비꼬기 위해 0ver이라는 풍자 사이트도 존재한다. 메이저 버전을 항상 0으로 두고 마이너 버전에 breaking changes를 넣는다면 사실상 SemVer를 쓸 필요가 없지 않느냐는 것.
(...)This pushes us to make significant updates, including a lot of changes at once (and therefore a lot of bugs, I'm not aiming at anyone…), rather than making the application evolve quietly, taking advantage of user feedback, and minimizing the damage.
Arnaud Joubay, I'm done with Semantic Versioning @
1.0.0을 달성한 대형 프로젝트들의 경우도 메이저 버전을 올리는 것을 최대한 미루고 이를 갱신하는 횟수 또한 최대한 줄이고자 몇 년 단위의 마일스톤 동안 머지된 모든 기능을 한 메이저 버전에 한꺼번에 쏟아넣는 경우가 존재한다. 물론 너무 잦은 메이저 갱신은 좋지 않지만, 반대로 한 버전 업그레이드에 너무 많은 변경사항이 존재하면 마이그레이션 비용이 비약적으로 증가하게 되는 문제가 생긴다. 이 경우 메이저 버전의 수를 늘리더라도 변경사항을 짧은 주기로 나누어 담는 것이 다운스트림 입장에서는 더 생산적이다.
Today, in SemVer ecosystems, it’s still a big deal. And I think that’s a problem.
Our collective hesitance to bump the major version of a package is so strong that we sometimes concoct elaborate justifications as to why a breaking change can be included in a minor version release.
제작자 톰 프레스턴 워너, Major Version Numbers are Not Sacred @
시맨틱 버전을 발명한 톰 프레스턴 워너 또한 이런 식의 완벽주의적 사고, 또는 강박적 기피가 비합리적이며, 원래 SemVer의 목적과 다르다고 비판하기도 했다.

7. 기타

첫 버전을 0.1.0으로 해야 하는지 0.0.0으로 해야 하는지는 표준에 별도로 명시되어 있지 않아, 간혹 개발자들 간의 논쟁거리가 되기도 한다.##@#363 여러 오픈소스들을 살펴보면, 0.1.0으로 시작하는 경우도 0.0.0으로 시작하는 경우도 모두 볼 수 있다.

8. 관련 문서

9. 외부 링크


[1] The Semantic Versioning specification was originally authored by Tom Preston-Werner, inventor of Gravatar and cofounder of GitHub. #@[2] Ten years ago, I sat down and wrote the first version of what became the Semantic Versioning (SemVer) spec. #@[3] Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable. #@[4] Version 1.0.0 defines the public API. The way in which the version number is incremented after this release is dependent on this public API and how it changes. #@[5] It MUST be incremented if any public API functionality is marked as deprecated. #@[6] When you deprecate part of your public API, you should do two things: (1) update your documentation to let users know about the change, (2) issue a new minor release with the deprecation in place. Before you completely remove the functionality in a new major release there should be at least one minor release that contains the deprecation so that users can smoothly transition to the new API. #[7] 단, 리팩토링이 반드시 패치 버전 bump를 함의하는지는 논의의 여지가 있다. #146[8] No, “v1.2.3” is not a semantic version. However, prefixing a semantic version with a “v” is a common way (in English) to indicate it is a version number. #@