현록

npx와 npm exec 차이

Node 프로젝트를 따라 하다 보면 npx 명령어를 자주 만난다.
예를 들어 Next.js 프로젝트를 만들 때 npx create-next-app@latest를 실행하라는 안내가 나온다.
ESLint나 Prettier 같은 도구를 한 번만 실행할 때도 npx eslint . 같은 예시를 볼 수 있다.

npx는 npm 패키지가 제공하는 실행 파일을 실행하는 명령어다.
현재 프로젝트에 설치된 패키지를 실행할 수도 있고, 설치되어 있지 않은 패키지를 npm 캐시에 받아서 실행할 수도 있다.

npx의 역할

npx는 패키지 안의 CLI를 실행할 때 사용한다.
CLI는 터미널에서 실행할 수 있는 명령어를 뜻한다.
패키지가 bin 필드를 통해 실행 파일을 제공하면, npx가 그 실행 파일을 찾아서 실행한다.

대표적인 예시는 프로젝트 생성 명령어다.

npx create-next-app@latest my-app

이 명령어는 create-next-app 패키지의 실행 파일을 실행한다.
패키지가 현재 프로젝트에 설치되어 있지 않다면, npm은 필요한 패키지를 캐시 영역에 받아서 실행할 수 있다.

한 번만 쓰는 도구를 전역으로 설치하지 않아도 된다는 점이 npx의 가장 큰 장점이다.
예전에는 npm install -g create-react-app처럼 전역 설치를 먼저 하고 실행하는 흐름이 많았다.
지금은 프로젝트 생성이나 일회성 점검 명령어를 npx로 바로 실행하는 예시가 더 익숙하다.

로컬 패키지 실행

npx는 먼저 현재 프로젝트에 설치된 실행 파일을 사용할 수 있다.
예를 들어 프로젝트에 eslint가 설치되어 있다면 아래 명령어로 실행할 수 있다.

npx eslint .

이 경우 프로젝트의 node_modules/.bin에 연결된 eslint를 실행한다고 생각하면 된다.
전역에 설치된 eslint 버전에 기대지 않고, 현재 프로젝트가 쓰는 버전을 실행할 수 있다.

자주 쓰는 명령어라면 npx보다 package.jsonscripts에 넣는 편이 더 좋다.

{
  "scripts": {
    "lint": "eslint ."
  }
}
npm run lint

반복해서 쓰는 팀 명령어는 scripts에 두고, 한 번만 실행하거나 문서 예시를 따라 할 때는 npx를 쓰는 식으로 나누면 편하다.
scriptsnpm run의 흐름은 package.json scripts와 npm run 정리에 따로 정리해두었다.

원격 패키지 임시 실행

현재 프로젝트에 없는 패키지도 npx로 실행할 수 있다.
npm 공식 문서 기준으로 요청한 패키지가 로컬 의존성에 없으면, npm은 확인 프롬프트를 띄운 뒤 패키지를 npm 캐시 폴더에 설치하고 실행할 수 있다.

npx cowsay hello

이런 방식은 작은 CLI를 잠깐 확인할 때 편하다.
다만 실행할 패키지를 네트워크에서 받아오는 일이므로 아무 패키지나 습관적으로 실행하는 것은 좋지 않다.
패키지 이름, 다운로드 수, 저장소, 문서, 유지보수 상태를 확인하는 편이 안전하다.

버전을 고정하는 습관도 좋다.

npx create-next-app@latest my-app
npx prettier@3.8.3 --version

latest는 항상 같은 버전을 뜻하지 않는다.
문서 예시를 재현해야 하거나 CI에서 실행해야 한다면 구체적인 버전을 쓰는 편이 더 예측 가능하다.

npm exec의 역할

npm exec도 npm 패키지의 실행 파일을 실행하는 명령어다.
npm v7부터 npx는 별도의 실행 흐름이라기보다 npm exec를 기반으로 동작한다.
그래서 최신 npm 기준으로는 npxnpm exec가 같은 목적을 가진 명령어라고 이해하면 된다.

아래 두 명령어는 비슷한 상황에서 사용할 수 있다.

npx eslint .
npm exec -- eslint .

npm exec에는 짧은 alias로 npm x도 있다.

npm x -- eslint .

팀 문서에서는 익숙한 npx를 쓰는 경우가 많고, npm CLI 동작을 명확히 설명해야 할 때는 npm exec가 더 직접적이다.

npx와 npm exec의 인자 차이

초보자가 가장 헷갈리는 부분은 옵션 위치다.
npm 문서 기준으로 npx에서는 npx 옵션을 positional argument 앞에 둬야 한다.
반면 npm exec에서는 --를 사용해서 npm의 옵션 해석을 멈추고, 뒤의 값을 실행되는 명령어에 넘길 수 있다.

예를 들어 foo라는 명령어에 --package라는 인자를 넘기고 싶은 상황을 생각해보자.

npx foo@latest bar --package=@example/foo

npx에서는 패키지 이름 뒤에 온 --package가 실행되는 foo 명령어의 인자로 처리될 수 있다.

npm exec에서는 아래처럼 --를 넣어 의도를 명확히 할 수 있다.

npm exec -- foo@latest bar --package=@example/foo

일반적인 사용에서는 이 차이를 깊게 외울 필요는 없다.
다만 npm exec를 쓸 때는 npm exec -- 명령어 인자 형태가 안전하다고 기억하면 된다.

--package 옵션

실행할 명령어 이름과 패키지 이름이 다르거나, 여러 패키지가 필요한 환경에서 실행해야 할 때는 --package 옵션을 쓸 수 있다.

npx --package=typescript tsc --version

위 명령어는 typescript 패키지를 사용할 수 있는 환경에서 tsc --version을 실행한다.
npm exec로 쓰면 아래처럼 표현할 수 있다.

npm exec --package=typescript -- tsc --version

단순히 패키지 이름과 실행 파일 이름이 같은 경우에는 --package를 몰라도 된다.
하지만 패키지가 여러 실행 파일을 제공하거나, 실행할 이름을 npm이 추론하기 어려운 경우에는 --package가 더 명확하다.

CI에서 주의할 점

CI에서는 npx가 패키지를 새로 받아 실행하는 흐름이 생각보다 위험할 수 있다.
네트워크 상태에 영향을 받고, 버전을 고정하지 않으면 실행 결과가 달라질 수 있기 때문이다.

반복적으로 쓰는 도구라면 프로젝트의 devDependencies에 추가하고 npm ci로 설치한 뒤 npm run으로 실행하는 편이 안정적이다.

npm install -D eslint
npm run lint

dependenciesdevDependencies를 나누는 기준은 dependencies와 devDependencies 차이에 정리해두었다.
CI에서 npm ci를 쓰는 이유는 npm ci란?과도 연결된다.

정리

npx는 npm 패키지가 제공하는 실행 파일을 실행하는 명령어다.
로컬에 설치된 패키지를 실행할 수도 있고, 설치되어 있지 않은 패키지를 캐시에 받아서 실행할 수도 있다.

npm exec도 같은 목적의 명령어다.
npm v7 이후의 npxnpm exec를 기반으로 동작한다.

한 번만 실행하는 명령어는 npx가 편하다.
프로젝트에서 반복해서 쓰는 명령어는 devDependencies에 설치하고 scripts에 등록하는 편이 더 안정적이다.
CI에서는 버전을 고정하고, 네트워크에서 즉석으로 받아 실행하는 흐름을 줄이는 편이 좋다.

참고 자료

관련 포스트
package.json scripts와 npm run 정리 thumbnail
package.json scripts와 npm run 정리
package.json의 scripts와 npm run 명령어를 초보자 기준으로 정리합니다. npm run dev, npm start, npm test, node_modules/.bin, -- 인자 전달 방식까지 함께 봅니다.
dependencies와 devDependencies 차이 thumbnail
dependencies와 devDependencies 차이
package.json의 dependencies와 devDependencies 차이를 초보자 기준으로 정리합니다. npm install과 npm install -D, 배포 환경 설치, package-lock.json과의 관계까지 함께 봅니다.
npm ci란? thumbnail
npm ci란?
npm ci는 CI, 테스트, 배포처럼 같은 의존성 트리를 반복해서 설치해야 하는 환경에 어울리는 clean install 명령어입니다. package-lock.json 조건, npm install과의 차이, .npmrc 플래그 주의점을 정리합니다.
.npmrc 파일이란? thumbnail
.npmrc 파일이란?
storybook을 사용해보려고 하다 마주한 이슈의 해결법을 알아보다가 등장한 .npmrc라는 파일에 대해 공부해보았다. .npmrc 파일이란? .npmrc 파일은 npm에 대한 config 파일이다. (npm에 대한 rc 파일) 프로젝트별 registry, install 옵션, 인증 토큰처럼 npm CLI가 읽는 설정을 관리할 때 사용한다.
스토리북이란? thumbnail
스토리북이란?
Storybook은 UI 컴포넌트를 독립적으로 개발하고, 문서화하고, 테스트하기 위한 프론트엔드 워크샵입니다. Storybook 10.4 기준 설치 흐름과 stories, 문서화, 테스트 활용 방식을 정리합니다.
TTV와 TTI란? thumbnail
TTV와 TTI란?
TTV는 Time to View이며, 사용자가 화면을 보는 시점을 의미한다. TTI는 Time to Interact이며, 사용자가 웹에서 특정 동작을 수행할 수 있는 시점을 의미한다.
Maria DB 외부 접속 설정하기 thumbnail
Maria DB 외부 접속 설정하기
안녕하세요. 오늘은 Maria DB 초기 세팅 시, 외부에서 접속이 안될 때 매뉴얼을 작성해보겠습니다. dotenv 패키지를 통해서 환경변수로 관리한다면, 별도의 추가 작업을 할 일이 없으실 겁니다.
package-lock.json 파일의 역할 thumbnail
package-lock.json 파일의 역할
안녕하세요. 오늘은 node 환경의 개발자라면 한번쯤 궁금했을만한 package-lock.json의 역할에 대해 알아보겠습니다. 우리는 node 환경에서 개발을 할 때 다양한 패키지들을 설치하여 활용하곤 합니다. 우리가 설치하는 패키지 또한 다른 npm 패키지를 활용하여 만든 패키지들이고 이들 또한 설치를 하게 됩니다. 이렇게 직간접적으로 설치된 패키지들은 대부분 호환성을 "^1.1.5"와 같이 표현하여, 범위로 지정해두고 있습니다.
Linux 환경 배포 자동화 체험해보기 thumbnail
Linux 환경 배포 자동화 체험해보기
안녕하세요. 요즘 포트폴리오를 만들면서 서버 상에 자주 반영할 일이 생겼는데, 매번 명령어들을 타이핑하는 것이 비효율적이라 생각이 들어 배포 자동화를 생각해보게 되었습니다. 현재 레벨에서는 단순히 명령어들만 단축시켜도 효율적이라 생각이 들어 간단한 쉘 스크립트만 작성하였습니다. 정말 간단하니 여러분도 도전해보시길 바랍니다.
협업 필수품. Prettier thumbnail
협업 필수품. Prettier
안녕하세요. 오늘은 Prettier이라는 도구에 대해 알려드리고자 합니다. 개발자는 각자의 코딩스타일이 존재합니다. 그러다보니 같은 프로젝트에서도 작성하는 소스마다 스타일이 제각기 다르기 일쑤입니다. 그럴 때 도입하면 좋은 것이 Prettier입니다. 프로젝트 root 폴더에 .prettierrc 라는 파일을 생성한 뒤, 위 예시와 같이 원하는 옵션을 JSON 형식으로 작성해주면 됩니다.