package.json의 버전 특수문자들 (~, ^, ...)
풀스택 프리랜서로 일하면서 주력이었던 C#1에서 node 관련 작업으로 전환하게 되었다. 이 과정에서 버전 관리의 중요성을 깊이 깨달았다. 특히, node의 package.json
에서 버전을 지정하는 다양한 방법과 그 의미에 대해 더 자세히 알아보게 되었고, 이에 대한 나의 이해와 경험을 공유하고자 한다.
패키지 버전
노드 패키지 버전 포맷
노드는 Semantic Versioning을 사용한다. 중학교 때부터 JetBrains ReSharper를 사용해오며 Calendar Versioning에 익숙한 난 처음에 각각의 구분이 뭘 의미하는지 몰라서 대충 첫 번째가 바뀌면 엄청난 패치, 두 번째가 바뀌면 일반 패치, 세 번째가 바뀌면 소소한 패치 정도로 이해했는데 지금 보니 얼추 맞는 것 같기도 하다. 하지만 정확하지는 않다. 이는 버전 제한자, 버전 범위에서 설명하도록 하겠다.
package.json
노드는 package.json
파일로 패키지를 관리한다. 닷넷의 csproj 파일이랑 같은 역할을 한다고 봐도 무방할 것 같다. 여기에서 프로젝트 이름, 의존성 관리 등 프로젝트의 세부 사항을 관리하게 된다.
버전 제한자, 버전 범위
그리고 package.json에서는 버전 제한자를 사용하여 패키지 매니저2를 통해 의존성을 설치할 때 설치될 버전의 범위를 지정할 수 있다.
문제는 어떤 오픈소스 프로젝트는 ~
를 앞에 붙이고, 어떤 건 ^
를 앞에 붙이고, 어떤 건 아예 안 붙이고, 어떤 건 >, >=, <, <=
를 붙이고… 마지막 부등호들은 직관적으로 이해가 되는데 문제는 ^, ~
는 뭐 하는 놈인지 알 수가 없었다.
node-semver
해당 내용은 node-semver 리포지토리의 버전 섹션에서 찾을 수 있었다. 특히 자주 쓰이는 ~(틸드)
와 ^(캐럿)
에 대해 설명해보자면 다음과 같다.
-
캐럿(^): 이 제한자는 메이저 버전 내에서 가장 최신의 마이너 버전으로 업데이트를 허용한다. 예를 들어,
^1.2.3
은1.2.3
이상,2.0.0
미만의 버전을 허용한다. 즉, 메이저 버전 번호가 바뀌지 않는 한 업데이트가 가능하다는 얘기다. -
틸드(~): 이 제한자는 마이너 버전 내에서 가장 최신의 패치 버전으로 업데이트를 허용한다.
~1.2.3
은1.2.3
이상,1.3.0
미만의 버전을 허용한다. 따라서 마이너 버전이 바뀌지 않는 한 업데이트가 가능하다.
>, >=, <, <=
같은 부등호들은 직관적으로 이해가 되지만, ^
와 ~
는 처음 보면 헷갈릴 수 있다. 이 두 제한자를 제대로 이해하고 사용하면 의존성 관리가 훨씬 수월해진다.
아무것도 지정하지 않으면 앞에 =
이 붙어있는 것과 동일하게 처리된다.
주의할 점은, 버전에
>1
이라 지정하면2.0.0
이상의 버전을 의미하지만>1.0
이라 지정하면1.0.1
과 같이1.0.0
보다 큰 버전을 의미한다는 것이다. 즉 자릿수에 따라서 대상 버전 범위가 달라지므로 이를 잘 생각하고 써야 한다.
메이저 버전과 마이너 버전의 차이
소프트웨어 버전 번호는 일반적으로 메이저.마이너.패치
형태로 구성된다. 각각의 부분이 의미하는 바는 다음과 같다.
-
메이저 버전(Major Version): 이 숫자가 바뀌면, 그건 보통 큰 변화를 의미한다. 즉, 기존 버전과 호환되지 않는 변경이 발생했다는 거지. 예를 들어, API가 완전히 바뀌거나, 기존 기능이 제거되는 등의 주요 변경이 이루어졌을 때 메이저 버전을 올린다. 메이저 버전이 바뀌면 기존 사용자들은 업그레이드하기 전에 주의 깊게 살펴봐야 한다.
-
마이너 버전(Minor Version): 마이너 버전이 바뀌는 건 새로운 기능이 추가되거나 개선이 이루어졌지만, 기존 버전과의 호환성은 여전히 유지되는 경우다. 이러한 변경은 새로운 기능을 도입하거나 기존 기능을 확장할 때 발생한다. 마이너 버전 업데이트는 기존 시스템이나 의존성에 큰 영향을 주지 않으면서도 새로운 기능이나 개선을 제공한다.
-
패치 버전(Patch Version): 패치 버전이 바뀔 때는 보통 버그 픽스가 진행되었거나, 보안 문제가 개션되었거나, 마이너 버전을 올리기엔 뭐할 정도로 소소한 기능 개선이 이루어졌을 때이다. 즉, 전체적인 프로그램의 작동에 큰 영향을 미치지 않지만 코드가 변경되었을 때 올라간다고 보면 된다.
예시 (next.js turbopack)
다음 코드는 create-next-app --example with-turbopack
으로 생성한 프로젝트의 package.json
이다.
{
"private": true,
"scripts": {
"dev": "next dev --turbo",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/node": "20.8.10",
"@types/react": "18.2.33",
"@types/react-dom": "18.2.14",
"typescript": "^5.2.2"
}
}
보면 react, react-dom
의 버전은 캐럿 버전으로 지정되었으므로 18.2.0 이상 19.0.0 미만의 버전 중 최신 버전이 설치될 것이다. typescript
도 마찬가지로 *5.2.2 이상 6.0.0 미만**의 버전 중 최신 버전이 설치된다.
반면 @types/node
를 비롯한 타입 패키지들은 prefix 없이 지정되었다. 또한 patch 버전까지 지정되어 있으므로 정확히 해당 버전만 설치되게 된다.
추가로, 만약 "@types/node": "20.8"
로 지정했다면 20.8.0 이상 21.0.0 미만이 설치되게 된다.
댓글
댓글 쓰기