Android Network Programming 2

2023. 12. 14. 14:34·Android

 

 

Android Network Programming 1

이 글 을 마지막으로 기본적인 Docker 기반의 서버 구축이 완료 되었습다. 추후 테이블도 변경하고 수정할 사항이 많겠지만 빨리 내가 직접 만든 서버와 통신을 해보고 싶어서 메다닥 달려왔습니

chanho-study.tistory.com

 

이전 글에서는 Retrofit Annotation을 알아보며 마무리 했었습니다. 이번엔 Retrofit으로 이미지를 전송하기 위한 Annotation에 대해 알아볼게요. 이 함수는 이미지 전송을 위한 예시 함수입니다. 하나씩 뜯어보죠

@Multipart
@POST(URL_TO_UPLOAD_IMAGE)
suspend fun uploadImage(
    @Part image: MultipartBody.Part
): Response<ImageDataClass>

Multipart

HTTP 클라이언트가 파일, 데이터등 여러 종류의 데이터를 동시에 HTTP 서버로 보내기 위한 방식으로 이름에서 유추할 수 있듯이 웹 클라이언트에서 요청을 보낼 때, HTTP Protocol Body에 데이터를 여러 부분으로 나눠 보내는 것 입니다. 

안드로이드에서 이미지를 전송하기 위해선 @Multipart 어노테이션을 명시해 줍니다.

https://square.github.io/retrofit/2.x/retrofit/retrofit2/http/Multipart.html

 

Multipart (Retrofit 2.7.1 API)

 

square.github.io

Part

Multipart 요청의 한 파트를 나타 냅니다.  @Multipart 와 같이 쓰이며 multipart 요청의 각 부분을 표현 합니다.  공식문서에 의하면 Part 어노테이션이 있는 매개변수 타입은 3가지가 있습니다.

https://square.github.io/retrofit/2.x/retrofit/index.html?retrofit2/http/Part.html

 

Retrofit 2.7.1 API

 

square.github.io

  • Multipart.Part Type : @Part MultipartBody.Part part와 같이 어노테이션을 사용하면 해당 파트의 내용이 직접 사용됩니다.파트의 이름을 어노테이션에서 생략할 수 있습니다.
  • RequestBody Type :  @Part("foo") RequestBody foo와 같이 어노테이션을 사용하면 RequestBody의 값이 직접 사용되며, 해당 값의 컨텐츠 타입이 함께 사용됩니다. 파트의 이름을 어노테이션에서 지정해주어야 합니다.
  • 다른 객체 타입인 경우: @Part("foo") Image photo와 같이 어노테이션을 사용하면, 해당 객체는 Retrofit의 컨버터를 통해 적절한 형태로 변환됩니다. 파트의 이름을 어노테이션에서 지정해주어야 합니다.
@Multipart
@POST("/db/upload") // 서버 엔드포인트 URL, HTTP POST 요청
suspend fun registStore(
    @Part storeimage: MultipartBody.Part, // 이미지 데이터를 나타내는 파라미터
    @Part("storename") storename: RequestBody,
    @Part("ceoName") ceoName: RequestBody,
    @Part("CRN") CRN: RequestBody,
    @Part("contact") contact: RequestBody,
    @Part("address") address: RequestBody,
    @Part("latitude") latitude: RequestBody,
    @Part("longitude") longitude: RequestBody,
    @Part("kind") kind: RequestBody
): ResponseBody
  • 위 예시를 REST 서버로 이미지와 텍스트 데이터를 전송합니다. 이미지를 전송하기 위해 @Multipart 어노테이션을 사용하고 REST API의 POST를 사용해 데이터를 전송했습니다.
  • 이미지는 Multypartbody.part 타입으로 보내고 그 외 String Type 데이터는 RequestBody 타입으로 전송했습니다.
val file = File(absolutelyPath(imgUri, context))
val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull())
val body = MultipartBody.Part.createFormData("storeimage", file.name, requestFile)

// 절대경로 변환
fun absolutelyPath(path: Uri, context : Context): String {
    val proj: Array<String> = arrayOf(MediaStore.Images.Media.DATA)
    val c: Cursor? = context.contentResolver.query(path!!, proj, null, null, null)
    val index = c?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
    c?.moveToFirst()

    val result = c?.getString(index!!)

    return result!!
}
  • file : 갤러리에서 가저온 이미지 Uri와 context를 전달해 파일의 절대 경로를 저장합니다.
  • requestFile : RequestBody 파일 객체를 생성하고 MIME Type은 "image/*" 로 지정합니다.
  • body : Multipary.part 객체를 생성합니다.

📌 Error Cause !

개요

이미지와 함께 매장 정보를 서버에 등록하는 과정에서 매장 정보를 객체로 보내면 코드가 더 줄어들것이라는 생각에 @Body 어노테이션을 사용해 객체를 전달하도록 수정했지만 Exception이 발생했습니다. 

storeBasicInfo.class

@Multipart
@POST("db/upload")
fun registStore(
    @Part storeimage: MultipartBody.Part,
    @Body requestBody: StoreBasicInfo // Error
) : Call<String>

 

@Body를 사용하면 HTTP 요청의 body에 객체를 직렬화해서 넣어줍니다.

하지만 @Multipart나 @FormUrlEncoded와 함께 사용할 수 없습니다. 

 

일반적으로 HTTP 요청은 하나의 본문만을 가지며, 이 본문은 단일 데이터 형식이여야 합니다.

@Body 어노테이션을 사용하면 해당 어노테이션이 붙은 매개변수를  HTTP 요청의 본문으로 사용해 이후에 오는 @Multipart나 @FormUrlEncoded 어노테이션과 충돌이 발생합니다 !! 

 

이를 해결하기 위해 객체로 만들어 주었던 파라미터를 모두 @Part로 나눠 전송해주었습니다.

interface StoreInfoApi {
    /**
     * Multipart : 이미지, 오디오, 비디오 등과 같은 여러 종류의 데이터를 서버에 업로드하거나 전송할 때 사용
     * 업로드할 데이터를 @Part 어노테이션을 사용해 파라미터로 지정
     * */
    @Multipart
    @POST("/db/upload") // 서버 엔드포인트 URL, HTTP POST 요청
    fun registStore(
        @Part storeimage: MultipartBody.Part, // 이미지 데이터를 나타내는 파라미터
        @Part("storename") storename: RequestBody,
        @Part("ceoName") ceoName: RequestBody,
        @Part("CRN") CRN: RequestBody,
        @Part("contact") contact: RequestBody,
        @Part("address") address: RequestBody,
        @Part("latitude") latitude: RequestBody,
        @Part("longitude") longitude: RequestBody,
        @Part("kind") kind: RequestBody
    ): Call<String>
     // 서버로부터 받은 응답을 처리하기 위한 Retrofit Call 객체를 반환
     // @Part로 나눠진 파라미터를 하나의 객체로 만들어 전송

}

 

'Android' 카테고리의 다른 글

Retrofit Network Time out  (0) 2023.12.15
안드로이드 HTTP 보안 정책  (0) 2023.12.14
Android Network Programming 1  (2) 2023.12.13
Android Studio MySQL 연동 6  (0) 2023.12.08
Android Studio MySQL 연동 5  (0) 2023.12.08
'Android' 카테고리의 다른 글
  • Retrofit Network Time out
  • 안드로이드 HTTP 보안 정책
  • Android Network Programming 1
  • Android Studio MySQL 연동 6
빨주노초잠만보
빨주노초잠만보
  • 빨주노초잠만보
    과거의 나를 통해 미래의 나를 성장시키자
    빨주노초잠만보
  • 전체
    오늘
    어제
    • 분류 전체보기 (108)
      • 우아한테크코스 (6)
      • TEKHIT ANDROID SCHOOL (4)
      • Android Architecture (8)
      • Android (38)
      • PROJECT (11)
      • KOTLIN (10)
        • 코루틴의 정석 (3)
      • BACK END (12)
      • CS (4)
      • 컨퍼런스 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    STATEFLOW
    android clean architecture
    Clean Architecture
    callbackflow
    thread Context Switching
    Room
    MVI
    DI
    안드로이드 디자인 시스템
    flow
    Throttle
    Two pass process
    Compose Typography
    DataSource
    orbit
    컴포즈 디자인 시스템
    coroutine Context Switching
    sealed class
    view 생명주기
    process Context Switching
    2025 우아콘 후기
    retrofit call
    value class
    android Room
    android view lifecylce
    Repository Pattern
    repository
    코틀린 코루틴의 정석
    의존성 주입
    ThrottleFirst
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
빨주노초잠만보
Android Network Programming 2
상단으로

티스토리툴바