Menu

Node.js fs.readFile로 파일 읽기 (동기, 비동기, Promise)

Node.js에서 파일을 읽으려면 fs.readFile(콜백), fs.readFileSync, fs/promises의 await readFile을 씁니다. 문자열로 받으려면 'utf8'을 넘기고, 파일이 없으면 ENOENT를 처리하세요.

이 페이지에는 실행 가능한 에디터가 있습니다. 편집하고 실행하면 결과를 바로 볼 수 있습니다.

fs.readFile(path, 'utf8', callback)은 파일 전체를 읽어 그 내용을 문자열로 콜백에 넘깁니다. fs.readFileSync(path, 'utf8')은 읽기가 끝날 때까지 기다렸다가 문자열을 바로 반환하고, fs/promises의 await readFile(path, 'utf8')은 같은 일을 블로킹 없이 합니다. 'utf8'을 빼면 텍스트 대신 원시 바이트의 Buffer를 받습니다.

readFile called가 먼저 출력됩니다. readFile은 읽기를 시작만 하고, 내용이 준비되어야 콜백이 실행되기 때문입니다. 'utf8',을 지우고 다시 실행하면 콜백은 파일의 원시 바이트인 <Buffer 42 75 79 20 6d 69 6c 6b 0a 43 61 6c 6c 20 41 64 61>를 받습니다.

readFile vs readFileSync vs fs/promises

셋 다 같은 파일을 읽습니다. 다른 점은 결과를 받는 방법과, 프로그램의 나머지가 기다리느냐입니다.

동기 줄이 먼저 출력됩니다. 그것이 반환될 때까지 다른 것은 실행되지 않기 때문입니다. 나머지 둘은 메인 코드가 끝난 뒤, 읽기가 끝나는 순서대로 완료됩니다.

readFileSyncreadFile(콜백)fs/promises의 readFile
반환값내용undefined내용을 담은 Promise
프로그램을 막음예아니요아니요
에러던져짐, try/catch 사용콜백의 첫 번째 인자reject된 Promise, await와 try/catch
쓰는 곳시작 코드와 스크립트예전 콜백 코드새 코드

시작할 때 설정을 읽거나 짧은 스크립트에서는 readFileSync를 쓰세요. 서버에서는 동기 읽기가 디스크가 일하는 동안 다른 모든 요청을 멈추므로 Promise 버전을 쓰세요. 그 API의 나머지는 fs/promises 페이지에서 다룹니다.

파일이 없을 때 처리하기 (ENOENT)

파일이 없으면 에러에 code: 'ENOENT'("error, no entry")가 붙습니다. err.code를 확인하면 파일이 없는 경우와 권한 문제 같은 다른 실패를 구분할 수 있습니다.

첫 호출은 파일을 찾지 못해 default settings로 대신하고, 두 번째는 dark mode를 읽습니다. 다른 에러를 다시 던져 두면 진짜 문제가 조용히 기본값 뒤에 숨지 않습니다.

fs.existsSync: 그냥 읽어 보는 게 나은 이유

fs.existsSync(path)는 true나 false를 반환하므로, 읽기 전에 확인하고 싶어집니다.

if (fs.existsSync('data.txt')) {
    const text = fs.readFileSync('data.txt', 'utf8');
}

한 단계면 될 일을 두 단계로 하는 것이고, 그 사이에 파일이 지워질 수도 있어서 읽기는 여전히 에러를 던질 수 있습니다. 위처럼 읽기를 시도하고 ENOENT를 처리하세요. existsSync는 무언가가 있는지만 알면 되고 열지는 않을 때, 예를 들어 폴더를 만들지 정할 때 쓰기 좋습니다.

JSON 파일 읽기

텍스트를 읽고 JSON.parse로 파싱합니다. 문법 오류가 있는 파일은 SyntaxError를 던지므로 같은 try 안에 두세요.

3001과 boolean이 출력됩니다. 파싱 후에는 값이 JSON 타입을 가집니다. require('./config.json')도 되지만 결과를 캐시하기 때문에, 파일이 바뀐 뒤에도 두 번째 require는 옛 객체를 반환합니다.

파일을 한 줄씩 읽기

메모리에 들어가는 파일이라면 통째로 읽고 줄바꿈 문자로 나눕니다. 마지막 줄바꿈이 남기는 빈 문자열은 걸러 내세요.

나누면 항목이 4개이고 마지막은 비어 있어서, 반복문은 번호 붙은 세 줄을 출력합니다. 윈도우에서 쓴 파일은 줄 끝이 \r\n이니, 둘 다 처리하려면 /\r?\n/로 나누세요.

몇 기가바이트짜리 로그는 메모리에 들어가지 않습니다. 대신 스트림으로 읽으면 한 번에 한 줄만 메모리에 둡니다.

const fs = require('fs');
const readline = require('readline');

async function main() {
    const rl = readline.createInterface({
        input: fs.createReadStream('big.log'),
        crlfDelay: Infinity,
    });
    for await (const line of rl) {
        console.log(line);
    }
}
main();

흔한 실수

콜백 밖에서 결과를 쓰는 것. const text = fs.readFile(...)은 undefined입니다. 내용은 콜백 안이나 await 뒤에만 있습니다.

인코딩을 잊는 것. 'utf8'이 없으면 Buffer를 받고, data.split('\n')은 data.split is not a function을 던집니다. 텍스트가 필요하면 항상 인코딩을 넘기세요.

엉뚱한 폴더 기준의 상대 경로. 'data.txt'는 node를 시작한 곳에서 찾습니다. 스크립트 옆에 두는 파일에는 path.join(__dirname, 'data.txt')를 쓰세요.

자주 묻는 질문

fs.readFile이 undefined를 반환하는 이유는 뭔가요?

fs.readFile은 내용을 반환하지 않습니다. 읽기를 시작하고, 나중에 (err, data)로 콜백을 호출합니다. 데이터는 콜백 안에서 쓰거나, readFileSync를 호출하거나, Promise 버전을 await하세요.

'data.txt' 같은 상대 경로는 어디서 찾나요?

현재 작업 디렉터리, 즉 node를 실행한 폴더에서 찾습니다. 스크립트가 있는 폴더가 아닙니다. 스크립트 옆의 파일을 읽으려면 path.join(__dirname, 'data.txt')로 경로를 만드세요.

fs.readFile로 URL의 파일을 읽을 수 있나요?

없습니다. 파일 시스템에서만 읽습니다. HTTP로 파일을 받으려면 fetch를 쓴 뒤 await res.text()를 하세요.

readFile은 얼마나 큰 파일까지 다룰 수 있나요?

파일 전체를 메모리에 올리므로 설정 파일, 템플릿, 작은 데이터 파일이라면 괜찮습니다. 수백 메가바이트짜리 파일은 fs.createReadStream으로 조각조각 읽으세요.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기