Cannot find module, 모듈을 찾을 수 없음, node_modules | 지워도 같은 줄이 뜨면 뭐부터 볼까?
Cannot find module은 패키지가 없어서가 아니라 노드가 이름을 찾을 폴더를 지나쳤거나 대소문자가 달라서 난다. node_modules를 지워도 같으면 cwd와 require 이름부터 본다. 크론, 깃허브 액션, 맥과 리눅스 CI, npm ci. 개발자, Node.js, npm, Next.js, 백엔드 기준. 2026년 8월 Node.js·npm 공식 문서.
Cannot find module은 패키지가 없어서가 아니라, 노드가 그 이름을 찾을 폴더를 이미 지나쳤거나 파일 이름 대소문자가 달라서 납니다. node_modules만 지워 다시 깔아도 같은 줄이면, 지금 프로세스의 작업 폴더와 require 경로부터 보세요.
로컬 터미널에선 되고 크론이나 깃허브 액션만 모듈을 못 찾는 일, 1인으로 넥스트 사이트를 돌리다 보면 한 번은 만납니다. 제가 쓰는 발행 스크립트도 저장소 밖에서 node를 부르면 바로 이 줄이 납니다.
NODE_PATH를 늘리기 전에 cwd, 패키지 이름, 맥과 리눅스 대소문자부터 맞추면 대부분 끝납니다. 근거는 Node.js 모듈 폴더 탐색과 에러 코드 문서에 있습니다.
지워도 같은 줄이면 어디를 보나
작업 폴더와 패키지 이름 대소문자부터 맞춥니다. 노드는 현재 디렉터리의 node_modules를 보고, 없으면 한 단계 위 폴더로 올라가며 같은 이름을 찾습니다. 그 사다리를 다 올라갔는데도 없으면 MODULE_NOT_FOUND 또는 Cannot find module을 던집니다.
그래서 rm -rf node_modules && npm ci가 만능이 아닙니다. 설치는 저장소 루트에 됐는데 실행은 홈 폴더나 scripts/ 안에서 하면, 그 cwd 기준으로는 패키지가 안 보입니다. 메시지에 찍힌 경로가 /Users/you/프로젝트인지, 홈인지부터 읽으세요.
패키지 이름 자체도 자주 갈립니다. 저장소에 적힌 이름과 코드의 불러오기 이름이 한 글자라도 다르면 디스크에 파일이 있어도 못 찾습니다. 한국에서 맥으로 짜고 리눅스 통합 환경에서만 깨지는 경우는 거의 대소문자입니다. 혼자 돌리는 새벽 작업도 저장소 밖에서 실행하면 같은 증상이 납니다.
먼저 기억할 것 | 지워 다시 깔기 전에 process.cwd()와 에러에 찍힌 모듈 이름을 나란히 보세요. 설치 위치와 실행 위치가 다르면 같은 줄이 반복됩니다.
노드는 현재 폴더의 node_modules부터 루트까지 올라가며 이름을 찾는다
먼저 확인할 세 칸
cwd, 모듈 이름, 대소문자 세 칸이면 원인이 갈립니다. 표로 먼저 나눕니다.
칸
증상
먼저 할 일
작업 폴더
로컬은 되고 크론·액션만 실패
에러 경로와 pwd를 비교, cd 후 실행
패키지 이름
방금 넣은 패키지인데 못 찾음
package.json dependencies 철자, 스코프 @
대소문자
맥은 되고 리눅스 CI만 실패
import 경로와 실제 파일명 일치
락파일
ci에서만 없음
npm ci는 lock 기준, 로컬 npm install과 다름
번들러 alias
앱은 되고 노드 스크립트만 실패
tsconfig paths는 런타임이 모름
다섯 번째 칸은 별글입니다. @/ 별칭이 넥스트 빌드에선 되고 node scripts/foo.js에선 안 되는 경우는 타입스크립트 경로 설정 문제입니다. 여기서는 순수 노드가 node_modules에서 이름을 못 찾는 줄만 봅니다.
락파일 칸은 피어 의존성 충돌과 자리가 다릅니다. ERESOLVE는 트리를 못 만들고, Cannot find module은 트리는 있는데 실행 순간에 파일을 못 여는 쪽입니다.
cwd와 모듈 이름을 같이 찍기
node -e "console.log('cwd', process.cwd())"
ls node_modules/패키지이름
# 에러에 찍힌 이름과 ls 결과가 다르면 철자·대소문자부터
실행 위치를 맞춘 다음 다시 깔기
먼저 저장소 루트에서 실행하고, 그래도 없으면 그때 지워 깔습니다. npm 스크립트는 package.json이 있는 폴더를 cwd로 잡습니다. 셸에서 node scripts/publish.js를 홈에서 치면 cwd가 홈이라 루트의 node_modules를 못 봅니다.
크론·launchd는 특히 그렇습니다. plist나 crontab에 작업 디렉터리를 안 넣으면 홈에서 시작합니다. 절대 경로로 스크립트를 불러도 cwd는 홈입니다. cd /path/to/repo && node scripts/foo.js처럼 디렉터리를 고정하세요.
그다음에야 설치를 다시 합니다. npm ci는 lock이 있어야 하고, lock과 package.json이 어긋나면 설치 자체가 실패합니다. 로컬에서 npm install만 하다가 CI에서 npm ci를 쓰면, 빠진 패키지가 여기 에러로 드러납니다.
루트에서 설치하고 루트에서 실행
cd /path/to/repo
pwd
npm ci
node scripts/foo.js
# launchd/cron 예: /bin/bash -lc 'cd /path/to/repo && node scripts/foo.js'
[ ] 에러 메시지에 찍힌 모듈 이름을 그대로 복사했다
[ ] process.cwd()가 저장소 루트인지 확인했다
[ ] ls node_modules/그이름 으로 디스크에 있는지 봤다
[ ] 맥/리눅스면 import 경로 대소문자를 파일명과 맞춰 봤다
[ ] cwd를 고친 뒤에야 npm ci를 다시 돌렸다
크론과 액션은 홈에서 시작할 수 있다. cd 후 실행이 기본이다
맥에선 되고 리눅스만 실패할 때
파일명 대소문자가 import와 다르면 리눅스에서만 납니다. 맥 기본 디스크는 대소문자를 구분하지 않아 Utils.js를 utils.js로 불러도 열립니다. 깃허브 액션 우분투는 구분해서 같은 코드가 Cannot find module이 됩니다.
고치는 법은 디스크의 실제 이름에 import를 맞추는 것입니다. 파일을 바꿔 커밋할 때는 깃이 대소문자만 바꾼 변경을 무시할 수 있습니다. git mv로 한 번 다른 이름에 옮겼다가 올바른 이름으로 되돌리면 추적됩니다.
모노레포라면 워크스페이스 패키지 이름과 폴더 이름까지 보세요. packages/App인데 require('app')이면 로컬 링크가 안 붙습니다. 워크스페이스 프로토콜 설정은 패키지 매니저 문서를 따릅니다.
NODE_PATH는 마지막 | 환경변수로 경로를 늘리면 cwd 버그가 숨습니다. 공식 모듈 탐색은 node_modules 사다리가 기본입니다. 경로를 우회하기 전에 실행 위치부터 고치세요.
ENOENT와 같은 줄이 아니다
파일을 못 여는 줄과 모듈을 못 찾는 줄은 원인 칸이 다릅니다. ENOENT는 경로 문자열 자체가 디스크에 없을 때입니다. Cannot find module은 모듈 해석기가 이름을 패키지로 풀어 node_modules 사다리에서 실패한 때입니다.
스크립트 경로 오타, fs.readFile('data.json')의 상대경로, 도커에서 마운트가 빠진 파일은 ENOENT 글에서 다룹니다. 여기서는 require('foo') / import 'foo'가 패키지 폴더를 못 찾는 경우만 봅니다.
포트가 막혀 서버가 안 뜨는 줄은 EADDRINUSE입니다. 모듈 에러와 동시에 보이면, 대개 서버 파일이 의존성을 못 읽어 바인딩 전에 죽은 겁니다. 모듈 줄부터 없애세요.