두 방식의 핵심 차이는 "파일 바이트가 애플리케이션 서버를 실제로 경유하느냐"에 가깝다. 다만 인증, 업로드 권한 판단, 업로드 완료 처리 같은 제어 로직은 두 방식 모두 서버가 관여할 수 있다.
서버 경유 업로드
- 사용자가 이미지를 선택
- 클라이언트가 서버로
multipart/form-data요청 전송 - 서버가 파일을 검증
- 서버가 스토리지에 파일 업로드
- 서버가 이미지 URL이나 파일 정보를 클라이언트에게 반환
이 방식은 서버가 파일을 받은 뒤 크기와 형식을 확인하고, 필요하면 리사이즈나 썸네일 생성같은 후처리를 진행한 뒤 스토리지에 저장한다.
프로필 이미지처럼 크기가 작고, 업로드 직후 서버에서 처리할 일이 많다면 이 방식이 적합하다. 서버가 모든 과정을 거치기 때문에 “이 사용자가 업로드할 수 있는 이미지인지”, “이미지를 저장하기 전에 변환해야 하는지”를 한곳에서 통제할 수 있다.
대신 이미지가 서버에게 전달되기 때문에, 10MB 이미지라면 서버가 10MB를 받고 다시 스토리지로 10MB를 전송해야 한다. 이는 업로드하는 사용자가 많아지거나 파일 크기가 커지면 서버에 부담이 크게 가중된다. 또한, 서버리스 환경에서는 요청 크기나 실행 시간 제한에 걸릴 수 있다. (예를 들어, 100명의 사용자가 10MB 이미지를 동시에 업로드 요청한다면, 서버는 10MB * 100 = 1GB를 수신하게 되어 부담이 커진다.)
그리고, 파일이 서버로 이동하고 다시 스토리지로 이동하기 때문에 일반적으로 Presigned URL 방식보다 느리다.
∴ 크기가 작거나 사후 처리가 필요한 경우에 서버 경유 방식이 적합하다.
Presigned URL 업로드
- 클라이언트가 서버에 업로드 URL을 요청
- 서버가 업로드 목적과 허용 조건(예: 예상 파일 타입, 크기 제한, 저장 경로)을 판단
- 서버가 스토리지에 제한된 권한의 임시 URL을 생성하고 클라이언트에게 전달
- 클라이언트가 해당 URL로 스토리지에 직접 파일을 업로드
- 업로드 완료 후 클라이언트가 서버에 완료 사실을 알리거나, 서버가 업로드 상태를 확인
이 방식은 서버가 파일을 직접 받지 않는다. 클라이언트가 업로드를 시작하면, 서버는 단순히 허용되는 파일인지 확인하고 스토리지에 업로드할 수 있는 임시 URL을 발급한다. 클라이언트는 해당 URL을 사용해 이미지를 스토리지에 직접 업로드한다.
이는 서버가 파일 바이트를 직접 처리하지 않기 때문에 서버 부담이 크게 줄어든다. 다만, 생각해야 하는 부분들이 많다.
만약에 이미지가 어떠한 형태로든 서비스에서 사용되는 에셋이라고 한다면, 클라이언트가 업로드를 완료했을 때 서버에게 "어떤 용도로 쓸 건지"를 전달해야 한다. (용도는 게시글 썸네일, 프로필 이미지 등등 다양한 케이스가 될 수 있다.)
어떤 케이스가 중요한 게 아니고, 이를 통해 이 이미지를 어디에 연결할지(예: 누구의 프로필 이미지인가, 어떤 게시글의 이미지인가)를 알 수 있도록 전달하는게 중요하다는 의미다.
이러한 전달이 없다면, 스토리지에는 파일이 있지만 DB에는 어디에도 연결되지 않아 고아 파일이 될 수 있다. 그래서 일반적으로 업로드 완료 API를 사용하기도 한다.
POST /api/images/complete
{
"key": "users/123/images/abc.jpg"
}
직접 비교 결과 (로컬 환경 → 원격 R2 업로드)
- 통제 변인: 이미지 크기(7.89MB)
- 결과:
- 서버 업로드 - 약 3.5s
- Presigned URL - 약 950ms
- 통제 변인: 이미지 크기(36KB)
- 결과:
- 서버 업로드 - 약 1.5s
- Presigned URL - 약 350ms
이 실험에서는 두 케이스 모두 서버 업로드 방식이 더 느렸다. 다만 이 결과만으로 파일 크기와 상관없이 항상 느리다고 일반화할 수는 없고, 네트워크 환경, 서버 위치, 스토리지 리전, 구현 방식에 따라 차이는 달라질 수 있다. 그렇다면 왜 서버 경유 업로드 방식을 사용하는 걸까라는 생각이 든다. 구현의 단순함과 통제력이 느린 속도를 압도할 만큼 좋은건가? 싶다가…
서버 경유 업로드에 대해 더 찾아보니:
- 서버가 직접 파일을 받기 때문에, 서버에서 파일의 보안적인 측면을 검사할 수 있다.
- 예: 바이러스가 있는 유해 파일인지
- 즉, 파일을 통과시키면서 서버에서 바로 처리해야 하는 과정이 있을 때 유리하다.
- 반면, Presigned URL 방식에서는 서버가 파일 바이트를 직접 보지 않으므로, 파일 내용 자체에 대한 검사(예: 악성 파일 스캔, 실제 포맷 검증)가 필요하면 업로드 후 별도 처리 단계가 필요하다.
- 또한 presigned URL에 어떤 제약을 강제할 수 있는지는 스토리지와 업로드 방식(PUT/POST 등)에 따라 다르다. 예를 들어 MIME 타입, 파일 크기, 저장 경로 같은 조건은 설계에 따라 제한할 수 있지만, 실제 파일 내용 검증과 동일한 수준의 보장을 주는 것은 아니다.