📝

엑셀 파일 다운로드 개선 경험 - 2

Created
2023/07/10 06:01
Tags
안녕하세요. 볼드나인의 백엔드 개발자 장유빈입니다.
이전에 제가 작성한 글 [엑셀 파일 다운로드 개선 경험 - 1]에 이어서
다음과 같은 예제를 통해 간단하게 stream을 커스텀 하여 사용할 수 있는 방법을 설명드리고자 합니다.
readable은 특정 데이터를 가져와서 부분적으로 뿌려주는 역할이며
writable은 readable이 뿌려주는 데이터를 받아서 특정한 곳에 다시 적재하는 역할이라고 보시면 됩니다.
위 코드에서 sourceData가 있는데요 이는 DB에서 가져올 수 있는 데이터라고 생각하시고
destData가 저희가 사용할 변수 즉 인메모리의 데이터라고 보시면 됩니다.
위 가정에서 16번째 줄의 sourceData.shift()는 실제로 DB에 쿼리를 요청해서 데이터를 가져오는 것으로 보셔도 무방합니다.
물론 DB가 아니라 특정한 데이터를 분할하여 전송한다고 생각하셔도 되며
이는 어떤 방식으로 데이터를 가져와서 readable에 넣어주냐에 따라 다 다르지만
this.push에 전달해 주는 값이 pipe를 통해서 writeable의 chunk에 전달된다는 것이 중요하게 보셔야 하는 점입니다.
readable의 read 구현 시 더 이상 전달할 값이 없을 경우 this.push(null)을 수행해야만 전달할 값이 더 이상 없다는 것을 알려줄 수 있습니다.
writable의 write 구현 시 데이터가 정상적으로 전달되었다고 알려주려면 callback()을 수행시켜줘야 하며 문제가 있는 경우 callback에 error 객체를 전달해 주면 됩니다.
stream은 EventEmitter를 통해 구현되어 있어 pipe의 종료 시점 시 발생되는 이벤트인 finish를 기다리기 위해
promise를 구현하고 해당 promise가 fulfilled 될 때까지 기다리도록 작성했습니다.
destData를 console에 출력하는데 sourceData의 모든 내용이 잘 담긴 것을 확인할 수 있습니다.
이러한 방식으로 이전 글에서 알려드렸던 엑셀 다운로드 시 데이터 크기가 얼마이던 메모리에 불러와서 저장한 방식을
readable의 read 내에서 db 쿼리에 limit, offset을 적용하여 감당 가능한 정도의 데이터만 불러오게 하고
해당 데이터를 가공 후 바로 GCP의 파일 객체에 쓰는 방식으로 개선을 할 수 있었습니다.
기존 코드에서는 데이터를 엑셀로 만들어서 줄 때 데이터가 너무 많은 경우 DB 조회 시 뻗으면 어쩌지?
혹은 메모리가 초과해서 서버가 뻗으면 어쩌지 하는 걱정을 하게 되었다면
개선된 코드에서는 한 번에 가져오는 데이터 양을 조정하고 메모리에 한 번에 모든 데이터를 올리지 않기 때문에 안정적인 운영을 할 수 있었습니다.
이번 내용은 여기까지입니다.
감사합니다.