이번 포스팅에서는 Server에 파일과 함께 정보를 업로드(POST)하는 3가지 방법에대해 알아보도록 하겠습니다. 이외에 더 많은 좋은 방법들이 있다면, 댓글로 부탁드립니다 ~
File과 Json을 함께 서버에 전송해야 하는 경우
서버를 개발하다보면, file을 업로드 해야하는 경우가 발생합니다.
x
public class TestController {
// file upload 처리 핸들러
"/file") (
public void upload( ("file") MultipartFile file) {
System.out.println("file name : " + file.getName());
}
}
File만을 업로드(https://galid1.tistory.com/564) 한다면, Server에서는 위와 같이 쉽게(?) 처리할 수 있습니다.
하지만, 실제 App을 만들다보면 File만을 업로드하는 경우는 굉장히 드뭅니다. 예를들어, 회원가입시, 사용자의 정보와 함께 프로필 이미지를 받는 경우가 있을 것입니다. 이는, 단순 파일만을 업로드 하는 경우와는 다른 난이도의 생각과 개발을 요구합니다.
이 포스팅에서 설명드리는 경우는 제가 경험해본 극히 제한적인 상황에서의 예제이며, 3가지 방법중 가장 옳은 방법은 존재하지 않습니다. 모든 설계가 그렇듯 적절한 트레이드오프가 필요하며, 그것 또한 개발자의 역량에 따라 충분히 달라질 수 있습니다.
Server에 파일과 함께 정보를 업로드(POST)하는 대표적 3가지 방법
1. Multipart/form-data
Server에 File과 정보를 함께 업로드하기 위한 첫번째 방법으로 Multipart/form-data
를 사용하는 것 입니다.
x
...
value = "/products") (
public List<ProductUploadResponse> uploadProductImages(
"location") ProductLocation[] productLocations, (
"file") MultipartFile[] productImageFiles) { (
verifyCountIsEqual(productImageFiles.length, productLocations.length);
return catalogProductUploadService
.uploadProductImages(userId, makeUploadedProductList(productLocations, productImageFiles));
}
private void verifyCountIsEqual(int imageFilesCount, int locationInformationCount) {
if(imageFilesCount != locationInformationCount)
throw new IllegalArgumentException("이미지의 개수와 위치정보의 개수가 다릅니다.");
}
위의 핸들러는, 사용자가 여러개의 상품 이미지가 업로드하는 경우 처리를 위한 핸들러입니다. ImageFile과 함께 전달되는 ProductLocation은 상품 사진이 찍힌 GPS 정보입니다. 따라서 핸들러의 두개의 매개변수(productLocations, productImageFiles)는 서로 연관이 있습니다.
장점
- 구현이 쉽다.
단점
- 파일과 함께 전달된 데이터가 연관이 있는 경우, 별도의 처리가 필요하다.
문제점
위에서 설명드린것과 같이 두개의 매개변수는 서로 연관을 가지고 있습니다. 하지만 별개의 매개변수로 전달되고 있는것이 문제입니다. 우선, 상품이미지의 개수와, 상품이미지의 위치정보의 개수가 같아야 하는 것은 당연합니다.
또한, Map 자료형이 아닌 단순히 List 자료형에 담겨 있기 때문에, 상품이미지에 대응하는 위치정보를 표현할 수 있는 방법은, 단순히 순서를 맞추는 방법 밖에 없습니다. 따라서, Client에서 보낸 상품이미지와 이미지 위치정보를 그냥 믿을 수 밖에 없습니다.
2. Base64 인코딩 후, JSON 객체로 전송
두번째 방법은, File을 Base64 encoding을하여, JSON 객체에 포함시켜 전송하는 것 입니다.
x
let product = {
productImageFile: btoa(file),
productLocation: location
};
upload(product); // 서버로 전송하는 코드
위의 코드는 javascript에서 file을 base64인코딩하여, 위치정보와 인코딩된 file을 하나의 객체로 맵핑하여 서버로 전송하는 코드입니다. 위와 같이 하나의 객체로 전송함으로써, 이미지파일과 연관된 위치정보를 하나로 묶을수 있습니다.
x
public class ProductUploadRequest {
private ProductLocation location;
private String base64ProductImage;
}
public class ProductUploadController
private final S3FileUploader s3FileUploader;
("/users")
public void uploadProductImage( ProductUploadRequest request) {
String uploadPath = s3FileUploader.uploadFile(IMAGE_PATH_KEY, Base64.decodeBase64(request.getBase64PassPortImage())); // s3에 업로딩
...
}
장점
- 인코딩, 디코딩을 위한 추가 작업과 서버 부하 증가
단점
- 인코딩하는 경우 String의 길이가 길어져, Server측에서 file을 읽지 못하는 경우가 발생
3. 파일과 JSON을 별도로 업로드
마지막 방법은, 별도로 파일을 업로드 하는 방법입니다.
우선, 이미지의 위치정보를 가지고, 상품을 생성합니다. 이후 update를 이용해 file을 별도로 업로드 받아 각각의 상품에 맵핑합니다.
하지만, 이 방법은 이번 포스팅에서 설명드리기 위해 예제로 사용된 상품이미지 업로드에는 적합하지 않아 보입니다. 상품 위치만을 가지고 상품이 생성되는 경우는, 굉장히 드물기도 하며, 이미지만을 가지고 위치정보에 맵핑하기가 어렵기 때문에, 업로드된 이미지를, 상품위치에 맵핑하기 위해서 별도의 처리가 또한 필요할 것이기 때문입니다.
단점
- 1번 방법과 마찬가지로 파일과, JSON 정보의 연관을 표현하기 어렵습니다.
장점
- 이미지에대한 특별한 처리가 필요한경우, 이를 특화시킬 수 있습니다.
회원가입 예제에 적합
이 방법은, 회원가입을 구현하는 경우에 적절해 보입니다. 회원가입을 한뒤, 프로필 설정에서, 사용자의 이미지를 업로드하는 경우의 도메인을 생각하면 이해가 쉬울 것입니다.
'Application Knowhow > Server' 카테고리의 다른 글
ApplicationKnowhow/Server - API 성능 개선기 2 (Query 개선) (0) | 2021.11.09 |
---|---|
ApplicationKnowhow/Server - API 성능 개선기 1 (JPA 성능 개선(kotlin)) (0) | 2021.11.09 |
ApplicationKnowhow/Server - 게시판 조회수 기능 성능 최적화 (4) | 2020.11.23 |
ApplicationKnowhow/Server - 사용자 입력 값 검증방법과 노하우 (0) | 2020.07.01 |
ApplicationKnowhow/Server - 공인 IP없이 외부에서 접속 가능하게 만들기(Ngrok란?) (0) | 2020.06.15 |