시맨틱 버저닝
1. 개요
1. 개요
시맨틱 버저닝은 소프트웨어 버전 번호를 부여하고 증가시키기 위한 일련의 규칙 및 요구사항이다. 이 체계는 소프트웨어 개발 과정에서 버전 간의 의미 있는 차이를 명확히 전달하고, 특히 의존성 관리를 체계적으로 하기 위해 고안되었다. 톰 프레스턴-베르너가 2011년에 제안한 이후, 오픈 소스 생태계를 중심으로 널리 채택되어 패키지 관리와 CI/CD 파이프라인에서 표준적인 관행으로 자리 잡았다.
시맨틱 버저닝의 핵심 목적은 버전 번호 자체가 변경 사항의 성격과 그에 따른 호환성 수준을 예측 가능하게 알려주는 것이다. 이를 통해 라이브러리나 도구를 사용하는 개발자는 새 버전을 업데이트할 때 기존 코드가 정상적으로 동작할지, 아니면 수정이 필요한지를 버전 번호만 보고도 대략적으로 판단할 수 있다. 이는 복잡한 소프트웨어 프로젝트의 빌드와 유지보수를 크게 단순화하는 장점을 제공한다.
2. 버전 형식
2. 버전 형식
2.1. 주 버전(Major)
2.1. 주 버전(Major)
주 버전은 시맨틱 버저닝 체계에서 가장 왼쪽에 위치하는 숫자이다. 이 숫자는 공개 API에 호환되지 않는 변경이 이루어졌을 때 증가한다. 주 버전이 변경되면, 이전 주 버전과의 하위 호환성이 깨진다는 것을 의미한다. 따라서 사용자나 다른 의존성을 가진 프로젝트는 해당 변경 사항을 반영하여 코드를 수정해야 할 수 있다.
주 버전을 증가시키는 변경의 예로는 기존 함수나 클래스의 이름 변경, 삭제, 또는 인터페이스의 구조를 바꾸는 수정 등이 포함된다. 이러한 변경은 소프트웨어의 핵심적인 동작 방식에 영향을 미칠 수 있다. 따라서 개발자는 주 버전 업데이트를 신중하게 결정해야 하며, 변경 로그를 통해 사용자에게 명확히 알려야 한다.
주 버전이 0인 경우(0.y.z)는 소프트웨어가 초기 개발 단계에 있음을 나타낸다. 이 시기의 공개 API는 불안정할 수 있으며, 어떤 변경도 하위 호환성을 보장하지 않는다. 소프트웨어가 안정화되어 공식적인 첫 번째 주요 릴리스를 준비하면, 버전은 1.0.0으로 올라간다.
2.2. 부 버전(Minor)
2.2. 부 버전(Minor)
부 버전은 시맨틱 버저닝 체계에서 두 번째 자리의 숫자를 가리킨다. 이 숫자는 이전 버전과의 하위 호환성을 유지하면서 새로운 기능이 추가되었을 때 증가한다. 즉, 공개 API의 변경 없이 기능이 확장된 경우에 해당한다. 부 버전이 증가했다는 것은 해당 소프트웨어가 이전 주 버전과 동일한 수준의 호환성을 제공하면서 더 많은 기능을 포함하고 있음을 의미한다.
부 버전의 증가는 주로 새로운 기능의 도입이나 개선을 반영한다. 예를 들어, 라이브러리에 새로운 유틸리티 함수가 추가되거나, 기존 기능의 성능이 개선되었지만 그 사용 방법(즉, 공개 API)은 변경되지 않은 경우가 여기에 해당한다. 이러한 변경은 기존 사용자의 코드를 깨뜨리지 않으면서 소프트웨어의 유용성을 높인다.
부 버전이 0인 상태, 즉 초기 개발 단계에서는 규칙이 다르게 적용될 수 있다. 이 시기에는 공개 API가 안정되지 않았을 가능성이 높기 때문에, 부 버전의 증가가 반드시 하위 호환성을 보장하지는 않는다. 안정된 1.0.0 버전 이후부터 본격적인 시맨틱 버저닝 규칙이 적용된다.
부 버전이 변경될 때는 수 버전(Patch) 숫자가 0으로 초기화된다. 이는 해당 부 버전 내에서 새로운 기능 추가와 관련 없는 버그 수정이 아직 이루어지지 않았음을 명시적으로 나타내는 방식이다.
2.3. 수 버전(Patch)
2.3. 수 버전(Patch)
수 버전은 버전 번호의 세 번째 숫자에 해당한다. 수 버전은 하위 호환성을 유지하는 버그 수정이 이루어졌을 때 증가한다. 여기서 버그 수정이란 잘못된 동작을 올바르게 고치는 내부 변경을 의미한다.
수 버전을 증가시키는 변경 사항은 공개 API를 변경해서는 안 된다. 즉, 기존 기능의 동작 방식을 의도적으로 바꾸지 않아야 한다. 이러한 변경은 사용자나 다른 의존성을 가진 시스템에 영향을 주지 않으며, 단순히 소프트웨어의 안정성과 신뢰성을 높이는 데 목적이 있다.
수 버전의 증가는 주 버전이나 부 버전의 증가보다 훨씬 빈번하게 발생하는 것이 일반적이다. 이는 소프트웨어의 수명 주기 동안 지속적으로 발견되는 사소한 결함을 수정하고 성능을 미세 조정하는 과정을 반영한다. 따라서 수 버전의 변경은 대체로 가장 위험이 낮은 업데이트로 간주된다.
수 버전 번호는 초기 개발 단계에서 0으로 시작하며, 각 버그 수정 릴리스마다 1씩 증가한다. 예를 들어, 버전 1.2.3에서 하위 호환성 있는 버그 수정이 이루어지면 다음 버전은 1.2.4가 된다. 이 규칙은 의존성 관리 도구가 자동으로 안전한 업데이트를 적용할 수 있는 기준을 제공한다.
2.4. 사전 릴리스 및 빌드 메타데이터
2.4. 사전 릴리스 및 빌드 메타데이터
사전 릴리스 및 빌드 메타데이터는 시맨틱 버저닝 규격에서 정식 버전 번호에 추가 정보를 덧붙이기 위해 정의된 확장 형식이다. 이들은 주, 부, 수 버전 번호 뒤에 하이픈(-) 또는 더하기 기호(+)를 사용하여 연결된다. 이 확장 정보는 버전의 우선순위를 결정하는 데 영향을 미치며, 의존성 관리 도구가 버전을 해석하고 정렬하는 방식에 중요한 역할을 한다.
사전 릴리스 버전은 하이픈(-) 뒤에 식별자를 점(.)으로 구분하여 표기한다. 예를 들어 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92와 같은 형태를 가진다. 이 식별자는 알파벳과 하이픈으로 구성된 ASCII 문자, 숫자, 점만을 포함해야 한다. 사전 릴리스 버전은 동일한 주, 부, 수 버전을 가진 정식 버전보다 우선순위가 낮다. 예를 들어 1.0.0은 1.0.0-alpha보다 높은 버전으로 간주된다. 사전 릴리스 버전 간의 우선순위는 점으로 구분된 각 식별자를 숫자, ASCII 문자 순으로 비교하여 결정한다. 이는 알파 테스트나 베타 테스트 단계의 불안정한 릴리스를 표시하는 데 주로 사용된다.
빌드 메타데이터는 더하기 기호(+) 뒤에 식별자를 점으로 구분하여 표기한다. 예를 들어 1.0.0+20130313144700, 1.0.0+exp.sha.5114f85와 같은 형태다. 빌드 메타데이터는 컴파일 타임, 빌드 번호, Git 커밋 해시 등 버전의 우선순위에 영향을 주지 않는 추가 정보를 포함하기 위해 존재한다. 따라서 1.0.0+20240301과 1.0.0+20240302는 동일한 버전으로 취급되며, 빌드 메타데이터는 버전 비교 시 무시된다. 사전 릴리스 식별자와 빌드 메타데이터는 함께 사용될 수 있으며, 이 경우 1.0.0-beta.1+exp.1과 같은 형식이 된다.
이러한 확장 형식은 소프트웨어 개발 과정에서 CI/CD 파이프라인을 통해 생성되는 다양한 빌드 아티팩트를 구분하거나, 사용자에게 아직 정식 릴리스가 아님을 명시적으로 알리는 데 유용하게 활용된다.
3. 버전 변경 규칙
3. 버전 변경 규칙
3.1. 주 버전 증가 조건
3.1. 주 버전 증가 조건
주 버전은 MAJOR.MINOR.PATCH 형식에서 가장 왼쪽에 위치한 숫자이다. 주 버전을 증가시키는 가장 핵심적인 조건은 하위 호환성이 깨지는 변경, 즉 이전 주 버전과의 호환성이 없는 변경사항이 포함될 때이다. 이는 API의 공개된 인터페이스가 변경되어 기존 사용자 코드가 더 이상 작동하지 않을 수 있는 경우를 의미한다.
구체적으로, 주 버전 증가 조건은 공개된 API가 의도적으로 변경되었을 때 적용된다. 예를 들어, 공개된 함수나 메서드의 이름이 바뀌거나, 매개변수의 개수나 순서가 변경되거나, 반환 값의 형태가 달라지는 경우가 이에 해당한다. 또한, 기존 기능의 동작 방식이 이전 버전과 호환되지 않도록 변경되는 경우도 주 버전을 올려야 한다.
이러한 규칙은 의존성을 관리하는 소비자에게 명확한 신호를 제공한다. 주 버전이 증가했다는 것은 해당 패키지를 업데이트할 때 자신의 코드를 검토하고 수정해야 할 가능성이 높다는 것을 의미한다. 따라서 시맨틱 버저닝을 따르는 라이브러리나 프레임워크는 하위 호환성을 깨는 변경을 매우 신중하게 고려하며, 이러한 변경이 불가피할 때만 주 버전을 올린다.
3.2. 부 버전 증가 조건
3.2. 부 버전 증가 조건
부 버전은 새로운 기능이 추가될 때 증가한다. 이때, 하위 호환성을 유지하는 방식으로 기능이 추가되어야 한다. 즉, 기존 API를 변경하거나 제거하지 않고 새로운 기능을 도입하는 경우에 해당한다. 이는 하위 호환성을 보장하기 위한 핵심 규칙이다.
부 버전이 증가하는 구체적인 예로는 새로운 메서드나 클래스를 추가하거나, 기존 기능의 성능을 개선하는 경우가 있다. 또한, 공개 인터페이스에 더 이상 사용되지 않는 기능 표시를 추가하는 것도 부 버전 증가에 해당할 수 있다. 이러한 변경은 기존 코드를 깨뜨리지 않으면서 소프트웨어의 기능을 확장한다.
부 버전이 증가할 때, 수 버전은 0으로 초기화된다. 예를 들어, 버전 2.1.3에서 새로운 하위 호환 기능이 추가되면 버전은 2.2.0이 된다. 이 규칙은 버전 번호 체계를 명확하게 유지하는 데 도움이 된다.
부 버전의 증가는 내부적인 비공개 코드의 대규모 리팩토링이나 개선만으로는 정당화되지 않는다. 이러한 내부 변경은 사용자에게 보이지 않는 변경으로 간주되어, 일반적으로 수 버전 증가의 대상이 된다. 따라서 변경의 공개적 영향 여부가 버전 증가 유형을 결정하는 중요한 기준이 된다.
3.3. 수 버전 증가 조건
3.3. 수 버전 증가 조건
수 버전은 버전 번호에서 세 번째 숫자에 해당하며, 패치 버전이라고도 불린다. 이는 하위 호환성을 유지하면서 버그 수정이나 사소한 변경이 이루어졌을 때 증가한다. 즉, 기존 기능을 깨뜨리지 않고 오류를 수정하거나, 내부적인 성능 개선, 문서 업데이트 등 공개 API에 영향을 주지 않는 변경 사항이 포함된다.
수 버전을 증가시키는 구체적인 조건은 하위 호환성을 보장하는 변경 사항에 초점을 맞춘다. 예를 들어, 잘못된 동작을 수정하는 버그 픽스, 보안 취약점 패치, 특정 환경에서의 호환성을 높이는 사소한 코드 수정, 또는 오타를 수정한 문서화 업데이트 등이 여기에 해당한다. 사용자나 다른 의존성 소프트웨어의 관점에서 볼 때, 이 변경은 기존 기능을 그대로 사용할 수 있도록 하면서 안정성이나 효율성만을 향상시킨다.
따라서 시맨틱 버저닝 규칙에 따르면, 수 버전이 증가했다는 것은 해당 소프트웨어 라이브러리나 패키지의 공개 인터페이스나 기대되는 동작에 변함이 없음을 의미한다. 이는 의존성 관리 시스템이 자동으로 최신 패치 버전으로 업데이트하는 것을 안전하게 허용하는 근거가 된다. 개발자는 새로운 기능이나 주요 변경 없이도 소프트웨어 품질을 지속적으로 개선할 수 있으며, 사용자는 호환성에 대한 걱정 없이 안정성 업데이트를 적용할 수 있다.
3.4. 사전 릴리스 버전 사용
3.4. 사전 릴리스 버전 사용
사전 릴리스 버전 사용은 정식 릴리스 이전의 불안정한 개발 단계를 나타내기 위해 사용된다. 이는 주 버전, 부 버전, 수 버전 뒤에 하이픈(-)과 식별자를 붙여 표시한다. 예를 들어 1.0.0-alpha, 2.1.0-beta.1, 3.0.0-rc.2와 같은 형태를 가진다.
사전 릴리스 식별자는 알파벳, 숫자, 하이픈(-)으로 구성되며, 점(.)으로 구분하여 여러 식별자를 나열할 수 있다. 이 식별자들은 일반적으로 개발 단계를 의미하며, alpha(초기 개발), beta(테스트), rc(릴리스 후보) 등이 자주 사용된다. 시맨틱 버저닝 규칙에 따르면, 사전 릴리스 버전은 해당하는 일반 버전보다 우선순위가 낮다. 즉, 1.0.0 버전은 1.0.0-beta 버전보다 새로운 것으로 간주된다.
사전 릴리스 버전은 안정적인 API를 제공하지 않는 개발 중인 기능을 실험하거나, QA 과정을 거치기 전에 사용자에게 미리 배포하여 피드백을 받는 데 유용하게 활용된다. 이를 통해 개발팀은 정식 릴리스 전에 버그를 발견하고 수정할 수 있는 기회를 가질 수 있다. 또한 의존성 관리 도구는 이러한 버전 구분을 통해 안정 버전과 개발 버전을 명확히 구별하여 프로젝트의 안정성을 유지할 수 있도록 돕는다.
4. 의존성 명시 및 범위
4. 의존성 명시 및 범위
4.1. 버전 범위 지정자
4.1. 버전 범위 지정자
의존성 관리에서 특정 버전이 아닌, 허용 가능한 버전의 범위를 지정할 때 사용하는 구문을 버전 범위 지정자라고 한다. 이는 의존성을 선언할 때 특정 버전에 고정되지 않고, 호환성을 유지하면서 업데이트를 자동으로 적용할 수 있도록 하기 위해 사용된다. 대부분의 패키지 관리자와 의존성 관리 도구는 이러한 범위 지정을 지원한다.
주요 버전 범위 지정 방식으로는 틸드(~)와 캐럿(^)이 널리 사용된다. 틸드(~)는 부 버전 또는 수 버전의 변경을 허용한다. 예를 들어 ~1.2.3은 1.2.3 이상이면서 1.3.0 미만의 모든 버전을 의미한다. 캐럿(^)은 주 버전이 0이 아닐 경우, 가장 왼쪽에서 0이 아닌 부분의 변경을 허용하지 않는다. ^1.2.3은 1.2.3 이상이면서 2.0.0 미만의 버전을 의미하며, ^0.2.3은 0.2.3 이상이면서 0.3.0 미만의 버전을 의미한다. 이 외에도 비교 연산자(>, >=, <, <=)를 사용한 범위 지정이나, 하이픈(-)을 이용한 범위, 와일드카드(*)를 사용하는 방법 등이 있다.
이러한 범위 지정자를 통해 개발자는 프로젝트가 특정 API나 기능에 의존하는 경우, 호환성이 보장되는 범위 내에서 의존성 패키지의 보안 업데이트나 버그 수정을 자동으로 적용받을 수 있다. 이는 유지보수 부담을 줄이고 보안 취약점을 신속하게 해결하는 데 도움이 된다. 특히 대규모 프로젝트나 많은 외부 라이브러리를 사용하는 환경에서 효과적이다.
그러나 버전 범위 지정은 주의 깊게 사용해야 한다. 너무 넓은 범위(예: *)를 지정하면 주요 변경 사항이 포함된 호환되지 않는 버전이 설치되어 예기치 않은 오류를 발생시킬 수 있다. 반대로 너무 엄격한 범위(예: 정확한 버전 고정)는 보안 패치 적용의 지연을 초래할 수 있다. 따라서 프로젝트의 안정성 요구사항과 위험 허용 범위에 따라 적절한 범위 지정 전략을 수립하는 것이 중요하다.
4.2. 의존성 관리 도구에서의 활용
4.2. 의존성 관리 도구에서의 활용
시맨틱 버저닝 규칙은 npm, Maven, pip, NuGet, Cargo 등 현대적인 패키지 관리자와 의존성 관리 도구의 핵심 원리로 광범위하게 채택된다. 이러한 도구들은 의존성을 선언할 때 시맨틱 버저닝에 기반한 버전 범위 지정자를 사용하여, 프로젝트가 호환되는 버전의 외부 라이브러리나 패키지를 자동으로 선택하고 업데이트할 수 있도록 돕는다. 예를 들어, npm의 package.json 파일에서는 "dependencies" 항목에 "라이브러리명": "^1.4.3"과 같은 형식으로 버전 범위를 명시한다. 이는 도구가 자동으로 1.4.3 이상이면서 2.0.0 미만의 최신 버전을 설치하거나 업데이트하도록 지시하며, 하위 호환성이 보장되는 범위 내에서 안전한 업데이트를 가능하게 한다.
의존성 관리 도구들은 시맨틱 버저닝을 활용해 복잡한 의존성 그래프를 해결하고 버전 충돌을 최소화한다. 프로젝트 A가 라이브러리 X의 버전 2.1.0 이상을, 프로젝트 B가 동일 라이브러리의 버전 2.0.0 이상을 요구할 때, 도구는 두 요구사항을 모두 만족하는 공통 버전(예: 2.1.0)을 찾아 설치한다. 이 과정을 의존성 해결이라고 한다. 또한 CI/CD 파이프라인에서는 이러한 버전 명시를 바탕으로 자동화된 빌드, 테스트, 배포가 수행되어 소프트웨어 개발 생명주기의 효율성과 안정성을 높인다.
도구/생태계 | 파일 형식 | 주요 버전 범위 지정자 예시 |
|---|---|---|
npm/Node.js |
|
|
Maven/Java |
|
|
pip/Python |
|
|
Cargo/Rust |
|
|
이러한 통합 덕분에 개발자는 수많은 오픈 소스 라이브러리에 의존하는 현대적인 애플리케이션을 구축할 때, 각 의존성의 정확한 버전을 일일이 관리하는 부담에서 벗어나 자동화된 관리를 통해 생산성을 극대화할 수 있다. 결과적으로 시맨틱 버저닝은 단순한 버전 번호 체계를 넘어, 소프트웨어 생태계 전체의 구성 요소들이 유기적으로 협력할 수 있는 표준화된 통신 수단 역할을 한다고 볼 수 있다.
5. 장점과 한계
5. 장점과 한계
시맨틱 버저닝은 소프트웨어 의존성 관리를 크게 단순화하는 핵심적인 장점을 가진다. 버전 번호의 의미를 명확히 규정함으로써, 개발자는 패키지 관리자를 통해 사용 중인 라이브러리의 새로운 업데이트가 자신의 프로젝트에 안전하게 통합될 수 있는지 쉽게 판단할 수 있다. 특히 수 버전과 부 버전의 증가가 하위 호환성을 보장한다는 규칙은, 개발자가 불필요한 두려움 없이 보안 패치나 기능 추가를 수용하도록 장려하여 소프트웨어 개발 생태계의 전반적인 보안과 현대성을 유지하는 데 기여한다. 이는 대규모 오픈 소스 프로젝트나 마이크로서비스 아키텍처에서 수많은 모듈이 상호 의존할 때 그 효용이 극대화된다.
그러나 이 체계는 몇 가지 실질적인 한계에 직면해 있다. 가장 큰 문제는 규칙의 준수가 전적으로 프로젝트 유지보수자의 의지와 신뢰에 달려 있다는 점이다. 규칙을 위반하는 업데이트가 배포되면, 시맨틱 버저닝을 신뢰하고 자동화된 업데이트 정책을 사용하는 CI/CD 파이프라인에 예기치 않은 빌드 실패나 런타임 오류를 초래할 수 있다. 또한, 공개 API의 변경 여부를 판단하는 것이 항상 명확하지 않은 경우가 있어, 어떤 변경이 주 버전 증가를 요구하는지에 대한 해석의 차이가 발생하기도 한다.
이러한 한계를 보완하기 위해, 많은 커뮤니티와 패키지 저장소는 시맨틱 버저닝을 보다 엄격하게 적용하도록 유도하거나, 변경 로그를 상세히 작성하는 문화를 장려한다. 또한, 개발자들은 완전한 자동 업데이트보다는 신중한 범위 지정(예: 캐럿(^)이나 틸데(~) 사용)을 통해 의존성을 관리하며, 주요 업데이트 전에는 테스트를 충분히 수행하는 것이 일반적이다. 결국 시맨틱 버저닝은 인간 중심의 협약으로서, 그 가치는 광범위한 채택과 일관된 준수에서 비롯된다고 할 수 있다.
