fs.readFile() 메소드는 Node.js에서 파일을 비동기적으로 읽을 때 사용하는 함수입니다.
파일을 열고 내용을 읽은 뒤, 콜백 함수를 통해 결과를 전달합니다.
이 함수는 파일을 완전히 읽은 후(전체 파일을 메모리에 로드)에 콜백 함수를 호출하기 때문에, 파일을 스트리밍하지는 않습니다.
Table of Contents
readFile 기본 문법
const fs = require('fs');
fs.readFile(path[, options], callback);
- path: 읽을 파일의 경로 (string, Buffer, URL, file descriptor)
- options (옵셔널):
- encoding: ‘utf8’, ‘ascii’, ‘base64’ 등
- flag: 기본값 ‘r’ (읽기 모드)
- callback: function(err, data) 형태
readFile 사용 방법
const fs = require('fs');
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('파일 읽기 오류:', err);
return;
}
console.log('파일 내용:', data);
});
위 예제는 example.txt 파일을 utf8 인코딩으로 읽어오는 코드입니다.
파일을 모두 읽은 후 콜백 함수를 통해 작업 결과와 파일 내용을 받을 수 있습니다.
여기서 인코딩을 지정하지 않으면 메소드는 Buffer 객체를 리턴합니다.
Buffer vs String
- Buffer는 바이너리 데이터를 표현할 수 있기 때문에 이미지, 동영상, 압축 파일 등을 읽을 때 적합합니다.
- 일반 텍스트 파일을 다룰 경우에는 “utf8” 등을 명시해 문자열로 읽는 것이 편리합니다.
- 만약 일반 텍스트 파일을 Buffer로 읽은 경우 data.toString(‘utf8’) 같은 구문을 사용해 일반 문자열로 변환할 수 있습니다.
다음은 파일의 내용을 Buffer로 읽는 예제입니다:
const fs = require('fs');
fs.readFile('example.txt', (err, data) => {
if (err) {
throw err;
}
console.log('버퍼:', data);
console.log('문자열로 변환:', data.toString('utf8'));
});
readFile 옵션
fs.readFile('example.txt', { encoding: 'utf8', flag: 'r' }, (err, data) => {
console.log(data);
});
옵션 flag의 기본 값은 “r” 입니다.
다음은 자주 사용하는 flag 값 설명입니다:
- “r”: 읽기(기본값)
- “r+”: 읽고 쓰기
- “w”: 쓰기(파일이 없으면 생성, 있으면 덮어쓰기)
- “a”: 추가(append) 모드
readFile 예제
예제 1: JSON 파일 읽기, 파싱
const fs = require('fs');
fs.readFile('data.json', 'utf8', (err, jsonData) => {
if (err) throw err;
const obj = JSON.parse(jsonData);
console.log('JSON 객체:', obj);
});
JSON 파일에는 객체의 데이터가 문자열로 저장되어 있습니다.
이를 utf8 인코딩으로 읽어서 JSON.parse() 메소드를 사용해 객체로 만들 수 있습니다.
예제 2: 존재하지 않는 파일 처리
fs.readFile('nofile.txt', 'utf8', (err, data) => {
if (err) {
if (err.code === 'ENOENT') {
console.error('파일이 존재하지 않습니다.');
} else {
console.error('오류 발생:', err);
}
return;
}
console.log(data);
});
파일이 존재하지 않는 경우 err 객체의 code 값이 ENOENT(error no entry)가 됩니다.
다른 상황으로 파일 읽기 권한이 없는 경우 code 값은 EACCES가 됩니다.
예제 3: 이미지 파일 읽기
fs.readFile('image.jpg', (err, data) => {
if (err) {
throw err;
}
console.log('이미지 크기:', data.length, 'bytes');
});
이미지같은 바이너리 파일을 읽을 경우 encoding을 지정하지 않고 Buffer로 결과를 얻을 수 있습니다.
예제 4: 동기 방식으로 읽기
const fs = require('fs');
try {
const data = fs.readFileSync('example.txt', 'utf8');
console.log('동기 파일 내용:', data);
} catch (err) {
console.error('에러:', err);
}
동기 방식으로 파일을 읽고 싶다면 fs.readFileSync() 메소드를 사용할 수 있습니다.
이는 콜백 함수를 사용하지 않으므로 try/catch 구문을 통해 에러를 확인해야 합니다.
주의할 점으로 이 방식은 파일을 읽는 동안 Node.js의 이벤트 루프가 멈추기 때문에 간단한 스크립트에선 편하지만, 서버 애플리케이션에서는 비동기 방식을 권장합니다.
예제 5: async/await + promises 버전
const fs = require('fs').promises;
async function readFileAsync() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log('비동기 파일 내용:', data);
} catch (err) {
console.error('에러 발생:', err);
}
}
readFileAsync();
비동기 방식의 장점은 취하고 콜백 함수의 복잡함을 피하고 싶다면 async/await 구문을 통해 코드 진행을 단순화할 수 있습니다.
주의 사항
이 메소드는 스트리밍을 하지않고 파일의 모든 내용을 메모리에 로드하므로 대용량 파일에는 부적합합니다.
따라서 100MB 이상의 파일을 읽는 것은 권장하지 않습니다.
하지만 대용량의 파일을 읽어야 한다면 fs.createReadStream() 메소드를 사용하여 스트리밍 방식으로 읽을 수 있습니다.
const fs = require('fs');
const readStream = fs.createReadStream('bigfile.txt', { encoding: 'utf8' });
readStream.on('data', chunk => {
console.log('읽은 조각:', chunk);
});
readStream.on('end', () => {
console.log('파일 읽기 완료');
});
정리
- readFile은 파일을 한 번에 메모리에 로드하기 때문에 대용량 파일의 경우 fs.createReadStream()을 사용하는 것이 효율적입니다.
- 파일이 없는 경우 ENOENT 오류 코드로 잡을 수 있습니다.
- 파일 인코딩이 잘못된 경우 내용이 깨질 수 있으므로 utf8 등 명확히 지정하는 것이 좋습니다.