유의적 버전(SemVer): ^와 ~, 그리고 1.2.3의 의미
"react": "^18.2.0" 의 ^는 무슨 뜻일까요? 의존성 버전 표기를 이해하면 "업데이트했더니 깨졌다" 사고를 줄일 수 있습니다.
MAJOR.MINOR.PATCH
유의적 버전(Semantic Versioning)은 1.2.3 형태로, 각 자리가 약속을 담습니다.
| 자리 | 올라가는 경우 | 호환성 |
|---|---|---|
| MAJOR (1) | 호환 깨지는 변경 | 코드 수정 필요할 수 있음 |
| MINOR (2) | 기능 추가(하위 호환) | 안전 |
| PATCH (3) | 버그 수정 | 안전 |
즉 1.x.x → 2.0.0은 "기존 코드가 깨질 수 있으니 주의"라는 공식 신호입니다.
^ 와 ~ 의 차이
package.json의 범위 표기가 어디까지 자동 업데이트를 허용할지 정합니다.
"^1.2.3" → 1.2.3 이상, 2.0.0 미만 (MINOR·PATCH 허용)
"~1.2.3" → 1.2.3 이상, 1.3.0 미만 (PATCH만 허용)
"1.2.3" → 정확히 1.2.3만
^는 "호환되는 새 기능까지 받겠다", ~는 "버그 수정만 받겠다"는 보수적 선택입니다.
예외: 0.x.x 버전대에서는 ^0.2.3이 0.3.0 미만으로 묶입니다. 1.0.0 이전엔 MINOR도 깨질 수 있다고 보기 때문입니다.
lock 파일이 진짜 설치 버전을 고정한다
범위 표기는 "허용 범위"일 뿐, 실제 설치된 정확한 버전은 package-lock.json(또는 yarn.lock)에 박힙니다. 그래서:
- lock 파일은 반드시 커밋합니다(팀·CI가 동일 버전을 설치)
- 재현 가능한 설치는
npm ci(lock 그대로 설치, 빠르고 엄격)
안전하게 업데이트하기
npm outdated # 무엇이 구버전인지 확인
npm update # 범위 내에서 최신으로(^,~ 규칙 준수)
npm install pkg@latest # MAJOR까지 올릴 땐 의도적으로
MAJOR 업그레이드는 변경 로그(CHANGELOG)와 마이그레이션 가이드를 먼저 읽고, 별도 브랜치에서 테스트하세요.
마무리 체크리스트
MAJOR=호환 깨짐,MINOR=기능,PATCH=수정^=MINOR까지,~=PATCH만- lock 파일은 커밋, CI는
npm ci - MAJOR 올릴 땐 CHANGELOG 먼저
버전 숫자는 "이걸 올려도 안전한가?"에 대한 작성자의 약속입니다. 약속의 문법을 알면 업데이트가 덜 무섭습니다.
함께 보면 좋은 글
좋은 코드 리뷰 하는 법: 팀을 성장시키는 리뷰 문화
코드 리뷰는 결함 잡기를 넘어 팀 학습의 장입니다. 리뷰어와 작성자 모두를 위한 실전 원칙과 좋은/나쁜 코멘트 예시를 정리합니다.
console.log를 넘어서: 더 빠르게 디버깅하는 7가지 방법
console.table·console.dir·조건부 중단점·watch 표현식·네트워크 탭까지. 무작정 로그를 찍는 대신 원인을 빠르게 좁히는 디버깅 습관을 정리합니다.
git stash 제대로 쓰기: 작업 중 급하게 브랜치 바꿀 때
작업 중인 변경을 커밋하지 않고 잠시 치워두는 git stash. save·pop·list·apply·drop의 차이와, 특정 파일만 stash하는 실전 패턴까지 정리합니다.