main-logo

package.json 어디까지 알고 계신가요?(peerDependencies, files, engines, version)

package.json의 역할

profile
sunny
2026년 06월 24일 · 0 분 소요

들어가며

프로젝트를 시작하면 공통 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 pacakge.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을 한 번쯤 들여다보는 것을 추천드립니다 🙂

읽어주셔서 감사합니다 :)