dependencies와 devDependencies 차이
Node 프로젝트를 처음 다루면 package.json 안의 dependencies와 devDependencies가 헷갈린다.
둘 다 패키지 목록처럼 보이는데, 어떤 패키지는 위에 있고 어떤 패키지는 아래에 있다.
명령어도 npm install react처럼 쓰는 경우가 있고, npm install -D eslint처럼 -D를 붙이는 경우가 있다.
핵심은 간단하다.dependencies는 애플리케이션 실행에 필요한 패키지다.devDependencies는 개발, 빌드, 테스트처럼 개발 과정에 필요한 패키지다.
dependencies의 역할
dependencies에는 앱이 실행될 때 필요한 패키지를 넣는다.
코드가 런타임에서 직접 import하거나, 배포된 앱이 동작하기 위해 필요한 패키지가 여기에 들어간다.
예를 들어 React 앱이라면 react와 react-dom은 보통 dependencies에 들어간다.
서버 앱이라면 express, zod, 데이터베이스 클라이언트 같은 패키지도 실행에 필요할 수 있다.
{
"dependencies": {
"react": "19.2.5",
"react-dom": "19.2.5",
"zod": "4.3.6"
}
}npm install react처럼 별도 옵션 없이 설치하면 npm은 기본적으로 패키지를 dependencies에 저장한다.
예전에는 --save 옵션을 명시하는 예시도 많았지만, 현재 npm에서는 기본 동작으로 생각하면 된다.
devDependencies의 역할
devDependencies에는 개발할 때 필요한 패키지를 넣는다.
애플리케이션 실행 자체에는 필요하지 않지만, 코드를 만들고 검사하고 빌드하는 과정에 필요한 도구가 여기에 들어간다.
대표적인 예시는 TypeScript, ESLint, Prettier, Vitest, Playwright 같은 패키지다.
이 패키지들은 개발자가 코드를 작성하거나 CI에서 테스트를 돌릴 때 필요하지만, 배포된 런타임 코드가 직접 import하지 않는 경우가 많다.
{
"devDependencies": {
"typescript": "6.0.3",
"eslint": "9.39.4",
"prettier": "3.8.3",
"vitest": "4.1.5"
}
}개발용 패키지는 -D 또는 --save-dev 옵션으로 설치한다.
npm install -D eslint
npm install --save-dev prettier두 명령어는 같은 의도로 사용한다.
짧게 쓰고 싶으면 -D, 명확하게 쓰고 싶으면 --save-dev를 쓰면 된다.
npm install과 npm install -D의 차이
npm install과 npm install -D의 차이는 설치 위치다.
둘 다 node_modules에 패키지를 설치하고 lock 파일을 갱신할 수 있다.
다만 package.json에 기록되는 위치가 다르다.
npm install axios이 명령어는 axios를 dependencies에 추가한다.
npm install -D eslint이 명령어는 eslint를 devDependencies에 추가한다.
설치 후에는 package.json을 확인하는 습관이 좋다.
실행에 필요한 패키지를 실수로 devDependencies에 넣으면 배포 환경에서 빠질 수 있다.
반대로 개발 도구를 dependencies에 넣으면 프로덕션 설치가 불필요하게 무거워질 수 있다.
배포 환경에서 설치되는 의존성
배포 환경에서는 개발용 의존성을 설치하지 않도록 설정하는 경우가 있다.
npm에서는 --omit=dev 옵션을 사용하면 devDependencies를 디스크 설치에서 제외할 수 있다.
production 설정에서도 dev 의존성을 제외하는 흐름이 사용될 수 있다.
npm ci --omit=dev이 명령어는 lock 파일을 기준으로 설치하되, 개발용 의존성은 실제 node_modules에 설치하지 않는다.
CI나 배포에서 npm ci를 사용하는 이유는 npm ci란?에 따로 정리해두었다.
여기서 주의할 점이 있다.
Next.js, Vite, TypeScript처럼 빌드가 필요한 프로젝트는 배포 전에 빌드 도구가 필요할 수 있다.
빌드 단계에서는 devDependencies가 필요하고, 빌드가 끝난 런타임 단계에서는 필요 없을 수 있다.
그래서 Docker나 서버 배포에서는 빌드 단계와 실행 단계를 나눠서 의존성을 설치하기도 한다.
헷갈리기 쉬운 패키지 예시
프레임워크 패키지는 프로젝트 방식에 따라 판단이 달라질 수 있다.next는 빌드 도구처럼 보이지만, Next.js 앱을 실행할 때도 필요할 수 있어서 보통 dependencies에 둔다.
반면 eslint-config-next는 lint 설정에 필요하므로 devDependencies에 두는 경우가 자연스럽다.
타입 패키지도 자주 헷갈린다.@types/node, @types/react 같은 패키지는 TypeScript가 타입 검사를 할 때 쓰므로 보통 devDependencies에 둔다.
실행 중인 JavaScript 코드가 타입 패키지를 import하는 것은 아니다.
테스트 도구도 대부분 devDependencies에 둔다.vitest, jest, playwright, @testing-library/react는 앱 실행보다 검증 과정에 가깝다.
package-lock.json과의 관계
dependencies와 devDependencies는 package.json에 사람이 읽기 좋은 의존성 선언으로 저장된다.
반면 package-lock.json은 실제로 설치될 전체 의존성 트리를 더 자세히 기록한다.
직접 설치한 패키지뿐 아니라, 그 패키지가 다시 의존하는 하위 패키지까지 포함된다.
그래서 개발용 패키지를 설치해도 package-lock.json은 함께 바뀔 수 있다.
이것은 정상이다.
lock 파일은 설치 결과를 재현하기 위한 파일이기 때문이다.
lock 파일의 역할은 package-lock.json 파일의 역할에 따로 정리해두었다.
.npmrc 설정도 설치 결과에 영향을 줄 수 있다.
registry, peer dependency 처리 방식, install 옵션을 프로젝트에서 고정해야 한다면 .npmrc 파일이란?도 같이 보면 좋다.
정리
dependencies는 실행에 필요한 패키지다.devDependencies는 개발, 빌드, 테스트에 필요한 패키지다.
npm install 패키지명은 기본적으로 dependencies에 저장한다.npm install -D 패키지명은 devDependencies에 저장한다.
패키지를 어디에 둘지 헷갈리면 한 가지 질문을 해보면 된다.
이 패키지가 배포된 앱이 실행되는 순간에도 필요한가.
필요하다면 dependencies에 가깝고, 코드 작성이나 검증 과정에서만 필요하다면 devDependencies에 가깝다.
참고 자료







