2026. 7. 9. 07:50ㆍUX Engineer 이야기
들어가며
프로젝트를 시작하면 공통 UI를 설계하더라도, 보통은 해당 프로젝트 내에서만 제한적으로 사용하는 경우가 많았습니다. 이번에는 저는 공통 컴포넌트를 별도 패키지로 만들어 배포하고, 다른 서비스에서 설치해 사용하는 구조로 진행하게 되었습니다.
그 과정에서 package.json을 단순히 “패키지 목록과 실행 스크립트를 관리하는 파일”로만 알고 있다가 실제로 어떤 역할을 하는지 살펴보게 되어 소개하고자 합니다.
1. PeerDependencies
dependencies와 devDependencies는 프로젝트 환경 세팅 시 설치해야 하는 항목들이라 익숙한 개념입니다. 하지만 peerDependencies는 npm init 시 기본적으로 생성되지 않기 때문에 상대적으로 생소할 수 있어, 함께 비교해 보았습니다.
- dependencies
패키지가 실제 실행될 때 필요한 의존성으로 런타임에서 필요한 라이브러리는 dependencies에 포함해야 합니다.
(예: react, react-dom, styled-components, lucide-react) - devDependencies
개발, 빌드, 테스트 과정에서만 필요한 의존성입니다.
(예: TypeScript, ESLint, Sass, Storybook) - peerDependencies
peer는 ‘동료’, ‘동등한 위치’라는 의미가 있으며, 이 패키지를 사용하는 쪽에서 직접 설치해야 하는 의존성을 의미합니다. 공통 컴포넌트를 패키지로 배포할 경우 react, react-dom, styled-components와 같은 라이브러리는 peerDependencies로 정의합니다. 특히 React는 중복으로 설치될 경우 Hooks 오류나 Context 분리 문제가 발생할 수 있기 때문에 peerDependencies로 관리하는 것이 중요합니다.
1-1. 공통 컴포넌트 설계 시 의존성 구성 방법
공통 컴포넌트를 설계할 때는 devDependencies와 peerDependencies에 동시에 선언하는 방식을 추천합니다.
react를 dependencies에 넣었다가 배포 시 제외하는 방법도 있지만, 경험상 “나중에 제외하는 방식”은 실수할 가능성이 높아서 개발 단계에서는 필요한 의존성을 devDependencies에 포함하고 실제 사용 환경에서는 peerDependencies를 통해
버전 호환성을 사용자에게 위임하는 구조가 더 안정적이라고 합니다.
// common-components package.json
{
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"styled-components": "^6.0.0"
},
"devDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.1.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"typescript": "^5.0.0"
}
}
또한 external 설정을 하지 않으면 peerDependencies로 선언했더라도 번들 결과물에 포함될 수 있기 때문에 반드시 설정해야 합니다.
export default defineConfig({
build: {
rollupOptions: {
external: ['react', 'react-dom', 'styled-components']
}
}
});
2. Files
files 필드를 사용하면 배포에 포함될 파일을 명시적으로 제한하는 요소인데요,
불필요한 파일이 배포되는 것을 방지하기 위해 번들 결과물만 포함하는 것이 좋습니다.
"files": [
"dist",
"README.md"
]
3. Engines, packageManager
패키지를 사용할 수 있는 Node.js 버전과 패키지 매니저를 명시할 수 있습니다.
현재 프로젝트에서는 Node 버전이 12~20까지 다양하고, npm, yarn, pnpm 등 여러 패키지 매니저가 혼용되고 있었습니다.
이처럼 협업 환경에서 Node 버전 차이 때문에 빌드 오류가 발생하는 경우가 많아서, engines 필드를 통해 최소 버전을 강제하는 것이 안정성을 높이는 데 도움이 되었습니다.
(실제로 lockfile 충돌 문제로 pnpm으로 통일하는 작업을 진행하기도 했습니다.)
{
"engines": {
"node": ">=18"
},
"packageManager": "pnpm@9.0.0"
}
4. Version
단일 프로젝트에서는 package.json의 version을 크게 중요하게 생각하지 않았는데 공통 컴포넌트를 배포하는 구조에서는 version 관리가 매우 중요했습니다.
사용하는 프로젝트 환경에 따라 적절한 버전을 선택해 설치해야 하므로 배포 전 version 관리와 검증은 필수입니다. Semantic Versioning 규칙을 따르며, 협업과 배포 안정성을 위한 중요한 기준이 됩니다.
1.0.0
│ │ │
│ │ └─ patch: 버그 수정
│ └─── minor: 하위 호환 기능 추가
└───── major: 하위 호환이 깨지는 변경
마치며
package.json을 단순히 “패키지 목록을 관리하는 파일” 정도로만 생각했는데 이번 과정을 통해 역할을 다시 알게 되었습니다. 혹시 공통 컴포넌트나 디자인 시스템을 구축하고 있다면, package.json을 한 번쯤 들여다보는 것을 추천해 드립니다. 🙂
읽어주셔서 감사합니다 :)
이 글은 https://tech.pxd.co.kr/ 에서도 보실 수 있습니다.