네이버 클라우드 Object Storage zip 파일 압축 해제 한계와 WAS 스트림 해결 방법
![]()
안녕하세요. 클라우드 스토리지를 백엔드 프로젝트에 연동하다 보면, 로컬 서버(File System)와는 전혀 다른 동작 방식 때문에 당황스러운 순간들이 발생합니다.
“사용자가 여러 이미지가 담긴
.zip파일을 통째로 업로드하면, 서버에서 자동으로 압축을 풀어서 개별 이미지 파일로 스토리지에 저장해주세요.”
이런 요구사항을 받았을 때, 리눅스 서버 환경에 익숙한 개발자라면 가장 먼저 이렇게 생각할 수 있습니다.
“스토리지에 zip 파일을 업로드한 다음, 스토리지 내부에서 unzip 명령어를 실행해서 풀면 되지 않을까?”
실제로 클라우드 콘솔을 뒤적이거나 SDK 문서를 검색하며 ‘압축 해제’ API를 찾느라 시간을 보내는 분들이 많습니다. 저 역시 처음엔 그랬으니까요. 하지만 이 접근은 Object Storage의 근본적인 개념을 간과한 착각이었습니다.
오늘은 네이버 클라우드(NCP) Object Storage 환경에서 이 문제를 어떻게 해결했는지, 디스크 I/O 낭비 없이 WAS 메모리 스트림을 활용하는 아키텍처적 고민을 공유해 보겠습니다.
1. 블록 스토리지(Block Storage)와 오브젝트 스토리지(Object Storage)의 차이
우리가 흔히 AWS EC2나 온프레미스 리눅스 서버에서 사용하는 파일 시스템은 블록 스토리지(Block Storage) 기반입니다. 운영체제(OS)가 설치된 인스턴스에 마운트되어, 디렉토리 계층 구조를 가지고 CPU 자원을 사용해 파일을 조작할 수 있습니다.
반면, 네이버 클라우드 Object Storage(또는 AWS S3)는 객체(Object) 단위의 단순 데이터 저장소입니다.
– Flat Namespace: 폴더처럼 보이는 구조는 사실 prefix일 뿐, 실제로는 계층이 없는 평면 구조입니다.
– 연산 능력 부재: 스토리지는 HTTP RESTful API(PUT, GET, DELETE 등)만을 제공할 뿐, 내부에 컴퓨팅 파워(CPU, RAM)가 할당되어 있지 않습니다.
즉, 오브젝트 스토리지는 가상 컴퓨터(VM)가 아니기 때문에, 스토리지 자체에서 쉘 명령어(unzip)를 실행하는 것은 물리적으로 불가능합니다.
2. 해결 방안: WAS 단에서 Stream 기반 압축 해제 파이프라인
스토리지 내부 조작이 불가능하다면, 스토리지에 업로드하기 전 단계에서 압축을 풀어야 합니다.
임시로 EC2(WAS) 로컬 디스크에 파일을 저장했다가 압축을 풀고 다시 업로드하는 방식은 불필요한 디스크 I/O를 발생시키고 서버 용량을 차지합니다. 이를 피하기 위해, WAS(Spring Boot)의 메모리 스트림(ZipInputStream)을 활용하여 파일 데이터를 실시간으로 읽고 클라우드로 바로 전송하는 방식을 선택했습니다.

Java (Spring) 기반 Stream 처리 수도 코드
로컬 디스크 저장을 생략하고, 스트림에서 바이트를 읽어내는 즉시 NCP SDK의 putObject 요청으로 넘기는 구조입니다.
public void uploadZipToStorage(MultipartFile zipFile) {
try (ZipInputStream zis = new ZipInputStream(zipFile.getInputStream())) {
ZipEntry entry;
// zip 내부의 파일을 순회하며 하나씩 추출
while ((entry = zis.getNextEntry()) != null) {
if (!entry.isDirectory()) {
String fileName = entry.getName();
// 스트림에서 바로 바이트를 읽어 Object Storage SDK로 PUT 업로드 (디스크 I/O 없음)
objectStorageClient.putObject(bucketName, fileName, zis);
}
zis.closeEntry();
}
} catch (IOException e) {
log.error("압축 해제 및 업로드 실패", e);
}
}
이렇게 하면 사용자가 올린 zip 파일이 WAS의 메모리를 흐르듯 거쳐 개별 파일 스트림으로 분리되고, 각각의 독립적인 객체(Object)로서 스토리지에 나란히 저장됩니다.
3. 트레이드오프 (Trade-off) 및 아키텍처 확장
이 방식은 요구사항을 명확하고 우아하게 해결하지만, 아키텍처 설계 시 반드시 감수해야 할 트레이드오프가 존재합니다. 압축 해제 로직은 본질적으로 CPU와 메모리를 많이 소모하기 때문입니다.
- WAS 부하 및 OOM(Out of Memory) 위험
-
사용자가 1GB 이상의 대용량 zip 파일을 올리거나, 다수의 사용자가 동시에 압축 해제 업로드를 요청할 경우, 비즈니스 로직을 처리하는 메인 WAS가 뻗어버릴 위험이 있습니다.
-
고급 해결책: Serverless 워커(Worker) 분리
- 위와 같은 부하 이슈가 예상되는 서비스라면, 메인 서버와 파일 처리 로직을 물리적으로 분리해야 합니다.
- 네이버 클라우드의 Cloud Functions(또는 AWS Lambda) 같은 서버리스 환경이나 비동기 메시지 큐(RabbitMQ)를 활용하여, “무거운 파일 처리 전담 인스턴스”에 역할을 위임하는 아키텍처로 확장하는 것이 바람직합니다.
마무리
처음에는 단순히 “클라우드 스토리지에서는 압축이 안 풀리네?” 하고 당황했던 경험이었지만, 이를 통해 블록 스토리지와 오브젝트 스토리지의 명확한 구조적 차이를 체감할 수 있었습니다.
디스크 I/O를 줄이는 메모리 스트림 방식은 성능 면에서 훌륭하지만, 그만큼 메모리 누수나 부하 관리에 신경 써야 한다는 점도 잊지 마시기 바랍니다. 다음 포스팅에서는 실제 Spring Boot 코드 레벨에서 NCP Object Storage SDK를 결합하여 견고하게 동작하는 전체 코드를 구현해 보도록 하겠습니다.
감사합니다.