Node.js 파일 읽기 fs.readFile()

fs.readFile() 메소드는 Node.js에서 파일을 비동기적으로 읽을 때 사용하는 함수입니다.

파일을 열고 내용을 읽은 뒤, 콜백 함수를 통해 결과를 전달합니다.

이 함수는 파일을 완전히 읽은 후(전체 파일을 메모리에 로드)에 콜백 함수를 호출하기 때문에, 파일을 스트리밍하지는 않습니다.

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 등 명확히 지정하는 것이 좋습니다.

관련 글

Node.js 튜토리얼