의존성은 단순히 설치하는 게 아니다

2025년 1월 15일
npmdependenciessemantic-versioninginternal-librarypackage-management

의존성은 단순히 설치하는 게 아니다

npm, Semantic Versioning, 그리고 내부 라이브러리 설계에서의 진짜 고민들


1. 들어가며 – npm install이 시작일 뿐이라는 걸 알기까지

프론트엔드 개발을 하다 보면 자연스럽게 npm install이라는 명령어를 매일같이 사용한다.
하지만 이 명령어가 어떻게 동작하는지, 우리가 설치한 패키지들이 실제로 어떤 식으로 애플리케이션과 얽히는지
깊게 이해하려 들면, 꽤 복잡한 세계가 펼쳐진다.

이번 글에서는 단순히 npm의 기초적인 개념을 짚는 걸 넘어서,
내부 라이브러리를 만들면서 직접 마주친 의존성 충돌과 버전 관리의 문제를 정리한다.


2. Semantic Versioning은 정말 안전한가?

npm 생태계는 Semantic Versioning에 기반한 버전 규칙을 사용한다.

  • major: 기존과 호환되지 않는 변경
  • minor: 호환되며 기능 추가
  • patch: 호환되며 버그 수정

그리고 "^1.2.3" 같은 표현은 ^ 기호를 통해 minor/patch 범위의 자동 업데이트를 허용한다.
하지만 문제는 0.x 버전대의 패키지다. 예를 들어:

"lodash-es": "^0.14.1"

이 표현은 실제로는 patch 수준이 아닌 minor 버전까지도 깨질 수 있다.
즉, ^0.14.1>=0.14.1 <0.15.0이 아닌, >=0.14.1 <0.15.0으로 해석되며, 매우 민감한 업데이트에도 깨질 위험이 있다.


3. dependencies, devDependencies, peerDependencies의 차이는 어디서 드러나는가

  • dependencies: 실제 런타임에서 필요한 모듈
  • devDependencies: 빌드, 테스트, 개발용 도구
  • peerDependencies: 직접 의존하지 않지만 호환성을 강제하거나, 반드시 외부에서 주입되어야 하는 경우

특히 peerDependencies플러그인 시스템, UI 라이브러리 같은 공통 레이어에서 필수다.
같은 react를 두 번 설치하거나, styled-components 인스턴스가 중복되면 예기치 않은 에러가 발생하기 때문이다.


4. 내부 라이브러리 개발에서 마주친 의존성의 그늘 – React, Apollo를 예로

공통 컴포넌트를 묶은 내부 라이브러리를 개발하던 중,
react, @apollo/client 같은 의존성 관리에서 여러 번 충돌을 겪었다.

"dependencies": {
  "react": "^18.2.0",
  "@apollo/client": "^3.8.0"
}

이 구조는 결국 다음 문제를 일으켰다.

  • React 중복 인스턴스Hooks can only be called inside... 오류
  • Apollo 캐시 전략 충돌, 타입 mismatching

그래서 아래처럼 설계를 바꿨다:

"peerDependencies": {
  "react": "^18.0.0",
  "@apollo/client": "^3.8.0"
},
"peerDependenciesMeta": {
  "react": {
    "optional": false
  },
  "@apollo/client": {
    "optional": true
  }
}
  • react: 필수, 반드시 외부에서 제공
  • apollo: 선택적, 특정 컴포넌트에서만 필요

→ 실무에서 유연성과 안정성을 동시에 확보하는 구조였다.


5. peerDependenciesMeta의 힘 – 선택적 의존성과 경고 수준 조절

npm은 peerDependenciesMeta를 통해 peerDependency 항목을 optional로 지정할 수 있게 해준다.
이를 활용하면 "있으면 좋지만 없어도 된다"는 의존성을 부드럽게 처리할 수 있다.

예:

"peerDependenciesMeta": {
  "react-dom": {
    "optional": true
  }
}

이 구조는 모든 기능을 react-dom에 의존하지 않는 컴포넌트에서 특히 유용하다.


6. 결론 – 의존성은 '통제 가능한 신뢰'를 설계하는 일이다

의존성은 단순히 npm install로 설치하고 끝내는 게 아니다.
그건 팀과 프로젝트 전반에 걸쳐 신뢰할 수 있는 체계를 설계하는 일이다.

  • 언제 어떤 버전을 받아들일지
  • 중복 인스턴스가 생기지 않도록 어떤 설계를 취할지
  • 선택적 모듈을 어떻게 선언하고 경고 수준을 조절할지

모두가 겪고 있으면서도 명확히 말하지 않았던 이 고민을
하나하나 문서화하고 구조화하는 것이
내부 라이브러리를 팀이 '믿고 쓸 수 있게' 만드는 첫걸음이었다.


🔍 태그 (SEO 키워드)

npm, dependencies, peerDependencies, semantic versioning, 내부 라이브러리, react, apollo, npm install, 패키지 버전 충돌, node_modules, package.json 이해