회사에서 웹 프로젝트 서버 개발을 담당하다보니 file 을 저장하는 API 를 구현해야하는 일이 빈번히 있었다.
node.js express 를 통해서 REST API 방식도 사용해봤고.
graphql 을 통해서 API 구현도 해보았는데 어쨋든 파일을 받아서 저장할때는 fs 모듈의 createReadStream 이나 createWriteStream 을 사용하게 되었다.
graphql 을 사용할때는 Upload scalar type 을 만들기 때문에
API 를 통해 파라미터를 받을때 파라미터에 readStream 함수가 포함된 Promise 가 넘어온다. 그래서
const {filename, readStream} = await file;
이런식으로 file 정보를 parsing 할 수 있었다.
기존의 처리방식은 하기와 같았다.
import fs from "fs";
...
//API 내부 로직
const {filename, readStream} = await file;
const _readStream = readStream;
const writeStream = fs.createWriteStream(`/uploads/${filename}`);
_readStream.pipe(writeStream);
하지만 이 경우 어떻게든 저장은 되지만 대용량 파일의 경우 어느정도의 시간이 소요되는데,
이를 기다리지 못하고 응답(response)은 반환해 버리니 아직 파일이 완벽하게 저장이 되지 않았을 경우 파일이 깨지거나 클라이언트가 바로 받을 수 없는 경우가 생겼다.
그래서 Promise 를 사용하게 되었다.
import fs from "fs";
...
//API 내부 로직
const {filename, readStream} = await file;
const _readStream = readstream;
const writeStream = fs.createWriteStream("/uploads/${filename}`);
//Promise 를 반환하는 비동기 함수 정의
const promiseExecute = async () => {
return new Promise((resolve) => {
_readStream.pipe(writeStream);
writeStream.on("finish", resolve);
});
}
await promiseExecute();
상기 코드와 같이 Promise 를 반환하는 비동기 함수 "promiseExecute" 를 만들어서 file 이 저장될 때까지 기다리도록 만드는 것이다.
fs 모듈의 createWriteStream() 은 객체를 반환하는데, 이 객체에는 "finish" 이벤트가 있어서 file 의 저장이 완료되었는지 알 수 있다.
Promise 는 대기를 필요로하는 모든 로직에 적용해 볼 수 있다.
상기와 같은 API 의 경우 말고도 파일을 옮길 경우에도 적용해 볼 수 있다.
예를 들어서,
A 폴더에 abc.zip 이라는 대용량의 파일이 있고,
이를 B 폴더에 옮기고 싶은 상황이다.
그렇다면 현재 디렉토리 구조는 이렇다.
/A/abc.zip
/B
import fs from "fs";
const fileMove = async() => {
//읽는 파일
const readStream = fs.createReadStream("/A/abc.zip");
//쓰는 파일
const writeStream = fs.createWriteStream("/B/abc.zip");
//Promise 사용부분
const promiseExecute = async () => {
return new Promise((resolve) => {
readStream.pipe(writeStream);
writeStream.on("finish", resolve);
});
}
await promiseExecute();
}
await promiseExecute() 실행시 "finish" 이벤트를 통해 file 이 모두 쓰여진 후 resolve 를 하기 때문에 file 저장이 완료되었다는 것을 확신할 수 있다.
하기와 같이 resolve 에 값을 넘겨서 값을 반환 받을 수도 있다.
import fs from "fs";
const fileMove = async() => {
//읽는 파일
const readStream = fs.createReadStream("/A/abc.zip");
//쓰는 파일
const writeStream = fs.createWriteStream("/B/abc.zip");
//Promise 사용부분
const promiseExecute = async () => {
return new Promise((resolve) => {
readStream.pipe(writeStream);
writeStream.on("finish", () => {
resolve("hi");
});
});
}
const res = await promiseExecute(); //hi
}