npm, Cargo, Composer, pip 모두 동일한 semver 단축 표기법을 서로 다르게 해석하며, 제가 테스트한 13가지 범위 표현식 중 4개 도구 모두에서 동일하게 작동하는 것은 단 2개뿐이었습니다.
각 패키지 매니저를 위한 별도의 파서를 작성하고 공식 사양을 따랐으며, 이후 모든 범위를 19개의 버전 번호 매트릭스에 대입하여 실행했습니다. 13 × 19 그리드(247개 셀)를 통해 확인한 결과는 파편화되어 있었습니다. 모든 도구에서 동일한 "yes" 또는 "no" 답변을 내놓은 셀은 17개뿐이었으며, 그 17개 중 대부분은 만장일치로 거절(rejection)된 경우였습니다. 요컨대, 도구들은 "yes"보다는 "no"에 훨씬 더 자주 동의합니다.
도구들이 서로 다른 이유
네 가지 매니저 모두 Semantic Versioning을 따른다고 주장하지만, 각자 고유의 단축 규칙을 추가했습니다.
- Caret (^) 및 dot-x – npm은 caret을 지원하지만, pip는 caret과 dot-x를 모두 무시합니다. 이것만으로도 npm과 pip 사이에 49개 셀의 불일치가 발생합니다.
- Bare versions (단순 버전) – Cargo는 일반적인
1.2.3을 caret 범위로 취급하여 "1.x와 호환됨"을 의미합니다. 반면 npm과 Composer는 동일한 문자열을 정확히 일치하는 것으로 읽어1.2.3만 허용합니다. - Tilde (~) – Cargo의 tilde는 minor 버전을 고정합니다(
~1.2는1.2.*와는 일치하지만1.3.0과는 일치하지 않음). Composer의 tilde는 major 버전을 고정합니다(~1.2는1.*와 일치함). 이 두 해석은 minor 구성 요소가 변경되는 모든 버전에서 갈라집니다.
프리릴리스(Pre-release) 처리 방식도 또 다른 놀라움을 줍니다. ^1.2.3과 같은 범위는 숫자 부분이 상한선보다 낮더라도 2.0.0-rc.1을 포함하지 않습니다. 규칙은 다음과 같습니다. 범위에 프리릴리스 식별자가 명시적으로 언급되지 않는 한 프리릴리스는 제외됩니다.
도구 간에 안전한 유일한 구문
실험 결과, >=1.2.0 <2.0.0과 같은 명시적인 부등호 범위는 npm, Cargo, Composer, pip에서 모두 동일하게 작동합니다. 모든 매니저는 두 경계값을 숨겨진 caret이나 tilde 의미론 없이 문자 그대로의 숫자 제한으로 취급합니다.
만약 "최소 1.2인 모든 1.x 버전"을 표현해야 한다면, 전체를 다 작성하십시오. 글자 수는 몇 자 더 늘어나겠지만, 코드가 어디에서 실행되든 예측 가능한 해결(resolution)을 보장합니다.
단축 표기법이 여전히 유효한 경우
단축 표기법은 단일 언어 프로젝트에서는 여전히 매력적입니다. 순수 npm 생태계 내에서는 ^1.2.3이 "향후 minor 및 patch 릴리스와 호환됨"을 간결하게 나타냅니다. 사용하는 도구가 바뀌지 않을 것이라는 확신이 있다면 Cargo의 caret이나 Composer의 tilde도 마찬가지입니다. 위험은 동일한 매니페스트가 언어 경계를 넘어 재사용되거나, CI 작업이 여러 생태계에서 의존성을 가져올 때 발생합니다.
핵심 요약
언어 간 호환성을 위해 semver 단축 표기법에 의존하는 것은 도박과 같습니다. 버전 제약 조건이 어디서나 동일한 의미를 갖도록 보장하는 유일하고 신뢰할 수 있는 방법은 명시적인 부등호를 사용하는 것입니다. 간결함이 필요할 때는 단일 생태계 내에 머무르고, 일관성이 필요할 때는 단축 표기법 대신 명확성을 선택하십시오.
