0. 공부 계기

프로젝트를 진행하며 Package.json 파일은 자주 보고, 사용하지만 Package-lock.json에 대해서 따로 공부해본 경험이 없습니다.

그저 패키지매니저를 재설치 할 때 package-lock.json와 node_modules를 지우고 재설치해야된다는 정도...? 그래서 정확히 공부해야할 필요성을 느껴 공부하기 시작했습니다.

 

1. Package.json이란

Node.js 프로젝트를 시작할 때, 터미널에 'npm install', 'yarn' 등의 패키지매니저를 설치하면 아래와 같이 여러 파일들이 기본적으로 설치됩니다.

 

현재 진행중인 프로젝트의 Package.json

{
    "name": "moyeota-webview",
    "private": true,
    "version": "0.0.0",
    "type": "module",
    "scripts": {
        "dev": "vite --host",
        "build": "tsc && vite build",
    },
    "dependencies": {
        "react": "^18.2.0",
        "react-bootstrap": "^2.10.1",
        /* 중략... */
    },
    "devDependencies": {
        "@types/navermaps": "^3.6.7",
        "@types/react": "^18.2.15",
        /* 중략... */
    }
}

 

2. Package-lock.json이란

  • package-lock.json 파일은 npm을 활용해 node_modules 트리나 package.json 파일을 수정하게 되면 자동으로 생성되는 파일입니다.
  • 이 파일은 파일이 생성되는 시점의 의존성 트리에 대한 정확한 정보를 가지고 있습니다.

다음은 Package-lock.json의 일부분입니다.

{
  "name": "moyeota-webview",
  "version": "0.0.0",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "": {
      "name": "moyeota-webview",
      "version": "0.0.0",
       /* 중략 */
      "devDependencies": {
        /* 중략 */
        "vite-plugin-svgr": "^3.2.0",
        "zustand": "^4.4.1"
      }
    },
  "dependencies": {
    /* 중략 */
    "@types/react": {
      "version": "18.2.28",
      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.28.tgz",
      "integrity": "sha512-ad4aa/RaaJS3hyGz0BGegdnSRXQBkd1CCYDCdNjBPg90UUpLgo+WlJqb9fMYUxtehmzF3PJaTWqRZjko6BRzBg==",
      "requires": {
        "@types/prop-types": "*",
        "@types/scheduler": "*",
        "csstype": "^3.0.2"
      }
    },
    "zustand": {
      "version": "4.4.3",
      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.3.tgz",
      "integrity": "sha512-oRy+X3ZazZvLfmv6viIaQmtLOMeij1noakIsK/Y47PWYhT8glfXzQ4j0YcP5i0P0qI1A4rIB//SGROGyZhx91A==",
      "dev": true,
      "requires": {
        "use-sync-external-store": "1.2.0"
      }
    }
  }
}

 

2.1 Package-lock.json이 필요한 이유

  • package.json 파일 의존성 선언에는 version range가 사용됩니다.
  • version range란 특정 버전이 아니라 버전의 범위를 의미합니다.
  • 예를 들어, npm install dotenv를 실행하면 "dotenv": "^16.3.1" 와 같은 형식으로 package.json에 설치됩니다.
    • 여기서 ^16.3.1^은 캐럿 표기법('16.3.1버전 이상, 17.0.0이하'라는 range를 알려줌)으로 ~(틸드)와 구분됩니다. (아래 정리돼있음)
  • package-lock.json에는 해당 라이브러리 설치 당시의 버전에 대한 기록을 가지고 있습니다.
  • 따라서 이후 다른 팀원이 git clone을 받고, npm install을 한다 하더라도 package-lock.json에 있는 파일 버전을 기준으로 동일하게 다운로드 할 수 있습니다.
   "node_modules/dotenv": {
      "version": "16.3.1",
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
      "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/motdotla/dotenv?sponsor=1"
      }
    },

 

2.2 package-lock.json 사용 예시

package.json and package-lock.json @Atatus

1) 6월 1일, Developer 1이 dotenv 16.3.1을 다운 받습니다. 이는 Package.json으로 관리합니다.

2) Package-lock.json 파일은 Package.json 파일에 기록된 당시의 의존성 트리를 관리합니다.

3) 6월 17일, dotenv 라이브러리가 16.4.0으로 업데이트 됐다고 가정해봅시다

4) 6월 18일, Developer 2가 Git 클론을 받고 npm install 합니다.

     4-1) Developer1이 package-lock.jsongit에 올리지 않았다면 dotenv 16.4.0이 다운로드 될 것입니다.
     4-2) Developer1이 package-lock.jsongit에 올렸다면 Developer2는 클론받은 프로젝트 내의 Package-lock.json 파일을 활용해 정확한 버전인 dotenv 16.3.1이 다운로드 될 것입니다.

 

 

3. 결론

Package-lock.json 파일이 없다면 협업시 팀원과 버전 충돌이 나타날 확률이 매~~우 높다.

우리는 이를 방지하기 위해 Package-lock.json 파일을 사용하는 것이다.

 

 

4. Caret & Tilde

4.1 Semeantic Versioning

React 12.3.5와 같은 버전은 MAJOR.MINOR.PATCH의 구조로 구성되고, 이는 Semantic Versioning, 간단히 줄여 semver이라 부릅니다.

  • MAJOR 버전은 하휘호환 되지 않는 API의 변화들
  • MINOR 버전은 하휘호환이 되는 범위 내에서 기능 추가들
  • PATCH 버전은 하휘호환이 되는 범위 내에서 버그 수정

 

SemVer 명세

1. SemVer을 사용하는 소프트웨어는 무조건(MUST) 공개 API에 선언되어야 한다. 이 API는 코드 그 자체로 선언되거나, 문서로 엄격히 명시해야 한다. 어떤 방식으로든, 정확하고 이해하기 쉬워야 한다.

2. 일반적인 버전 넘버는 무조건(MUST) X.Y.Z 형태여야 한다. 여기서 X, Y, Z는 양의 정수여야 하며, 절대(MUST NOT) 앞에 0을 붙여서는 안된다. X는 MAJOR, Y는 MINOR, Z는 PATCH 버전으로, 각각의 요소는 증가하는 수여야 한다. (1.9.0 -> 1.10.0 -> 1.11.0)

3. 특정 버전을 배포하고나면, 해당 버전의 내용은 절대 (MUST NOT) 변경되어서는 안된다. 어떠한 수정사항일지언정 새로운 버전을 릴리즈 해야한다.

 

여기까지가 제가 번역한 SemVer 명세 1, 2, 3번 입니다. 더 깊은 내용이 궁금하신 분들은 공식문서를 읽어보시면 좋을 것 같습니다.

 

4.2 캐럿(^)과  틸드 (~) 그리고 기타 등등 (<, >, x)

  • 캐럿(^)
    • ^X.Y.Z라 한다면, X 이하의 하위호환성이 보장되는 범위 내에서 버전 업데이트
    • ^1.1.0 : 1.1.0 <= 업데이트 버전 < 2.0  인 버전과 호환 가능
    • 주의) 캐럿도 pre-release 버전 (<1.0)일 경우 틸드처럼 동작
  • 틸드(~)
    • ~X.Y.Z라 한다면, Z 범위 내에서 버전 업데이트
    • ~1.1.0 : 1.1.0 <=, 1.2 > 인 버전과 호환 가능
    • 1.2.x라 한다면, 1.2.x로 표시된 버전만 자동 업데이트가 가능
    • 즉, 1.2.0 ~ 1.2.9까진 가능하지만 1.3.0부턴 자동 업데이트가 불가능
  • ||
    • 1.2.3 || >= 1.2.9 < 2.0.0
    • ||은 or 연산자로, 하나 이상의 범위를 명시할 수 있습니다.
    • 만일 A || B로 버전을 명시한 경우, A 혹은 B 둘 중 하나만 충족해도 버전업이 가능
    • 예를 들어 1.2.3 || >= 1.2.9은 1.2.3 또는 1.3.0 으로 업데이트 가능

 

4.3 그래서 나랑 무슨 상관..?

  • 예를 들어, 내가 zustand 4.4.1 버전을 사용하고 있다고 가정합시다.
  • 그러던 중 4.4.1버전에 버그가 생겨 zustand 개발자는 버그를 수정 후 4.4.2 버전을 다시 릴리즈 했습니다.
  • 그러나 현실적으로 개발자가 사용하는 유저에게 모두 사용중인 zustand의 버전을 업그레이드 하라고 연락을 할 순 없습니다.
  • 이때 만일 package.json의 zustand 버전이 ^4.4.1이라면, 4.5 미만의 버전과는 모두 호환이 가능하다는 것입니다.
  • 따라서 4.4.2 버전이 릴리즈 되고, 업데이트 된다면 나는 굳이 새로 zustand를 업데이트 하지 않아도 자동으로 업데이트 및 호환이 가능하다는 것입니다.

 

 

 

 


[참고자료]

https://semver.org/

https://hyunjun19.github.io/2018/03/23/package-lock-why-need/

https://han7096.medium.com/package-lock-json-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-5652f90b734c

https://dev-ellachoi.tistory.com/65 

+ Recent posts