한 달 정도 전에 트위터에서 다음과 같은 트윗을 보고 처음 캐럿(^
)의 존재를 알게 되었다. 아랫글의 의미는 npm에서 package.json
에서 의존성의 버전을 명시할 때 기존의 기본값인 ~1.2.3
대신 ^1.2.3
과 같이 사용하라는 의미이다.
npm protip: use ^ not ~
— TJ Holowaychuk (@tjholowaychuk) January 30, 2014
틸드(~)
일단 틸드(~
)를 모르는 분들을 위해 간단히 설명하자. npm문서를 보면 npm을 사용할 때 package.json
에서 버전 명시를 다음과 같이 할 수 있다.
1.2.3
>1.2.3
>=1.2.3
<1.2.3
<=1.2.3
~1.2.3
여기서 가장 많이 사용하는 방식이 틸드(~
)를 이용한 방식이고 npm install MODULE --save
나 npm install MODULE --save-dev
를 사용하면 자동으로 package.json
에 의존성을 추가할 수 있는데 이때 기본값으로 사용하는 방법이 틸드(~
)방식이다. 틸드는 간단히 말하면 현재 지정한 버전의 마지막 자리 내의 범위에서만 자동으로 업데이트한다.
~0.0.1
:>=0.0.1 <0.1.0
~0.1.1
:>=0.1.1 <0.2.0
~0.1
:>=0.1.0 <0.2.0
~0
:>=0.0 <1.0
그래서 다양한 방식으로 버전을 명시했을 때 위와 같은 범위내에서 자동으로 업데이트한다. 이 방식은 꽤 다루기가 편해서 그동안 유용하게 사용하고 있었다.
캐럿(^
)
그러면 캐럿(^
)은 틸드와 어떻게 다른가?
캐럿을 설명하기 전에 먼저 Semantic Versioning(보통 SemVer라고 부른다.)를 설명해야 하는데 Node.js도 그렇고 npm의 모듈은 모두 SemVer를 따르고 있다. SemVer는 MAJOR.MINOR.PATCH
의 버저닝을 따르는데 각 의미는 다음과 같다.
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
즉, MAJOR 버전은 API의 호환성이 깨질만한 변경사항을 의미하고 MINOR 버전은 하위호환성을 지키면서 기능이 추가된 것을 의미하고 PATCH 버전은 하위호환성을 지키는 범위내에서 버그가 수정된 것을 의미한다.
캐럿(^
)은 Node.js 모듈이 이 SemVer의 규약을 따른다는 것을 신뢰한다는 가정하에서 동작한다. 그래서 MINOR나 PATCH버전은 하위호환성이 보장되어야 하므로 업데이트를 한다.
^1.0.2
:>=1.0.2 <2.0
^1.0
:>=1.0.0 <2.0
^1
:>=1.0.0 <2.0
그래서 캐럿을 사용했을 때는 위와 같이 동작한다. 틸드와 비교해 보면 차이점은 명확한데 1.x.x
내에서는 하위호환성이 보장되므로 그 내에서는 모두 업데이트하겠다는 의미이다.
하지만 여기에 예외사항이 있다. 다음 내용을 보자.
^0.1.2
:>=0.1.2 <0.2.0
^0.1
:>=0.1.0 <0.2.0
^0
:>=0.0.0 <1.0.0
^0.0.1
:==0.0.1
버전이 1.0.0
미만인 경우(SemVer에서는 pre-release라고 부른다.)에는 상황이 다르다. 소프트웨어 대부분에서 1.0
버전을 내놓기 전에는 API 변경이 수시로 일어난다. 그래서 0.1
을 쓰다가 0.2
를 사용하면 API가 모두 달라졌을 수 있다. 그래서 캐럿(^
)을 사용할 때 0.x.x
에서는 마치 틸드처럼 동작해서 지정한 버전 자릿수 내에서만 업데이트한다.(앞에 예시와 비교해보면 차이점을 알 수 있다.) 그리고 0.0.x
인 경우에는 하위호환성 유지가 안 될 가능성이 더 높으므로 위의 마지막 예시처럼 지정한 버전만을 사용한다.
캐럿에 대한 오류
캐럿은 동작방식을 이해하면 수긍할만하다.(물론 배포용으로는 어렵지만 이건 틸드도 마찬가지다.) 하지만 약간 복잡한 부분이 있어서 그냥 틸드를 쓰고자 할 수도 있는데 그럼에도 캐럿의 존재 정도는 알고 있어야 한다. 캐럿은 근래에 추가된 기능이므로 과거 버전의 npm은 캐럿을 이해하지 못한다.(정확히 어느 버전부터 추가되었는지 찾지 못하겠다.) 그래서 캐럿이 없는 구 버전의 npm에서 캐럿을 사용하면 다음과 같이 오류가 발생한다.
Jade에 1.3.0
이 존재하지만 ^1.3.0
의 구문을 이해하지 못하므로 버전을 찾지 못하고 "Error: No compatible version found"이라는 설치 오류가 발생했다. 이는 npm install -g npm
을 통해서 npm을 최신 버전으로 올리면 해결할 수 있다.(캐럿을 모른 상태에서 저 오류를 보면 좀 당황스럽다.)
npm v1.4.3부터는 npm install MODULE --save
나 npm install MODULE --save-dev
를 사용했을 때 기본값이 틸드 대신 캐럿이 되었기 때문에 틸드를 사용하고자 한다면 매번 직접 수정해야 한다. 그리고 본인은 틸드방식을 사용하더라도 참조한 모듈이 다른 모듈을 참조할 때 캐럿을 사용할 수도 있으므로 npm을 최신 버전으로 업데이트하지 않으면 결국 오류가 나서 제대로 설치가 안 될 것이다.
한글로된 자료 찾기 힘들었는데 감사합니다~~~!
pre-release 버전에는 예외가 있는지 몰랐네요.
정리를 잘 해주셔서 많은 도움이 되었습니다. 감사합니다.