빠른 흐름
- 작고 단순한 파일:
fs/promises.readFile - 큰 파일: 스트림
- 여러 파일 병렬 읽기:
Promise.all
import { readFile, writeFile, mkdir } from "node:fs/promises";Node의 파일 I/O 카드는 사실상 fs/promises 기준 카드라고 봐도 됩니다.
비동기 I/O
먼저 구분해 둘 기본형은 아래입니다.
- 작은 파일 한 번 읽기:
readFile - 작은 파일 한 번 쓰기:
writeFile - 경로 보장 후 쓰기:
mkdir(..., { recursive: true }) - 독립 작업 병렬화:
Promise.all - 큰 파일/연속 처리: 스트림
왜 비동기가 기본값인가
Node는 메인 이벤트 루프를 오래 잡지 않는 것이 중요합니다. 파일 작업을 기다리는 동안 다른 요청이나 콜백이 계속 돌 수 있어야 하기 때문입니다.
그래서 이 카드의 초점은 "비동기 원리 설명"보다 "메인 루프를 막지 않는 선택"에 있습니다.
병렬 읽기
Promise.all은 병렬 읽기 카드
const [a, b] = await Promise.all([
readFile("a.json", "utf-8"),
readFile("b.json", "utf-8"),
]);서로 독립인 파일이라면 순차 await보다 이 패턴이 기준입니다.
단, 파일 수가 아주 많아지면 무제한 병렬화가 오히려 디스크와 메모리를 흔들 수 있으니 배치나 제한도 같이 생각해야 합니다.
쓰기 준비
디렉터리 보장은 쓰기 흐름에 같이 붙는다
await mkdir("./logs", { recursive: true });
await writeFile("./logs/app.log", "start\n");파일 쓰기 카드는 거의 항상 "경로 보장"과 같이 읽어야 합니다.
fs/promises와 스트림 경계도 같이 본다
readFile()은 편하지만, 큰 파일을 통째로 메모리에 올립니다.
그래서 Promise API 선택보다 "버퍼 한 번에 처리할 크기인가"를 먼저 판단하는 편이 맞습니다.
언제 병렬로 읽나
체크포인트
- 새 코드는
fs/promises - 작은 파일은
readFile - 큰 파일은 stream
- 독립 파일은
Promise.all - 쓰기 전에 경로 보장
- 파일 수가 많으면 병렬 수를 제한할지 검토
주의할 점
비동기 파일 I/O를 쓴다고 해서 설계가 자동으로 좋아지는 것은 아닙니다. 큰 파일을 readFile로 통째로 읽으면 여전히 메모리 압박이 생기고, 없는 디렉터리에 writeFile을 바로 하면 즉시 실패합니다. 결국 핵심은 API가 아니라 사용 패턴입니다.
참고 링크
1 sources