Node.js 인터뷰 질문 16
질문: Node.js에서 파일을 읽고 쓰는 방법은 무엇인가요?
답변:
Node.js에서는 파일을 읽고 쓰기 위한 여러 가지 방법을 제공합니다. 주로 fs
모듈을 사용하며, 이 모듈은 동기식, 비동기식, 프로미스 기반, 스트림 기반 등 다양한 방식을 지원합니다.
1. 비동기식 방법 (콜백 기반)
const fs = require("fs");
// 파일 읽기
fs.readFile("input.txt", "utf8", (err, data) => {
if (err) {
console.error("파일 읽기 오류:", err);
return;
}
console.log("파일 내용:", data);
});
// 파일 쓰기
fs.writeFile("output.txt", "안녕하세요, Node.js!", "utf8", (err) => {
if (err) {
console.error("파일 쓰기 오류:", err);
return;
}
console.log("파일 쓰기 완료");
});
// 파일에 내용 추가
fs.appendFile("log.txt", "새로운 로그 항목\n", (err) => {
if (err) {
console.error("파일 추가 오류:", err);
return;
}
console.log("파일 추가 완료");
});
2. 동기식 방법
const fs = require("fs");
try {
// 파일 읽기
const data = fs.readFileSync("input.txt", "utf8");
console.log("파일 내용:", data);
// 파일 쓰기
fs.writeFileSync("output.txt", "안녕하세요, Node.js!", "utf8");
console.log("파일 쓰기 완료");
// 파일에 내용 추가
fs.appendFileSync("log.txt", "새로운 로그 항목\n");
console.log("파일 추가 완료");
} catch (err) {
console.error("파일 작업 오류:", err);
}
3. 프로미스 기반 방법 (Node.js 10 이상)
const fs = require("fs").promises;
// 파일 읽기
fs.readFile("input.txt", "utf8")
.then((data) => {
console.log("파일 내용:", data);
// 파일 쓰기
return fs.writeFile("output.txt", "안녕하세요, Node.js!", "utf8");
})
.then(() => {
console.log("파일 쓰기 완료");
// 파일에 내용 추가
return fs.appendFile("log.txt", "새로운 로그 항목\n");
})
.then(() => {
console.log("파일 추가 완료");
})
.catch((err) => {
console.error("파일 작업 오류:", err);
});
4. async/await 사용 (Node.js 10 이상)
const fs = require("fs").promises;
async function handleFiles() {
try {
// 파일 읽기
const data = await fs.readFile("input.txt", "utf8");
console.log("파일 내용:", data);
// 파일 쓰기
await fs.writeFile("output.txt", "안녕하세요, Node.js!", "utf8");
console.log("파일 쓰기 완료");
// 파일에 내용 추가
await fs.appendFile("log.txt", "새로운 로그 항목\n");
console.log("파일 추가 완료");
} catch (err) {
console.error("파일 작업 오류:", err);
}
}
handleFiles();
5. 스트림 방식 (대용량 파일 처리에 적합)
const fs = require("fs");
// 읽기 스트림 생성
const readStream = fs.createReadStream("large-input.txt", { encoding: "utf8" });
// 쓰기 스트림 생성
const writeStream = fs.createWriteStream("large-output.txt");
// 데이터 이벤트를 통한 처리
readStream.on("data", (chunk) => {
console.log(`${chunk.length} 바이트 읽음`);
// 데이터 가공/처리 가능
writeStream.write(chunk);
});
readStream.on("end", () => {
writeStream.end();
console.log("읽기 완료");
});
writeStream.on("finish", () => {
console.log("쓰기 완료");
});
// 오류 처리
readStream.on("error", (err) => console.error("읽기 오류:", err));
writeStream.on("error", (err) => console.error("쓰기 오류:", err));
// 또는 파이프를 사용해 간단하게 처리
fs.createReadStream("large-input.txt")
.pipe(fs.createWriteStream("piped-output.txt"))
.on("finish", () => console.log("파이프 작업 완료"));
6. 파일 디스크립터를 이용한 저수준 작업
const fs = require("fs");
// 파일 열기
fs.open("file.txt", "r+", (err, fd) => {
if (err) {
console.error("파일 열기 오류:", err);
return;
}
// 버퍼 생성
const buffer = Buffer.alloc(64);
// 파일에서 읽기
fs.read(fd, buffer, 0, buffer.length, 0, (err, bytesRead, buffer) => {
if (err) {
console.error("파일 읽기 오류:", err);
} else {
console.log(
`${bytesRead} 바이트 읽음:`,
buffer.slice(0, bytesRead).toString()
);
}
// 파일에 쓰기
const content = Buffer.from("새로운 내용");
fs.write(fd, content, 0, content.length, 0, (err, bytesWritten, buffer) => {
if (err) {
console.error("파일 쓰기 오류:", err);
} else {
console.log(`${bytesWritten} 바이트 씀`);
}
// 파일 닫기
fs.close(fd, (err) => {
if (err) console.error("파일 닫기 오류:", err);
console.log("파일 닫음");
});
});
});
});
권장 사항 및 모범 사례
- 비동기 메서드 사용: 동기적 메서드는 메인 스레드를 차단하므로 프로덕션 환경에서는 비동기 메서드를 사용하는 것이 좋습니다.
- 대용량 파일에는 스트림 사용: 대용량 파일 처리 시 메모리 효율성을 위해 스트림을 사용합니다.
- 에러 처리 철저히: 모든 파일 작업에서 오류 처리를 반드시 포함합니다.
- 최신 Node.js에서는 fs.promises 활용: 비동기 코드를 깔끔하게 작성할 수 있습니다.
- 경로 관리에 path 모듈 사용: 크로스 플랫폼 호환성을 위해 경로 문자열 연결 대신 path.join() 사용합니다.
const path = require("path");
const filePath = path.join(__dirname, "data", "file.txt");
Node.js의 파일 시스템 모듈은 다양한 파일 작업을 처리할 수 있는 유연하고 강력한 API를 제공합니다.