Development/Node.js

[fs : FileSystem] Promise로 동기적으로 처리하기 (+Sync 함수를 사용하면 안되는 이유)

자르비옹스 2022. 1. 9. 20:45

fs는 일반적으로 비동기적으로 사용되어 callback 함수를 사용하는데, 실제 서버에서 사용할 때에는 동기적으로 사용할 필요가 있다. callback을 이용하면 편리할 때도 있지만, callback 지옥에 빠질 수 있으므로 동기적 구현을 고려해보아야 한다.

 

fs Sync 함수

기본적으로 fs에는 동기적으로 실행시켜 주는 Sync함수가 존재한다. 이는 기본 함수명 뒤에 Sync를 붙이면 되므로 간편하다.

// asynchronous
fs.readFile(path, () => {
	// ...callback function
})

// synchronous
fs.readFileSync(path)

Sync vs Promise

그러나 Sync함수와 Promise 함수에는 한 가지 차이점이 존재한다.

Sync 함수와 같은 경우, nodejs에 여러 요청이 들어왔을 때 Thread를 정지시킨다. (Stop the world 라고 표현하는 듯)

fs Sync 함수 사용 시 Thread Stop

만약 파일을 읽는 다중 요청이 발생한다면 아래와 같이 동작한다.

Sync 함수의 성능

눈으로 보기엔 파일 처리가 바르게 진행되는 것 처럼 보이지만, stop the world 현상이 프로그램에 어떤 영향을 미치는지 알아볼 필요가 있다.

const fs = require("fs");

["p1", "p2", "p3"].map(async (f) => {
	console.log(`Reading ${f}`);
	fs.readFileSync(f);
	console.log(`Reading ${f} finished`);
});

위와 같은 코드가 있다고 가정한다. 해당 코드는 p1, p2, p3 파일을 병렬적(in parallel)으로 읽을 것이다.

Reading p1
$ echo "p" > p1
Reading p1 finished
Reading p2
$ echo "p" > p2
Reading p2 finished
Reading p3
$ echo "p" > p3
Reading p3 finished

하지만 위의 코드를 실행한 결과 Synchronous하게 동작하는 것을 확인할 수 있다. p1을 읽기 시작하고, p1을 다 읽기까지 thread를 멈춘다. 그리고 p1을 다 읽으면 p2를 읽기 시작한다.

이러한 현상은 병렬로 실행되는 Promise, 타이머, Callback 및 네트워크 요청과 같이 겉보기에는 관련이 없어 보이는 것들에 영향을 미친다.

 

fs.promises

위와 같은 이슈를 방지하기 위하여, Node.js가 업데이트 되면서 fs.promises 버전이 추가되었다.

const contents = fs.promises.readFile("...");

위와 같이 fs.promises 다음에 사용할 함수를 작성하면 된다.

 

이러한 함수들은 stop the world에 영향을 주지 않고, callback 기반 fs함수들에 대한 추상일 뿐이기 때문에 프로그램의 확장성에 영향을 미치지 않는다. async/await 기능을 사용할 경우 코드는 거의 동일하다.

const fs = require("fs");

["p1", "p2", "p3"].map(async (f) => {
	console.log(`Reading ${f}`);
	await fs.promises.readFile(f);
	console.log(`Reading ${f} finished`);
});
Reading p1
Reading p2
Reading p3
$ echo "p" > p1
Reading p1 finished
$ echo "p" > p3
Reading p3 finished
$ echo "p" > p2
Reading p2 finished

해당 파일은 서로 독립적으로 읽어진다. ( in parallel )

 

결론

fs의 Sync함수 대신에 fs.promises를 사용하자!

 

 

참고

https://advancedweb.hu/do-not-use-fs-sync-methods-in-javascript-use-fs-promises-instead/