파일 업로드를 멈추세요: React와 Vite로 만든 100% 클라이언트 사이드 이미지 변환기

발행: (2026년 1월 14일 오후 11:46 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

나는 “JPG to PNG”를 구글링하면서 개인 문서(ID 스캔, 계약서)를 무작위 서버에 업로드하도록 요구하는 의심스러운 사이트에 지쳤다.
그래서 직접 만들기로 했다.

목표는 간단했다: 서버 업로드 제로. 브라우저 메모리 안에서 완전히 실행되고, 파일을 즉시 처리하며, UI가 멈추지 않고 대량 변환을 수행할 수 있는 도구가 필요했다.

The Stack

  • React – UI 렌더링
  • Vite 6 – 빠른 개발 서버 및 번들러
  • Zustand – 가벼운 상태 관리

The Challenge: The “Serverless” Engine

모든 작업을 클라이언트‑사이드로 유지하기 위해 브라우저의 기본 Canvas API를 비전통적인 방식으로 활용했다. 아키텍처는 파이프라인처럼 동작한다:

  1. Ingestion – 기존의 느린 FileReader(이미지를 거대한 Base64 문자열로 변환) 대신 createImageBitmap을 사용했다. 이는 이미지 데이터를 브라우저의 GPU 메모리에서 직접 디코딩해 거의 즉시 처리한다.
  2. The “Invisible” Canvas – DOM에 전혀 삽입되지 않는 <canvas> 요소를 프로그래밍적으로 생성한다. 캔버스는 이미지 크기에 맞게 사이즈를 조정하고, 비트맵 데이터를 “그려” 메모리에 보관한다.
  3. Extraction – 캔버스가 제공하는 블롭을 특정 MIME 타입(image/png 혹은 image/webp 등)으로 내보내는 기능을 사용한다. 본질적으로 브라우저에게 캔버스의 “사진”을 다른 포맷으로 찍어 달라고 요청하는 것이다.

Result: 5 MB 이미지도 수 밀리초 안에 변환하고, 네트워크 지연이 전혀 없는 파이프라인을 구현했다.

The Performance Trap: Infinite Loops

상태 관리를 위해 Zustand를 선택했는데, 초보적인 실수를 저질렀다: 메인 컴포넌트를 전체 상태 객체에 연결해 버렸다.

이미지 하나가 변환을 마칠 때마다 상태가 업데이트된다. 전체 객체를 구독하고 있었기 때문에 React는 새로운 객체 참조를 감지하고 리스트 전체를 다시 렌더링한다. 50개의 이미지가 하나씩 상태를 업데이트하면, 메모리 누수처럼 보이는 재렌더링 폭풍이 발생했다.

Fix

원자적 셀렉터를 사용하도록 상태 로직을 리팩터링했다. 컴포넌트가 필요로 하는 특정 불리언이나 배열만을 구독하도록 강제함으로써 UI는 “버벅거림”에서 ~60 fps의 부드러운 흐름으로 변했고, 대량 배치 처리 중에도 원활하게 동작했다.

The Business Pivot: AdSense vs. Reality

처음 계획은 Google AdSense였다. 하지만 AdSense는 “Single Page Tools”을 싫어한다는 것을 금방 알게 되었다. 2,000단어 이상의 기사 없이 “낮은 가치 콘텐츠”로 플래그가 붙는다. 게다가 “프라이버시” 도구에 구글 추적기를 넣는 것은 위선적으로 보였다.

사용자들은 무작위 신발이나 신용카드가 아니라 데이터 안전을 원한다는 것을 깨달았다.

Pivot

직접 제휴 모델로 전환했다. 자리표시자 광고 슬롯을 제거하고, 프라이버시 중심 도구(VPN, 비밀번호 관리자)를 홍보하는 맞춤형 “네이티브 카드”를 디자인했다. 이는 수익 모델을 제품의 미션과 일치시키며, 광고라기보다 기술 스택 추천에 가깝게 만든다.

The Result

  • 현대 웹 삼위일체 지원: JPG, PNG, WebP
  • 벡터 SVG 파일을 완벽히 처리
  • 작업 공간을 어지럽히지 않으면서 유틸리티와 수익화를 균형 있게 배치한 “Holy Grail” 대칭 레이아웃 제공
  • 약속을 지킴: 페이지를 로드한 뒤 Wi‑Fi를 끊어도 기밀 문서를 하루 종일 변환할 수 있다. 파일이 기계 밖으로 나가는 일은 전혀 없다.

Live demo: Secure Converter
Source code: GitHub Repo

Back to Blog

관련 글

더 보기 »

🚨 React 재렌더링 방법: 레퍼런스가 중요!

변형 메서드는 참조를 변경하지 않습니다. 이러한 메서드들은 메모리 내 동일한 배열/객체를 수정하므로 React 상태에 직접 사용하면 재렌더링이 트리거되지 않을 수 있습니다.