저는 채팅에 이미지를 올리다가 갤럭시 단말에서만 실패한다는 제보를 받고 진땀을 뺐습니다. 똑같은 파일이 아이폰에서는 잘 올라가는데, 갤럭시에서는 50%에서 멈춰버리더군요. 그래서 Supabase 스토리지 업로드 흐름을 전면 점검하며 기기별 안정성을 높였습니다.
createSignedUploadUrl로 받은 URL에 PUT으로 업로드하고, DB 저장이 실패하면 Storage에서 파일을 즉시 삭제해 롤백했습니다.__optimisticUpload 메타로 UI에 표시하고, 끝내 모두 실패하면 메시지를 하드 딜리트했습니다.validateFile로 100MB 제한과 빈 파일 여부를 확인했습니다. 오류 메시지는 사용자에게 바로 전달할 수 있도록 문자열로 남겼습니다.generateUniqueFileName은 crypto.randomUUID()를 기본으로 쓰되, 지원되지 않을 때를 대비해 Math.random 기반 UUID로 대체합니다. 업로드 전에는 validateFile이 용량과 빈 파일을 걸러내 사용자에게 친절한 오류를 보여줍니다.
getSignedUploadUrl이 돌려준 URL에 uploadFileWithSignedUrl로 PUT 요청을 보냅니다. 모바일에서는 타임아웃을 60초로 늘리고, 재시도할 때마다 exponentialBackoff로 지연을 늘렸습니다. 갤럭시 단말이라면 업로드 전후에 100~150ms 정도 쉬어가게 해 리소스를 안정화했습니다.
import { exponentialBackoff } from './retry';
import { isMobileDevice } from './devices';
async function uploadFileWithSignedUrl(
file: File,
signedUrl: string,
maxRetries = 5,
) {
for (let attempt = 1; attempt <= maxRetries; attempt += 1) {
try {
const controller = new AbortController();
const timeout = isMobileDevice() ? 60000 : 30000;
setTimeout(() => controller.abort(), timeout);
const response = await fetch(signedUrl, {
method: 'PUT',
headers: { 'Content-Type': file.type || 'application/octet-stream' },
body: file,
signal: controller.signal,
});
if (response.ok) return true;
throw new Error(`Upload failed: ${response.status}`);
} catch (error) {
// 갤럭시 단말에서는 재시도 간 간격을 조금씩 늘려 네트워크를 안정화합니다.
await exponentialBackoff(attempt);
if (attempt === maxRetries) throw error;
}
}
}
업로드가 성공하면 attachment 테이블에 메타데이터를 저장합니다. 여기서 실패하면 deleteFileFromStorage로 방금 올린 파일을 지워 Storage와 DB가 엇갈리지 않게 했습니다. 이미 업로드된 파일을 갱신할 때는 단순히 순서만 업데이트합니다.
채팅에서는 uploadFiles가 메시지를 먼저 생성하고 __optimisticUpload에 전체/성공 개수를 기록합니다. 업로드가 전부 실패하면 deleteChatMessageHard로 메시지를 삭제해 빈 메시지가 남지 않도록 했습니다.
Content-Length 헤더를 강제로 넣으면 실패했습니다. 그래서 헤더에서 해당 값을 지우고 브라우저가 알아서 설정하도록 했습니다.sanitizeFileName으로 한글을 file로 치환하고 영문, 숫자, 언더스코어만 남겼습니다.지금은 갤럭시 단말에서도 업로드 성공률이 확 올라갔습니다. 실패해도 롤백이 깔끔하게 되니 데이터가 엉키지 않고, 사용자는 업로드 진행률을 눈으로 확인할 수 있게 됐습니다. 다음에는 백엔드에서 이미지 리사이즈를 비동기로 처리해 모바일 네트워크 부담을 더 줄여볼 생각입니다.
혹시 비슷한 업로드 문제를 겪으셨나요? 다른 기기에서의 삽질담이 있다면 댓글로 공유해 주세요. 서로의 케이스를 비교해 보면 의외의 해결책이 떠오르기도 하더라고요.