[Android] API 연동 Multi Part Post 기록 (서버로 이미지 전송)

API 서버에  이미지 + 내용 데이터 전송하기

 서버에서 원하는 데이터 형식은 아래와 같다.

 

 

Posting (게시물)을 서버 쪽으로 POST 할 때 2가지의 값을 multipart/form-data 형식으로 받는다.

이미지 파일을 변환하고 나머지 데이터를 param 형식으로 전송하는 방법을 시도했다.

 

MultiPart 형식으로 데이터를 전송하기 위해 ViewModelScope를 사용했다.

 

ViewModelScope

viewModelScope는 viewModel 내부에서 사용되며,

lifecycle을 인식하는 코루틴 스코프를 만들 수 있다.

이 방식은 viewModelScope 블럭에서 실행되는 작업들을

별도의 처리 없이  ViewModel이 clear 되는 순간 자동으로 취소하게 할 수 있다.

데이터를 전송하기 위해 multipart로 데이터를 변환하는 작업은 취소하게 되면 작업 취소 과정을 생략할 수 있다.

 

새로 데이터를 추가하는 함수를 ViewModel 내부에 선언하고, 해당 함수에 viewModelScope를 적용한다.

try 구문 내에서 데이터 변환과 전송을 시도한다.

 

class MultiPartViewModel: ViewModel(){
    fun uploadCommunityPoster(productId:Int,multiCommunityData: MultiCommunityData,filePath : Uri?,
                              activity: Activity,paramFunc:(ReviewInCommunity?,String?)->Unit){
        viewModelScope.launch {
            try {
            
            /,,,,,
            
                        }catch (e:Exception){
                paramFunc(null,"error")
            }
        }

    }

 

 

1. 이미지 데이터 변환

 

    @Multipart
    @POST("boards/community/posts/")
    fun addCommunityPost(
        // param
        @Part multipartFile: MultipartBody.Part? //image
    ): Call<Review>

이미지 데이터를 전송하기 위해서는 MultipartBody.Part 형식으로 변환해야 한다.

포스팅 함수를 실행할 때 filepath라는 이미지 Uri 데이터를 가져왔다.

이 데이터를 MultipartBody.Part 형식으로 변환하면 된다.

 

//이미지 처리
val multipartFile : MultipartBody.Part? = if (filePath!=null) {
    val path = getImageFilePath(activity, filePath)
    val file = File(path)
    val imageRequestBody = file.asRequestBody()
    //이미지 데이터 생성
    MultipartBody.Part.createFormData(
        "multipartFile",
        file.name, imageRequestBody
    )

}else{ null } //이미지가 없다면 null 처리

 

path 함수를 통해서 이미지 소스를 String으로 전환한 후 file 형식으로 변환한다.

그 후에 imageReqeustBody 변수에 파일 형식을 RequestBody 형식으로 변환해서 넣어준다.

마지막으로 MultipartBody.Part에 createFormData를 통해 파일 이름과 imageReqeustBody를 넣어준다.

 

만약에 전달 된 filePath가 없을 경우 전송할 이미지 파일이 없으므로 null 값을 넣는다.

이제 param 데이터를 생성하면 된다.

 

2. param (나머지 데이터 : type, subject, text, link 등..) 변환

 

이제 서버로 param 데이터를 전송하려면 나머지 데이터를 하나로 묶어야 한다.

이 과정에서 3가지 방법을 시도했다.

 

[방법 1] : Hash Map을 통한 RequestBody 매칭

 

가정 먼저 시도한 방법인데 실패했다.

HashMap에 RequestBody로 변환한 데이터를 넣고 해당 값을 전송하는 방식이었다.

//데이터 변환
val typeRequestBody : RequestBody = multiCommunityData.type.toString()
    .toPlainRequestBody()
val subjectRequestBody : RequestBody = multiCommunityData.subject
    .toPlainRequestBody()
val textRequestBody : RequestBody = multiCommunityData.text
    .toPlainRequestBody()
val linkUrlRequestBody : RequestBody = multiCommunityData.linkUrl
    .toPlainRequestBody()
val jointProductRequestBody : RequestBody = multiCommunityData.jointProduct
    .toPlainRequestBody()

오류가 발생했는데, 데이터의 형식이 맞지 않는다는 것이었다.

 

[방법 2] : Hash Map으로 단순 key- value 형식으로 보내기

 

두 번째로 실패한 방법인데, json 형식에 맞게 키 - 밸류 형식으로 데이터를 전송했다.

 

 방법 2
 key -value 형식으로 묶음
val params = hashMapOf<String,String>()
params["type"] = multiCommunityData.type.toString()
params["subject"] = multiCommunityData.subject
params["text"] = multiCommunityData.text
params["linkUrl"] = multiCommunityData.linkUrl
params["jointProduct"] = multiCommunityData.jointProduct

 

데이터의 형식이 맞지 않는 오류가 발생해서 다시 서버쪽의 데이터 전송 방식을 살펴봤다.

서버 쪽에서는 param 데이터를 받을 때 {}에 감싸진 json 형식이 아닌 body : {} 형식을 원했다.

내가 보낸 데이터는  {d1 : "" , d2 : "" ....} 이러한 json 형식인데,

서버에서는  "body" : {d1 : "" , d2 : "" ....} 형식으로 데이터를 받고 있었다.

 

 

[방법 3] : body로 감싼 데이터 전송

 

body 데이터를 가지는 새로운 model을 선언하고, 해당 body 값으로 데이터를 넘겨주는 방식을 사용했다.

class addCommunityForPOST(
    val body : MultiCommunityData
) : Serializable

POST용 클래스를 다시 선언했다.

 

전달할 값을 그대로 addCommunityForPOST에 넣고 이를 param 데이터로 보내봤다.

val body = BodyReviewForPOST(multiCommunityData)

 

retrofit을 연결하고, 해당 데이터를 전송했더니 결과는 성공이었다.

 

서버와 API 통신을 할 경우 당연한 얘기지만, 

서버에서 받는 방식으로 데이터를 변환해서 보내야 한다.

 

멀티파트를 사용하는 곳에서 에러가 발생한 줄 알았지만 멀티파트 전송 부분은 정상적으로 작동했다.,