본문 바로가기
C.W.K.
Stream
Lesson 02 of 05 · published

package.json과 시맨틱 버전

~14 min · modules, package-json, semver, exports

Level 0노드 입문자
0 XP0/40 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"package.json은 단순한 설정 파일이 아냐. 런타임, 패키지 관리자, 번들러, 편집기가 저마다 필요한 약속을 여기서 읽어."

프로젝트의 신분증부터 읽어

package.json을 보면 이 프로젝트가 누구인지, 어떤 Node에서 실행되는지, 밖으로 무엇을 공개하는지 알 수 있어. 필드가 많아 보여도 먼저 확인할 순서는 분명해.

  • name은 패키지 이름이야. 레지스트리에 공개하려면 필요하고, 범위를 붙인 패키지는 @scope/name처럼 적어.
  • version은 시맨틱 버전 문자열이야. 공개 패키지라면 반드시 있어야 해.
  • type.js 파일을 ESM으로 읽을지 CommonJS로 읽을지 정해. 새 프로젝트에서 가장 먼저 확인할 필드 중 하나야.
  • main은 주로 예전 CommonJS 소비자를 위한 진입점이야. 새 패키지는 공개 경로를 exports로 더 정확히 제한해.
  • exports는 소비자가 가져올 수 있는 경로와 조건을 선언해. ESM, CommonJS, 타입 선언을 같은 공개 경로 아래에서 나눌 수 있어.
  • scripts에는 빌드, 테스트, 실행처럼 반복할 명령에 이름을 붙여 둬.
  • 네 가지 의존성 필드는 런타임용, 개발용, 호스트 제공용, 선택 설치용 패키지를 구분해. 다음 수업에서 자세히 다뤄.
  • engines는 지원하는 Node 버전 범위를 알려 줘. 패키지 관리자 설정에 따라 경고하거나 설치를 막을 수 있어.
  • files는 공개할 압축 파일에 넣을 경로를 좁혀 줘. 공개 전에 npm pack --dry-run으로 실제 목록까지 확인하는 게 안전해.

exports로 공개 경로를 정해

exports는 단순히 첫 파일 하나를 가리키는 필드가 아냐. 패키지의 공개 경계를 만들고, 같은 경로를 ESM과 CommonJS 소비자에게 알맞은 파일로 연결해.

"exports": {
  ".": {
    "types": "./dist/index.d.ts",
    "import": "./dist/index.mjs",
    "require": "./dist/index.cjs"
  },
  "./utils": {
    "types": "./dist/utils.d.ts",
    "import": "./dist/utils.mjs",
    "require": "./dist/utils.cjs"
  }
}

import { x } from 'my-pkg'에는 . 아래의 import 조건이 쓰여. require('my-pkg/utils')에는 ./utils 아래의 require 조건이 쓰이고. 더 중요한 점도 있어. exports에 내놓지 않은 내부 경로는 소비자가 함부로 가져갈 수 없어. 공개 API를 파일 배치와 분리하는 셈이지.

버전 숫자는 호환성 약속이야

시맨틱 버전은 MAJOR.MINOR.PATCH로 읽어.
  • MAJOR는 기존 사용법이 깨지는 변경이야.
  • MINOR는 이전 사용법을 유지하면서 기능을 더한 버전이야.
  • PATCH는 공개 API를 바꾸지 않는 수정이야.
의존성 범위도 함께 읽어야 해. ^1.2.3은 보통 2.0.0 전까지, ~1.2.3은 보통 1.3.0 전까지 허용해. 1.2.3은 그 버전 하나만 가리켜. 범위는 업데이트 정책이고, 잠금 파일은 실제 설치 결과를 고정하는 기록이야. 둘은 서로 대신하지 않아.

scripts는 팀이 공유하는 실행 표면이야

npm run buildscripts.build에 적힌 명령을 실행해. 뒤쪽 명령에 인자를 넘길 때는 npm run build -- --flag처럼 구분자를 둬. 인자 없이 npm run만 실행하면 프로젝트가 제공하는 스크립트 목록을 볼 수 있어.

스크립트를 실행할 때는 node_modules/.bin이 경로에 들어와. 그래서 "build": "vite build"처럼 적어도 전역 Vite를 따로 설치할 필요가 없어. 이 작은 규칙 덕분에 누구나 같은 프로젝트 버전의 도구를 실행해.

Pippa의 고백

예전에는 템플릿의 package.json을 읽지도 않고 복사했어. 아빠가 각 필드가 누구와 맺는 계약인지 설명해 보라니까 막히더라. 그 뒤로는 새 프로젝트를 만들 때 exports, engines, files부터 확인해. 이 파일은 잡다한 설정 모음이 아니라 프로젝트가 바깥세상에 내미는 신분증이니까.

Code

모던 Node 패키지의 기본 모양·json
{
  "name": "@cwk/example",
  "version": "1.2.3",
  "description": "ESM을 기본으로 작성한 모던 Node 패키지",
  "type": "module",
  "engines": {
    "node": ">=22"
  },
  "main": "./dist/index.cjs",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    },
    "./utils": {
      "types": "./dist/utils.d.ts",
      "import": "./dist/utils.mjs"
    }
  },
  "scripts": {
    "build": "tsc -p tsconfig.build.json",
    "test": "node --test",
    "lint": "eslint .",
    "start": "node --env-file=.env --watch ./src/server.mjs"
  },
  "files": [
    "dist",
    "README.md"
  ],
  "dependencies": {
    "undici": "^7.0.0"
  },
  "devDependencies": {
    "@types/node": "^24.0.0",
    "typescript": "^5.5.0"
  }
}
버전과 의존성 범위 확인하기·bash
# 내 패키지 버전 올리기
npm version patch    # 1.2.3 → 1.2.4
npm version minor    # 1.2.4 → 1.3.0
npm version major    # 1.3.0 → 2.0.0
# 기본 설정에서는 커밋과 태그도 함께 만든다.

# 선언된 범위 안에서 의존성 갱신하기
npm update
npm install pkg@latest   # 범위를 넘어 최신 버전으로 바꿀 때는 신중하게

# 설치 버전과 갱신 가능 범위 확인하기
npm outdated
# Package   Current   Wanted   Latest
# undici    7.0.0     7.2.1    8.0.0
# Wanted는 현재 범위 안의 최신, Latest는 공개된 전체 최신 버전

External links

Exercise

node_modules에서 패키지 하나를 골라 그 패키지의 package.json을 열어 봐. type, exports, engines.node를 찾고, ESM과 CommonJS를 모두 제공하는지도 확인해. 마지막에는 소비자가 보게 될 진입점, 필요한 Node 버전, 모듈 형식을 한 문단으로 정리해.
Hint
하위 경로마다 importrequire 조건이 모두 있으면 두 모듈 형식을 함께 제공하는 경우가 많아. import만 있으면 ESM 전용일 수 있고, main만 있고 exports가 없다면 예전 CommonJS 배포 방식일 가능성이 커. engines.node는 지원 범위를 알리는 선언이니 패키지 관리자 설정과 실제 CI 조건도 함께 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 똘이warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.