HAMA DEVELOP

또 depedency 버전을 잘못 설치하고 말았다.

October 10, 2021

package-lock.json meme

작은 규모의 팀에서 일할 때는 의존성 관리가 비교적 쉬웠다. 거의 나의 로컬 머신에서 작업을 하고 거의 그대로 프로덕션에 사용이 되었다. 의존성은 나만의 의존성이고 뭐가 되었든 npm i 를 하면 의존성 관리는 끝이었다. node_modules, package.json, package-lock.json은 알아서 생기는 부산물일 뿐이라서 별생각 없이 사용하고 있었다.

근데 큰 규모의 팀에서 일하기 시작하니 의존성이 나만의 의존성이 아니게 되었다. 내가 멋모르고 버전을 올려버린 의존성에 의해서 다른 팀원의 컴포넌트에 버그가 생길 수도 있다.

심지어 의존성을 설치하는 순서에 따라서도 다른 package-lock.json이 만들어 질 수도 있다.

의존성 관리에 대한 이해를 높이기 위해서 필요한 지식을 정리해 두면 추후 도움이 될 일이 많을 것 같아 정리해 본다.

결론부터 말하자면, package-lock.json 을 버젼관리 시스템에서 관리해야 하고, 내가 수정한 package-lock.jsonpush 할 때는 필요한 부분만 잘 업데이트되었는지 확인할 필요가 있다고 말하고 싶다.

아래부터는 node_modules가 어떻게 만들어지고 package-lock.json이 어떻게 꼬일수 있는지에 대한 다소 지루할수도 있는 이야기를 한다. 한번 읽어보고 package-lock.json을 더 소중히 여겨야겠다는 생각이 들었으면 좋겠다.

주요 개념

node_modules

실제로 설치된 의존성 들이 들어 있는 곳이다. 애플리케이션을 만들 때 여기의 코드를 가져다 쓰게 된다.

package.json

어떤 의존성이 있는지를 알려주는 파일이다. 의존성 관리 말고 여러 가지 역할도 하지만, 오늘은 의존성 관리 측면에서만 생각해 본다. dependency 하위에 있는 것들이 해당 프로젝트의 의존성이라는 것을 알려준다.

package-lock.json

실제로 프로젝트에 설치되어야 하는 의존성의 정확한 버전 등을 관리한다. 일반적인 프로젝트의 package.json 을 보면, "@babel/code-frame": "^7.14.0", 처럼 레인지가 표시되어 있다. 구체적으로 몇 버전이 설치되어 있는지는 알려주지 않는다. 구체적으로 이런 정보가 담기는 부분이 package-lock.json 이다. 여럿이서 작업하는 프로젝트가 항상 동일한 의존성을 가질 수 있도록 하는 역할을 한다.

npm이 node_modules 폴더를 만드는 법

결국 우리가 사용하는 코드는 node_modules 안에 있는 코드다. npm이 의존성을 설치할 때 node_modules를 어떻게 업데이트하는지 알아보자.

node_modules 폴더도 없고 package-lock.json 도 없고 package.json 의 dependency도 없는 경우

제일 간단한 경우다. npm install 를 입력했다고 생각해 보자. A라는 의존성이 설치된다. node_modules 아래처럼 의존성이 설치된다.

node_modules
|_ A

A가 다른 의존성을 가지고 있는 경우에는 아래처럼 A가 의존하고 있는 의존성도 같이 설치된다. (여러 의존성 들이 서로 같지만 다른 버전의 의존성을 공유하고 있을 때는 폴더의 depth 를 한단게 더 깊게 판다. 2번의 예시부터는 이런 사례를 다룬다. )

node_modules
|_ A
|_ a @v2.0

node_modules 폴더도 없고 package-lock.json 도 없지만 package.json 의 dependency는 있는 경우

 "dependencies": {
 "A": "1.0.0",
 "B": "2.0.0"
 }
 }

package.json이 위와 같은 경우다. npm i 를 하면 위의 의존성을 따라서 설치하게 된다. 이번에는 A는 a@2.0에 의존하고 B는 a@1.0에 의존하는 상황이라고 생각해 보자.

A와 B가 모두 같은 의존성을 가지고 있지만, 의존하는 버전이 다르다. 이 경우 먼저 선언된 A의 의존성을 최상단에 위치시키고 나중에 선언된 B의 의존성은 한 단계 더 깊이 위치시키게 된다.

node_modules
|_ A
|_ a @v2.0
|_ B
 |_ node_modules
 |_ a @v1.0

node_modules 폴더는 없지만, package-lock.json 과 package.json 의 dependency는 있는 경우

package-lock.json이 아래처럼 생겼다고 생각해 보자.

{
 "dependencies": {
 "A": {
 "version": "1.0.0",
 "resolved": "URL 주소",
 "requires": {
    "a": "2.0.0"
    }
 },
 "a": {
 "version": "2.0.0",
 "resolved": "URL 주소",
 },
 "B": {
 "version": "2.0.0",
 "resolved": "URL 주소",
    "requires": {
    "a": "1.0.0",
    "b": "3.0.0"
    },
"dependencies": {
    "a": {
        "version": "1.0.0",
        "resolved": "URL 주소",
        }
    }
 },
 "b": {
 "version": "3.0.0",
 "resolved": "URL 주소",
 }
 }
}

package.json에 있는 의존성을 불러온다. 두 번째 경우와 마찬가지로 package.jsondepedency 에서 정의한 것들을 불러오긴 하지만, 이 순서에 상관없이 package-lock.json에 있는 폴더 구조가 우선된다. 즉, pakage-lock.jsondependency 순서에 상관없이 아래의 구조를 가지게 된다.

node_modules
|_ A
|_ a @v2.0
|_ B
|  |_ node_modules
|       |_ a @v1.0
|_ b @v3.0

node_modules도 있고 package-lock.json 과 package.json 의 dependency도 있는 경우

node_modulespackage-lock.json의 폴더 구조를 따르는 방식으로 재배치 되고 세 번째 케이스와 동일하게 움직인다.

설치순서에 따라 달라지는 Package-lock.json

//command 1
npm install A
npm install B

---

//command 2
npm install B
npm install A

두 커맨드는 같은 기능을 할 것 같지만 다른 식으로 동작한다.(A,B는 모두 앞선 예시에서 계속 사용해온 내부 의존성을 가지고 있다) 둘 다 아래와 같은 package.json을 만들 것이다.

{
"dependencies": {
 "A": "1.0.0",
 "B": "2.0.0"
 }
}

하지만 폴더 구조는 다르게 생성된다. 첫 번째 커맨드에서는 a@v2.0 가 최상위에 있고 a@v1.0은 B안의 node*modules에 위치하게 된다. 두 번째 커맨드는 그 반대다.

//command1
node_modules
|* A
|_ a @v2.0
|_ B
| |_ node_modules
| |_ a @v1.0

//command2
|_ A
| |_ node*modules
| |* a @v2.0
|_ a @v1.0
|_ B

이 경우, 코드를 작성할 때 let a = require('a'); 라고 사용하게 되면 첫 번째 커맨드의 경우 v2 를 사용하게 되고 두 번째 커맨드의 경우 v1을 사용하게 된다. 같은 package.json을 사용하지만 각 프로그램이 다른 식으로 동작할 수도 있는 것이다.

그래서 어쩌면 좋죠?

이야기는 길었지만, 결론은 처음에 말한 것과 같다.

  • package-lock.json은 git과 같은 버전 관리 시스템에서 관리해야 한다.

    여럿이서 같은 버전의 의존성을 가지고 있게 하기 위해서는 같은 package-lock.json을 공유 해야 한다. 서로 다른 버전의 의존성에 기반해서 작업하다가는 예기치 않은 버그가 발생할 수 있다.

  • 변경된 package-lock.json 을 push하기 전에 내가 업데이트 하고자 하는 의존성들만 업데이트되었는지 체크해야 한다.

    내가 프로젝트에 새 의존성을 추가할 때 정말 필요한 것들만 추가 되었는지 확인할 필요가 있다. 뭔가 실수로 다른 의존성을 업데이트 해버렸다던지, 의존성 설치 순서로 인해 이상하게 바뀐 게 있을 수도 있다. package-lock.json 을 업데이트 하는 commit 을 push 하기 전에 내가 진짜 의도한 사항만 반영되었는지 확인해 보자.

단순히 package-lock.json 이 중요하다고 아는 것보다는 node_modules가 만들어지는 원리를 알면 package-lock.json을 더 소중히 다를 것 같아서 이번 글을 작성해 보았다.

tags: