browser-image-compression 도입기 — 압축, 버그, 트레이드오프

rondido·2026년 8월 7일

projects

목록 보기
13/13

들어가며

스마트폰 촬영 이미지(평균 3~4MB)가 업로드 제한(2MB)을 초과해 실패가 빈번했습니다. 단순히 제한을 올리는 방법도 있었지만, 원본 이미지가 그대로 전송·저장되면 모바일 업로드 속도 저하와 S3 비용 증가가 예상되는 상황이었습니다. 그래서 클라이언트 단에서 먼저 압축해 전송량 자체를 줄이는 방향을 선택했습니다.

단순히 압축 라이브러리를 붙이는 작업처럼 보였지만, 실제로는 FSD 레이어 설계 고민과 예상치 못한 버그 발견까지 이어졌습니다.


1. 업로드 흐름

widgets/file-input → entities/file/api/file.api.ts의 uploadFile
→ presigned URL 발급 → S3 직접 POST

파일 선택 시점에 10MB 초과 파일은 업로드 자체를 차단하는 정책이 적용되어 있었습니다.


2. 압축 로직을 어디에 둘 것인가

압축 라이브러리(browser-image-compression)를 도입하면서 가장 먼저 고민한 것은 압축 로직을 어느 레이어에 둘 것인가였습니다.

처음에는 entities/file/api/file.api.tsuploadFile 내부에 두는 방향을 검토했습니다. entities는 도메인 재사용 로직을 담는 계층이고, features → entities 의존 방향에도 맞았기 때문입니다.

그런데 한 가지 순서 문제를 발견했습니다. file-input.tsx의 10MB 용량 검증은 파일 선택 즉시 실행되고, uploadFile의 압축은 업로드 시점에야 실행됩니다. 즉 압축을 uploadFile에 두면 "압축하면 통과할 수 있었던 큰 원본"이 선택 단계에서 이미 막혀버리는 구조가 될 수 있었습니다.

다만 이 프로젝트는 10MB 초과 시 무조건 업로드 차단이 의도된 정책이었기 때문에 순서 문제는 실제로 해당되지 않는 것으로 결론이 났고, 압축은 uploadFile(entities 계층)에 두는 구조로 진행했습니다.


3. 압축 적용

// entities/file/api/file.api.ts
import imageCompression from 'browser-image-compression';

const options = {
  maxSizeMB: 2,
  maxWidthOrHeight: 1920,
  fileType: 'image/webp',
  useWebWorker: true,
};

let compressionFile = file;

try {
  if (file.size > 2 * 1024 * 1024) {
    compressionFile = await imageCompression(file, options);
  }
} catch (error) {
  console.error('압축 실패, 원본 사용:', error);
}
  • 2MB 초과 파일만 압축
  • useWebWorker: true로 메인 스레드 블로킹 방지
  • 압축 실패 시 원본 파일을 그대로 사용 (graceful fallback)

4. 압축만 했을 뿐인데 버그가 생겼다

압축을 적용하고 나서 예상치 못한 버그를 발견했습니다.

압축 결과물은 image/webp로 변환되는데, presigned URL 발급 요청은 원본 file.type으로 보내고 있었습니다.

// 버그 — 원본 타입으로 presigned URL 발급
const url = await getPresignedUrl(file.type);

// 수정 — 실제 업로드되는 파일의 타입으로 발급
const url = await getPresignedUrl(compressionFile.type);

S3 presigned POST policy의 Content-Type 검증과 실제 업로드되는 파일 타입이 불일치하면 업로드 실패 또는 잘못된 메타데이터 저장으로 이어질 수 있는 문제였습니다. 압축 성공/실패 여부와 무관하게 항상 실제 업로드되는 파일의 타입으로 요청하도록 수정했습니다.


마치며

단순한 압축 라이브러리 도입 작업에서 두 가지를 배웠습니다.

압축 로직의 위치는 실행 시점을 고려해야 합니다. 선택 시점과 업로드 시점의 차이가 정책과 충돌할 수 있다는 것을 확인했습니다.

압축은 파일 타입을 바꿉니다. presigned URL 발급 시 원본 타입이 아니라 실제 업로드되는 파일의 타입을 사용해야 한다는 것을 압축 도입 이후에야 발견했습니다.

profile
풋살을 좋아하는 프론트엔드 개발자

0개의 댓글