본문 바로가기
  • 하고 싶은 일을 하자
dev

틸드tilde(~)와 캐럿caret(^)의 차이, 어떤 걸 써야할까? (이슈 회고)

by 박빵떡 2023. 6. 26.
반응형

회사에서 tilde와 caret 버전 관련 이슈가 터졌었다.

정말 끔찍했었는데... 어떤 문제였는지 알아보고, 틸드와 캐럿 중에 어떤 걸 써야 하는 지도 살펴보자!

 

어떤 이슈였나?

금요일에 QA를 무사히 마치고 다음주 월요일에 배포했다.

그런데 갑자기 form 에서 submit을 할 때 validation check 하는 부분에서 버그가 발생하며 submit이 되지 않았다. 실제로 validation 문제는 없었는데도 말이다.

그래서 멘붕왔고 롤백했다...

 

원인은 무엇이었나?

form에서 submit 할 때 문제가 발생했으니, 해당 외부 라이브러리의 버전을 낮추니 해결되었다.

 

자세히 설명하자면

금요일 QA가 끝났을 때는 서버에 있던 버전에서는 문제가 없었는데

월요일에 배포할 때는 주말 동안 해당 라이브러리의 버전이 업데이트되었다.

package.json 에 해당 라이브러리 버전은 ^ 으로 관리되고 있었고 (ex ^1.2.3)

마이너 업데이트라 배포할 때는 반영되었다.

마이너 업데이트는 기존 코드에 영향이 없어야 했는데 해당 외부 라이브러리의 실수로 이 문제가 발생했다.

 

해결 방법은?

배포할 때 package.json 에 caret(^)을 tilde(~)로 바꿔서 해결했다. (ex ^1.2.3 이었다면 1.2.9 로)

버전은 major.minor.patch 순서인데 ^보다 ~를 사용하면 좀 더 보수적으로 최신 버전을 사용할 수 있다.

이번에 발생한 문제도 예방할 수 있다.

그럼 모든 버전을 tilde(~)로 사용하면 되는 것일까?

그렇지 않다. 

아래 아티클의 글을 참고해 정리해 보겠다.

https://nodesource.com/blog/semver-tilde-and-caret/

 

Semver: Tilde and Caret

Semver: Tilde and Caret

nodesource.com:443

 

처음에 tilde가 기본이었다

처음 package.json의 semver(Semantic Versioning)에서는 2년 6개월 동안 tilde (~)가 기본값이었다. npm install 을 실행하면 아래처럼 package.json에 기록된다.

"dependencies": {
  "qs": "~2.2.3"
}

tilde (~) 가 있을 경우, patch는 최신 버전을 허용한다. 예를 들어 위의 예시는 2.2.9는 허용하되 2.3.0은 허용하지 않는다.

patch 업데이트의 경우는 기능적으로 이전 버전과 호환되고, 대부분의 유저들이 인지하지 못하는 버그를 해결한다.

 

새로운 Range Specifier 에 대한 필요성

semver에 따르면 minor 버전은 업데이트되어도 기능적으로 이전 버전과 호환이 가능하다.

예를 들어 내가 1.2.3 버전으로 개발했더라도 1.9.9 버전을 설치해서 실행해도 문제없다.

1.2.3 대비 1.9.9 버전은 훨씬 더 많은 버그들이 해결되었을 것이다.

기능적으로 문제없는데 버그가 더 적은 1.9.9 버전으로 배포하는 것이 이득이다.

semver에는 이에 대한 규칙이 없었고, 이에 대한 필요성이 생겨 minor 최신 버전까지 쓸 수 있는 caret(^) 이 생기게 되었다.

 

단, 여기서 주의해야 할 점은 라이브러리 개발자가 "minor 버전은 이전 버전과 완벽하게 호환한다"라는 규칙을 철저하게 지켜야 한다.

내가 겪었던 이슈는 이를 못 지켜서 발생한 것이었다.

 

Tilde와 Caret 의 차이

Tilde는 Patch 에 대해 유동적이다

예를 들어 ~1.2.3 인 경우 다음 마이너 버전인 1.3.0 미만인 버전을 허용한다 (ex 1.2.9)

 

Caret은 Minor와 Patch에 대해 유동적이다.

예를 들어 ^1.2.3 인 경우 2.0.0 미만인 버전을 허용한다 (ex 1.2.9 , 1.5.1)

그리고 major가 0인 경우는 특이하게 적용된다.

^0.0.Z -> 0.0.Z (ex ^0.0.3 인 경우는 0.0.3만 허용된다)

^0.Y.Z -> 0.Y.Z - 0.(Y+1).0 (ex ^0.1.3인 경우 0.2.0 미만 버전 모두 허용된다)

major 0 인 경우는 초기 개발하는 단계라 언제든 어떤 것이든 바뀔 수 있기 때문에 이렇게 예외적인 경우로 정했다고 한다.

그런데 이게 복잡해서 1.0.0으로 시작하는 것을 추천한다.

 

major 0 인 경우에 대해서 실제로 어떻게 동작하는 지 알고 싶다면

node-semver 를 사용하면 된다.

https://github.com/npm/node-semver

 

GitHub - npm/node-semver: The semver parser for node (the one npm uses)

The semver parser for node (the one npm uses). Contribute to npm/node-semver development by creating an account on GitHub.

github.com

 

var semver = require('semver')

semver.toComparators('~1.2.3')
// [ [ '>=1.2.3-0', '<1.3.0-0' ] ]

semver.satisfies('0.0.4', '^0.0.3')
// false

 

https://semver.npmjs.com/

version range 를 어떻게 작성하면 실제로 어떤 버전이 사용 가능한 지 알려주는 곳도 있다.

 

혹시 semver에 대해서 더 궁금하다면 아래 링크로!

https://semver.org/lang/ko/

 

유의적 버전 2.0.0

Semantic Versioning spec and website

semver.org

결론: Caret이 새로운 표준이다.

semver 규칙에 의하면 minor 는 기존 버전과 기능적으로 호환이 되기 때문에

더 버그가 많이 해결된 caret(^) 을 쓰는게 이득이고, 현재 표준이다.

(단, 외부 라이브러리가 semver 규칙을 어긴다면, 즉 minor 업데이트 했을 때 이전 버전과의 호환 문제가 있다면 버그가 발생할 수 있다.)


npm 1.4.3 버전부터 caret 이 표준이 되었다.

npm install --save, npm install --save-dev 등을 하면 caret 으로 버전 관리를 한다.

 

그렇다면 내가 겪었던 이슈는 어떻게 잡아야 하는 것이냐

모든 버전을 tilde 로 바꾸는 건 아니고

결국엔 배포 전에 테스트 코드를 도입해야 해결할 수 있을 것 같다.

 

 

 

반응형

댓글