
패키지와 의존성, 그리고 package.json과 lockfile 등등 헷갈리는 개념 정리해보기!
패키지는 다른 사람이 만들어 배포한 코드 라이브러리를 의미한다.
예를 들어 이런것들이 다 패키지다.
이런 패키지들은 npm registry에 배포되어 있고, 우리는 다음과 같은 명령어로 쉽게 설치할 수 있다.
pnpm add axios
이렇게 설치하면 프로젝트에서 axios 라이브러리를 사용할 수 있게 된다.
즉,
패키지 = 재사용 가능한 코드 묶음
이라고 이해하면 쉽다.
의존성은 프로젝트가 동작하기 위해 필요한 패키지를 의미한다.
예를 들어 코드에서 axios를 사용한다면:
import axios from "axios"
이 프로젝트는 axios에 의존하고 있다고 말할 수 있다.
그래서 package.json에는 다음과 같이 기록된다.
"dependencies": {
"axios": "^1.6.0"
}
package.json은 프로젝트의 설정과 의존성을 관리하는 파일이다.
대표적으로 다음과 같은 정보를 담고 있다.
예시:
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"react": "^18.2.0",
"axios": "^1.6.0"
}
}
여기서 dependencies 항목은
이 프로젝트가 어떤 패키지에 의존하는지
를 보여준다.
package.json을 보면 버전이 이렇게 적혀 있는 경우가 많다.
axios: ^1.6.0
이 의미는
1.6.x 범위에서 최신 버전 사용
이라는 뜻이다.
그래서 설치할 때마다 다음과 같이 다른 버전이 설치될 수 있다.
1.6.1
1.6.2
1.6.5
이 경우 팀원 간에 다른 버전의 패키지가 설치되어 문제가 발생할 수 있다.
이를 해결하기 위해 lockfile이 사용된다.
lockfile은 실제로 설치된 정확한 버전을 기록하는 파일이다.
예를 들어 pnpm-lock.yaml에는 다음과 같은 정보가 저장된다.
axios 1.6.3
lodash 4.17.21
그래서 팀원이 다음 명령어를 실행하면
pnpm install
항상 동일한 버전의 패키지가 설치된다.
대표적인 lockfile은 다음과 같다.
package-lock.json (npm)yarn.lockpnpm-lock.yaml즉,
| 파일 | 설명 |
|---|---|
| package.json | 프로젝트가 허용하는 패키지 버전 범위를 정의 |
| lockfile | 실제 설치된 정확한 패키지 버전을 기록 |
의존성은 크게 두 가지로 나눌 수 있다.
개발자가 직접 설치한 패키지다.
pnpm add axios
이 경우 axios는 직접 의존성이다.
설치한 패키지가 또 다른 패키지를 사용하는 경우다.
예를 들어 axios 내부 구조는 다음과 같다.
my-project
└ axios
└ follow-redirects
우리는 follow-redirects를 설치한 적 없지만
axios가 필요로 하기 때문에 자동으로 함께 설치된다.
이런 패키지를 전이 의존성이라고 한다.
Peer dependency는 처음 보면 가장 헷갈리는 개념인데,
간단히 말하면
이 패키지를 사용하려면 특정 패키지가 함께 필요하다
라는 의미다.
예를 들어 react-query는 React 위에서 동작한다.
그래서 내부에 다음과 같이 선언되어 있다.
peerDependencies:
react: ^18
이 의미는
react-query는 React가 필요하지만
React는 프로젝트에서 직접 설치해야 한다
라는 뜻이다.
그래서 우리는 보통 이렇게 설치한다.
pnpm add react @tanstack/react-query
React 같은 라이브러리는 여러 버전이 동시에 설치되면 문제가 생긴다.
예를 들어 이런 상황이 발생할 수 있다.
react (앱)
react (라이브러리 내부)
React는 싱글톤 구조로 동작하기 때문에 두 개가 존재하면 충돌이 발생한다.
그래서 React 같은 핵심 라이브러리는 프로젝트에서 직접 관리하도록 peer dependency를 사용한다.
devDependencies는 개발할 때만 필요한 패키지다.
예:
예시:
"devDependencies": {
"typescript": "^5.0.0"
}
이 패키지들은 개발 환경에서만 필요하고 실제 서비스 실행에는 필요하지 않다.
패키지 관리 구조는 다음과 같이 정리할 수 있다.
프로젝트
├ package.json
│ ├ dependencies
│ ├ devDependencies
│ └ peerDependencies
│
├ lockfile
│ └ 실제 설치된 버전 기록
│
└ node_modules
├ 직접 의존성
└ 전이 의존성
1️⃣ package.json → 어떤 패키지를 사용할지 정의
2️⃣ lockfile → 실제 설치된 정확한 버전 기록
3️⃣ node_modules → 실제 패키지가 설치되는 위치
이 개념을 이해하면 패키지 관련 문제를 훨씬 쉽게 해결할 수 있다. 🚀