DEVELOPMENT NOTE / Modu Square

이미지 첨부 기능 전송 비용 최적화

이미지 첨부 기능 구현

  • JPEG·PNG·WebP 이미지를 게시글당 최대 5장, 파일당 10MB까지 첨부
  • 글쓰기 화면에 업로드와 변환 상태 표시
  • 변환이 끝난 WebP 이미지를 게시글 상세 화면에 노출

WebP 이미지가 표시된 Modu Square 게시글

변환 완료 후 게시글 상세 화면에 노출된 WebP 이미지

문제

  • 브라우저가 보낸 원본 이미지를 애플리케이션 서버가 받은 뒤 Object Storage에 다시 저장
  • 이미지 업로드가 늘수록 API 처리와 무관한 바이트 전송이 서버 네트워크와 메모리를 점유
  • 인기 게시글에서 원본 이미지를 반복 제공하면 조회 수만큼 전송량 증가

1,117,288바이트 PNG를 초당 3건씩 30초간 업로드해 서버 경유 시 부하 측정.

서버 경유 업로드에서 애플리케이션 서버가 받은 이미지 바이트

  • 90건 업로드 동안 애플리케이션 서버로 95.90MiB 유입

기술 선택

애플리케이션 서버 경유 업로드 — 제외

  • 파일 크기만큼 서버 네트워크 사용량이 늘고 API 요청과 이미지 전송이 같은 자원을 사용

Presigned URL 직접 업로드 — 채택

  • Media Service는 10분 동안 유효한 업로드 URL만 발급
  • 브라우저가 Object Storage로 원본을 직접 보내 애플리케이션 서버를 전송 경로에서 제외

요청 시 원본 제공 — 제외

  • 구현은 단순하지만 게시글 조회마다 큰 원본 파일을 반복 전송

Kafka 비동기 WebP 변환 — 채택

  • Worker가 업로드 완료 이벤트를 받아 원본을 최대 1600×1200 WebP로 변환
  • 변환 작업을 게시글 저장 요청에서 분리하고 PENDING → PROCESSING → READY 상태를 UI에 표시
브라우저Media ServiceObject StorageKafkaImage WorkerWebP 변환업로드 URL 요청Presigned URL 반환원본 직접 업로드업로드 완료처리 이벤트비동기 변환WebP 저장
원본 이미지가 애플리케이션 서버를 거치지 않도록 업로드 경로를 분리하고, Kafka Worker가 WebP를 비동기로 생성

동시 갱신 문제 해결

등록 흐름을 점검하다가 이미지 변환 완료와 게시글 연결이 같은 media_asset 행을 동시에 수정하는 문제 확인.

  • Worker가 article_id = null인 데이터를 먼저 조회
  • 게시글 저장 과정에서 article_id 연결
  • Worker가 이전 상태의 엔티티 전체를 저장하며 article_id를 다시 null로 변경

Worker는 이미지 처리 필드만 조건부로 갱신하도록 수정.

@Query("""
        update MediaAsset media
           set media.thumbnailKey = :thumbnailKey,
               media.thumbnailSize = :thumbnailSize,
               media.status = board.media.domain.MediaStatus.READY,
               media.updatedAt = :updatedAt
         where media.mediaId = :mediaId
           and media.status = board.media.domain.MediaStatus.PROCESSING
        """)
int markReady(...);
  • 게시글 연결 필드와 이미지 처리 필드의 갱신 범위 분리
  • 두 작업의 실행 순서를 바꿔 가며 재현한 회귀 테스트에서 READY 상태와 article_id 유지 확인

부하 테스트

같은 이미지와 요청률로 서버 경유 업로드와 저장소 직접 업로드를 각각 90건씩 비교.

지표변경 전적용 후
애플리케이션 서버로 들어온 원본95.90MiB0바이트
파일당 조회 이미지1,117,288바이트52,748바이트
90회 조회 전송량95.90MiB4.53MiB

원본과 WebP 이미지의 예상 조회 전송량 비교

  • WebP 변환 후 파일 크기와 90회 조회 전송량 95.28% 감소
  • 로컬 MinIO 비교이므로 실제 CDN 요금이 아닌 애플리케이션 경유 바이트와 파일 크기를 판단 기준으로 사용

판단과 한계

  • 운영 환경에서는 CDN 도메인과 원본 접근 제한, 업로드 소유권 검증 필요
  • 처리량 증가에 대비한 Worker 확장과 변환 실패 격리는 후속 과제