본문 바로가기
모바일앱 프로젝트

안드로이드에서 서버로 데이터 전송하기

by Nicole 2021. 3. 4.

일단..

이 포스팅을 쓰기까지 굉장히 험난했다..

retrofit 관련해서 온갖 유투브 영상들, 공식 문서들, 블로그 포스팅들을 참고하였으나,

내가 진행하는 프로젝트와 정확히 일치하는 정보는 없었기 때문에,

또, 정보가 조각조각 있었기 때문에

거의 2-3일 정도를 보고 나니 어느 정도 감이 잡혔다.

 

언어는 수단일 뿐이다.

파이썬과 자바를 어느 정도 안다고 해도 새로운 프레임워크를 사용하는 것은 다른 문제이다..

 

우선 본론으로 들어가자면,

장고 코드에 작성된 데이터 형식과 필드명과 정확히 같은 포맷으로 보내지 않는 경우 400 에러가 response 값으로 들어온다.

이 부분이 어디에도 정확히 적혀있지 않아서 몰랐다.

 

구현할 때 까지 정말 온갖 http status code 를 다봤다.

500번대의 internal error 부터 시작해서 404 not found (이건 url을 잘못 쓴 경우가 다였다.)

그리고.... 400번! 파일이 전송은 되지만 읽지를 못하는..

POST request를 보내는 경우에는 데이터까지 처리하고 201 status code를 받으면 된다.

 

우선 안드로이드에서 서버 단으로 데이터를 보낼 때 나는 우선 텍스트를 보내보고 성공한 뒤, 이미지를 전송했다.

 

 

장고 측 코드

- models.py

class Post(models.Model):
    title = models.CharField(default='title', max_length=200)
    image = models.ImageField(default='media/default_image.jpg')

모델에는 'title'과 'image' 필드가 있다.

 

 

- serializers.py

class PostSerializer(serializers.ModelSerializer):
    image = serializers.ImageField(use_url=True)

    class Meta:
        model = Post
        # fields = ['title']  # 이렇게 단일 필드인 경우는 왜인지 모르겠는데 튜플을 인식하지 않는다. 리스트로 보낸다.
        fields = ('title', 'image')

테스트 해볼 때 텍스트만 먼저 전달했었는데, 필드가 하나인 경우에 fields = ('title') 와 같이 튜플로는 계속 에러가 나서 리스트 안에 넣어주었다.

 

테스트를 하는 과정에서는 매번 db 테이블 구조를 업데이트 할 때 마다 cmd 창에서

$ python manage.py makemigrations

$ python manage.py migrate

해줘야 바뀐 테이블 정보가 업데이트 된다.

 

- MyAPI.java

@Multipart
    @POST("/posts/")
    Call<ResponseBody> post_posts(
            @Part("title") RequestBody param,
            @Part MultipartBody.Part image
    );

MyAPI 라는 인터페이스를 만들어주고,

나는 일단 POST만 사용해서 POST annotation만 사용했다.

 

여기서 중요했던 것은 main activity에서 response를 받아보기 위해서는 Call<ResponseBody> 타입을 사용해야 한다는 것이다.

그리고 텍스트와 이미지는 하나로 묶어 보내줄 수 없기 때문에 @Part 로 나눠서 전송한다.

Django 에 테이블 필드를 명시한 것과 같이 "title"과 "image" 필드를 명시해줘야 한다.

텍스트(문자열)의 경우에는 RequestBody 클래스를 통해 전송이 가능하다.

main activity에서 post_posts 메소드를 호출할 때 param 에 텍스트(문자열)을, image 파라미터에 에 이미지 파일을 전달한다.

 

 

- MainActivity.java

private void uploadImage() {
        RequestBody title = RequestBody.create(MediaType.parse("text/plain"), "sewoni");
        RequestBody requestBody = RequestBody.create(MediaType.parse("image/*"), tempSelectFile);
        MultipartBody.Part parts = MultipartBody.Part.createFormData("image", tempSelectFile.getName(), requestBody);
        
        // Retrofit 객체를 생성하고 이 객체를 이용해서, API service 를 create 해준다.
        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl("https://31a0863e0981.ngrok.io")
                .addConverterFactory(GsonConverterFactory.create());
        Retrofit retrofit = builder.build();

        MyAPI myAPI = retrofit.create(MyAPI.class);

        // post 한다는 request를 보내는 부분.
        Call<ResponseBody> call = myAPI.post_posts(title, parts);
        // 만약 서버로 부터 response를 받는다면.
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if(response.isSuccessful()){
                    Log.d(TAG,"등록 완료");
                }else {
                    Log.d(TAG,"Post Status Code : " + response.code());
                    Log.d(TAG,response.errorBody().toString());
                    Log.d(TAG,call.request().body().toString());
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.d(TAG,"Fail msg : " + t.getMessage());

            }
        });
    }

title 부분이 문자열 부분이고, media type을 저렇게 지정해줬다.

각 데이터에 다른 MIME 문법은 아래 레퍼런스에서 참고하면 된다.

parts 변수에 이미지가 전달된다.

 

몇 포스팅 전에 적었던 baseurl은 ngrok에서 임의로 만든 url을 사용했다.

두 시간 마다 새로 업데이트 해줘야 하는 url이다.

아주 유용하게 사용 중이다..

Retrofit 객체를 생성해서 빌드해주고,

내 api 객체에 텍스트와 이미지를 넣어 Request 요청을 서버로 보낸다.

서버로부터 받은 response는 위의 log들을 통해 확인할 수 있다.

역시 웹이나 앱이나 로그가 중요하다.

 

결과

로컬url에서 확인할 수 있다.
에뮬레이터에서는 이미지를 선택하고 preview를 보여준 다음, 보내기 버튼을 클릭할 시, 텍스트와 이미지가 서버로 POST된다.

 

안드로이드 logcat을 확인해보면 서버에서 response 받은 값이 출력된다.
Mysql workbench에서도 기존에 연동해 두었던 데이터베이스 테이블을 통해 데이터가 제대로 전송된 것을 확인할 수 있다.

 

쨋든 오늘은 여기까지 하도록 하고,

내일은 mysql에 연동하느라 create 메소드에서 돌아가던 딥러닝 코드가 안 돌아가는데, 그 부분을 수정해야겠다.

 

 

 

 

 

REFERENCE

참고한 레퍼런스 중 최종 결과물에 결정적으로 도움이 되었던 자료들이다.

1) MIME 문법 (개별타입과 Multipart 타입)

huiveloper.tistory.com/13

 

Retrofit2 사용해보기

레트로핏은 서버와 클라이언트 간의 데이터를 주고 받기 위해서 사용되는 하나의 도구라고 생각하면 쉽습니다. 그런데 레트로핏하면 항상 따라다니는 용어가 있죠. 바로 RESTful API입니다. 혹은 R

huiveloper.tistory.com

 

2) Retrofit 라이브러리를 사용하지는 않았지만 로직은 거의 같다.

derveljunit.tistory.com/302?category=523828

 

[빠르게 보는] 안드로이드 서버에 파일전송 with OkHttp3

스프링 서버에 안드로이드의 파일 이미지를 보내보자. 스프링 서버 파일 업로드는 [빠르게보는]스프링5 / 스프링부트 파일 업로드을 참고하자. 안드로이드 이미지 선택은 [빠르게 보는] 안드로

derveljunit.tistory.com

 

3) 안드로이드 -> 서버 데이터 전송하는 다양한 방법이 기술되어 있다.

아마 처음 접하시는 분들은 이것만 보고 syntax를 이해하기 어려울 수 있다.

큰 틀은 이러하고, 부가적인 내용은 더 찾아보면 금방 나온다.

velog.io/@dev_thk28/Android-Retrofit2-Multipart%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-Java

 

[Android] Retrofit2 Multipart사용하기 (Java)

Multipart 사용하기

velog.io

 

댓글