이미지 업로드 전에 서버에서 WebP로 정리하기
프로필, 배너, 분석 사진 업로드가 커지고 느려질 때, 서버 업로드 전 이미지 검증과 압축 계층을 만든 기록입니다.
Code notes
코드에서 확인한 구현 포인트
배포 기준 2026.05.03
관련 파일
apps/web/lib/server/image-upload.tsapps/web/app/api/profile/photo/route.tsapps/web/app/api/admin/*/upload/route.ts구현 메모
prepareImageUpload는 이미지 MIME 타입과 확장자를 확인한 뒤 sharp로 회전 보정, 리사이즈, WebP 변환을 수행한다.
압축 실패 시 요청 전체를 실패시키지 않고 원본 buffer와 content type으로 업로드를 계속할 수 있게 했다.
프로필 사진 업로드는 안전한 파일명, Storage 업로드, profiles upsert, user metadata 갱신을 한 흐름으로 처리한다.
업로드 성공만으로는 충분하지 않았다
초기 업로드는 파일을 받아 Storage에 올리는 데 집중했다. 하지만 운영 화면과 모바일 화면이 늘어나면서 이미지 크기, 파일명, 확장자, content type이 조금씩 문제를 만들기 시작했다.
큰 이미지는 업로드와 표시를 느리게 만들고, 파일명이 불안정하면 저장 경로가 지저분해진다. 그래서 업로드 전에 서버에서 이미지를 한 번 정리하는 공통 계층이 필요했다.
이미지인지 먼저 판단하기
prepareImageUpload는 content type과 파일 확장자를 보고 이미지 파일인지 판단한다. 이미지라면 회전 정보를 반영하고, 최대 크기를 제한한 뒤 WebP로 변환한다.
이미지가 아니거나 변환에 실패하면 무조건 요청을 죽이지 않고 원본 buffer와 content type을 돌려준다. 업로드 계층이 너무 공격적으로 실패하면 운영자가 단순 파일 교체도 못 하게 될 수 있기 때문이다.
프로필 사진 업로드에 적용하다
프로필 사진 API에서는 multipart/form-data인지, file과 userId가 있는지 먼저 확인한다. 그 다음 안전한 파일명으로 바꾸고, 압축 결과의 확장자에 맞춰 Storage 경로를 만든다.
업로드 후에는 public URL을 profiles와 사용자 metadata에 반영한다. 파일 저장과 사용자 프로필 갱신이 함께 성공해야 앱에서 바로 새 사진을 볼 수 있다.
다시 배운 점
이미지 업로드는 Storage 기능이 아니라 제품 성능 기능이다. 압축, 파일명 정리, content type, DB 갱신을 한 흐름으로 봐야 한다.
또 압축 실패가 곧 업로드 실패는 아니다. 사용자와 운영자에게 필요한 것은 가능한 한 안전하게 저장하고, 실패 지점을 분명히 알려주는 것이다.
Keep reading
다른 글 이어서 보기
Debugging
2026.05.05
6 min read
GitHub 푸시 후 Vercel 배포가 Blocked가 되었을 때
커밋은 올라갔지만 Vercel 자동 배포가 막혔던 상황에서, GitHub 작성자와 프로젝트 권한을 확인해 해결한 기록입니다.
Debugging
2026.05.04
7 min read
서브도메인은 연결됐는데 /admin이 404였던 이유
blog, ad, nacho 서브도메인을 나누는 과정에서 Vercel 404와 앱 내부 라우팅 문제를 구분해 해결한 기록입니다.
Debugging
2026.05.03
8 min read
도메인은 연결됐는데 접속이 거부될 때
tangly.kr과 www 도메인을 Vercel에 붙이며 DNS, 네임서버, SSL 발급 대기 상태를 구분해 해결한 기록입니다.