🔍 검색 키워드: Node.js ENOENT 해결, ENOENT no such file or directory, Node.js 파일 경로 에러, __dirname 경로 에러, fs.readFile ENOENT, Node.js path 모듈
Node.js 개발하다 보면 파일 읽기/쓰기 코드에서 자주 만나는 에러다. 로컬에서는 잘 되는데 서버에 올리면 갑자기 죽는 경우도 이 에러 때문인 경우가 많다.
Error: ENOENT: no such file or directory, open '/app/config/settings.json'
at Object.openSync (fs.js:476:3)
at Object.readFileSync (fs.js:377:35)
ENOENT는 Error NO ENTry의 약자다. 운영체제가 해당 경로에서 파일을 찾을 수 없다는 뜻이다.
증상
fs.readFile,fs.readFileSync,fs.writeFile등 파일 시스템 작업에서 에러- 로컬에서는 잘 동작하는데 서버/Docker에서는 에러 발생
require('./config')또는import구문에서 모듈을 찾지 못하는 에러- 상대 경로로 파일을 읽는데 실행 위치에 따라 동작이 달라짐
원인
원인 1: 상대 경로 기준점 오해
Node.js에서 상대 경로는 프로세스를 실행한 디렉토리(CWD)를 기준으로 한다. 파일이 있는 디렉토리가 아니다.
# 프로젝트 구조
/project/
src/
utils/
fileReader.js ← 여기서 './config.json' 읽으려 함
config.json
# 이렇게 실행하면
cd /project/src/utils
node fileReader.js
# './config.json'은 /project/src/utils/config.json을 찾음 → 없음 → ENOENT
원인 2: __dirname vs process.cwd() 혼용
// process.cwd() → 프로세스 실행 위치 (실행할 때마다 달라질 수 있음)
// __dirname → 현재 파일이 있는 디렉토리 (항상 고정)
// ❌ CWD 기준 — 실행 위치에 따라 경로가 달라짐
fs.readFileSync('./config.json');
// ✅ __dirname 기준 — 항상 이 파일 기준으로 경로 계산
fs.readFileSync(path.join(__dirname, 'config.json'));
해결방법
방법 1: __dirname + path.join() 사용 (CJS)
const fs = require('fs');
const path = require('path');
// ✅ 항상 이 파일 위치 기준으로 경로 계산
const configPath = path.join(__dirname, 'config', 'settings.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
// 상위 디렉토리 올라갈 때
const rootConfigPath = path.join(__dirname, '..', '..', 'config.json');
방법 2: ES Modules에서 __dirname 대체
// ESM에서는 __dirname이 없음 — 이렇게 구현
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { readFileSync } from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const config = JSON.parse(
readFileSync(join(__dirname, 'config.json'), 'utf-8')
);
방법 3: 파일 존재 여부 사전 확인 + 에러 처리
try {
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
return config;
} catch (err) {
if (err.code === 'ENOENT') {
console.error(`파일을 찾을 수 없습니다: ${configPath}`);
return defaultConfig;
}
throw err; // ENOENT가 아닌 다른 에러는 다시 던짐
}
방법 4: 파일 쓰기 전 디렉토리 자동 생성
function writeFileWithDir(filePath, data) {
const dir = path.dirname(filePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(filePath, data, 'utf-8');
}
writeFileWithDir(
path.join(__dirname, 'output', 'reports', '2026-07-01.json'),
JSON.stringify(data, null, 2)
);
Docker 환경에서의 ENOENT
# ✅ Dockerfile에서 필요한 디렉토리 미리 생성
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN mkdir -p /app/uploads /app/logs /app/tmp
EXPOSE 3000
CMD ["node", "server.js"]
// ✅ 앱 시작 시 디렉토리 보장
const dirs = ['uploads', 'logs', 'tmp'].map(d => path.join(__dirname, d));
dirs.forEach(dir => fs.mkdirSync(dir, { recursive: true }));
디버깅 방법
// 어느 경로를 보고 있는지 출력
console.log('CWD:', process.cwd());
console.log('__dirname:', __dirname);
console.log('찾는 경로:', path.join(__dirname, 'config.json'));
console.log('파일 존재 여부:', fs.existsSync(path.join(__dirname, 'config.json')));
점검 체크리스트
| 상황 | 확인 항목 |
|---|---|
| 상대 경로 쓸 때 | path.join(__dirname, '...') 형식인지 |
| ES Modules | import.meta.url로 __dirname 재구현했는지 |
| Docker 배포 | 필요한 디렉토리가 이미지 안에 있는지 |
| 파일 쓰기 전 | 상위 디렉토리 존재하는지 확인 |
| 에러 처리 | err.code === 'ENOENT' 분기 처리했는지 |
| 디버깅 시 | process.cwd()와 __dirname 출력해서 확인 |
정리
ENOENT 에러는 대부분 두 가지 중 하나다. 경로 기준점을 잘못 잡았거나, 파일이 실제로 없는 것이다.
코드 레벨에서는 path.join(__dirname, ...) 패턴을 습관으로 만들면 실행 위치에 무관하게 안정적으로 동작한다. 배포 환경 이슈라면 Docker 이미지에 필요한 디렉토리가 포함됐는지 확인하고, 앱 시작 시 자동으로 디렉토리를 생성하는 코드를 추가하는 게 가장 확실한 방법이다.
댓글 없음:
댓글 쓰기