의존성 롤러코스터: NPM 테마 파크 탐색
It looks like only the source line was provided. Could you please share the text you’d like translated? Once I have the content, I’ll translate it into Korean while keeping the source link and formatting unchanged.
모든 것을 시작하게 만든 “아하!” 순간
새 기능을 구현하고 있었고, 마치 코드 마법사 🧙♂️가 된 듯한 기분이었다. PR을 제출했는데, TL이 마치 외국어처럼 들리는 댓글을 남겼다:
소비하는 앱이 설치할 수 있도록 이를 peer dependencies로 추가해야 합니다.
뭐라고? 🤔
화면을 바라보았다. Node를 시작한 이후로 dependencies와 devDependencies만 사용해 왔는데, peerDependencies라니? 그리고 내가 사용하는 라이브러리가 직접 의존성을 가지고 있지도 않고, 내가 직접 사용하고 있지도 않은 react-hook-form을 왜 설치해야 하는 걸까?
그 혼란은 나를 npm 의존성 생태계를 완전히 새롭게 이해하게 만든 토끼굴로 이끌었다. package.json을 관리한다는 것은 놀이공원을 운영하는 것과 많이 닮았다.
비유: 프로젝트를 세계적 수준의 테마파크라고 생각해 보라. 놀이기구를 정상 가동하고 손님들을 만족시키려면 다양한 종류의 자원이 필요하다.
의존성 — 주요 매력
{
"dependencies": {
"react": "^18.2.0",
"axios": "^1.6.0"
}
}
이것이 실제 롤러코스터입니다. “Big Loop” 패키지가 없으면, 그 공원은 테마파크가 아니라 단순히 빈 주차장일 뿐입니다.
- 사용 시점: 코드가 직접 import하고 런타임에 필요로 하는 모든 패키지.
- 규칙: 이들은 자동으로 설치됩니다. 만약 없으면, 공원(앱)이 닫힌 것이며(코드가 프로덕션에서 실행되지 않음).
DevDependencies — 유지보수 팀
{
"devDependencies": {
"jest": "^29.0.0",
"eslint": "^8.0.0",
"typescript": "^5.0.0"
}
}
이것들은 안전모, 렌치, 그리고 설계도입니다. 손님들은 보지 못하지만, 이 없이는 롤러코스터를 만들거나 수리할 수 없습니다.
- 사용 시점: 개발, 테스트, 빌드 또는 린팅을 위한 도구 (예: Jest, ESLint, Vite).
- 규칙: 패키지를 라이브러리로 설치할 때 설치되지 않음. 이들은 오직 건설 현장(당신의 로컬 머신)에서만 존재합니다.
PeerDependencies — “자신의 장비를 가져오세요” 요구 사항
{
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
이것은 놀이기구 입구에 있는 “Requirement” 표지판이다. 물미끄럼틀에 “우리는 슬라이드를 제공하지만 당신은 수영복을 직접 가져와야 합니다.” 라고 적힌 것을 상상해 보세요.
“아하!” 순간
우리 컴포넌트 라이브러리는 물미끄럼틀이다. 만약 슬라이드마다 수영복을 함께 번들에 넣어두면(dependencies에 React를 넣는 것과 마찬가지) 손님들에게 맞지 않는 중복된 젖은 옷이 가득하게 되고(번들 부피 증가 및 버전 충돌) 결국 문제가 된다.
대신 우리는 입구에서 확인한다: “수영복(React 19)이 있나요?” 우리 라이브러리는 호스트 환경에 이미 해당 패키지가 설치되어 있기를 기대한다.
체인 미스터리 – 전이적 Peer Dependencies
우리 슬라이드가 특정 부표(다른 라이브러리, 예: awesome-form-components)를 사용하고, 그 부표가 펌프(react-hook-form)를 필요로 한다면, 손님은 두 가지를 모두 가져와야 한다—우리 라이브러리가 react-hook-form을 직접 사용하지 않더라도.
Main App (The Guest) → 메인 앱 (게스트)
└── My Library (The Slide) → └── My Library (슬라이드)
└── awesome-form-components (The Floatie) → └── awesome-form-components (플로트)
└── [peer] react-hook-form (The Pump) 🚩 → └── [peer] react-hook-form (펌프) 🚩
결과: 메인 앱은 반드시 react-hook-form을 설치해야 한다. 설치하지 않으면 npm이 Peer Dependency Resolution Error를 발생시킨다.
- 사용 시점:
- “플러그인” 형태이거나 UI 키트가 더 큰 프레임워크(React, Vue, Tailwind 등)에 연결되는 경우.
- 싱글톤 강제(패키지 인스턴스가 하나만 존재해야 할 때).
- 버전 유연성(개발자가 지정한 범위 내에서 호환 가능한 어떤 버전이라도 사용할 수 있도록 허용).
OptionalDependencies — The VIP Fast Pass
{
"optionalDependencies": {
"fsevents": "^2.3.0"
}
}
These are the “nice‑to‑have” extras. Maybe it’s a heated seat on the log flume. If the heater is out of stock, the ride still works—it’s just a bit colder.
- When to use: Platform‑specific optimizations (e.g., macOS‑only features).
- The rule: If installation fails, npm just shrugs and continues. Your code should handle the “empty seat” gracefully.
전이 의존성 – 공급망
전이 의존성은 우리 의존성의 “의존성”입니다.
롤러코스터(직접 의존성)를 구매했다고 상상해 보세요. 하지만 그 롤러코스터는 특정 브랜드의 볼트와 그리스(전이 의존성)를 사용해 제작되었습니다. 당신은 볼트를 주문하지 않았지만 이제 공원에 들어와 있습니다!
Your Park (Project)
├── Roller‑coaster (Direct)
│ ├── Specialized Bolts (Transitive)
│ └── Industrial Grease (Transitive)
위험: 그 볼트에 안전 리콜(보안 취약점)이 발생하면, 당신의 전체 놀이기구가 위험에 처합니다—볼트 제조업체와 직접 대화한 적이 없더라도.
빠른 참고 표
| Type | Installed When? | Analogy |
|---|---|---|
dependencies | 항상 | 라이드 (필수) |
devDependencies | 로컬 전용 | 도구 (건설) |
peerDependencies | 사용자에 의해 | 장비 (수영복/헬멧) |
optionalDependencies | 가능한 경우 | VIP 혜택 (추가) |
Practical Tips
- Building a library? Use
peerDependenciesfor framework packages so you don’t force a specific React version on your users. This gives them flexibility and keeps bundle size small. - Seeing duplicates? Run
npm dedupeoryarn dedupeto ensure your park isn’t storing five copies of the same “bolt.” - Lock it down: Always commit
package-lock.json(oryarn.lock). It’s the “as‑built” blueprint that guarantees every developer (and every CI run) gets the same versions.
버전 범위 – 공급업체에 무엇을 보내야 하는지 알려주는 방법
| Symbol | Name | Meaning | Example | Accepts | Rejects |
|---|---|---|---|---|---|
^ | Caret | 마이너 + 패치 업데이트 | ^1.2.3 | 1.2.3, 1.3.0, 1.9.9 | 2.0.0 |
~ | Tilde | 패치 전용 업데이트 | ~1.2.3 | 1.2.3, 1.2.4, 1.2.9 | 1.3.0 |
| (none) | Exact | 정확한 버전 필요 | 1.2.3 | 1.2.3 only | 1.2.4 |
>= | Range | 이상 | >=1.2.3 | 1.2.3, 1.3.0, 2.0.0 | — |
이러한 기호들을 이해하면 의존성 버전의 허용 범위(또는 엄격함)를 제어할 수 있어, 여러분의 프로젝트를 안전하고 최신 상태로 유지할 수 있습니다.
버전 호환성
1.2.0“우리 프로젝트가 회로를 건드려서 다음 오류를 발생시킵니다:SyntaxError: Cannot use import statement outside a module
Part 2 에서는 이 문제 뒤에 있는 기술적인 “배선”을 살펴보겠습니다:
- 15 A vs 5 A – ESM과 CommonJS가 단순히 “플러그 앤 플레이”가 안 되는 이유.
- 단락 회로 – 코드 로직이 완벽해도 빌드가 깨지는 이유.
- 고성능 어댑터 –
transpilePackages의 미스터리를 해결하기.
기대해 주세요! ⚡ 곧 배선을 수정할 예정입니다.
그때까지, Pranipat 🙏