https://freekim.tistory.com/4

 

Django REST framework 예제

1. Restframework 설치 - 터미널에 아래의 명령어를 입력시켜 준다. pip install djangorestframework - settings.py 에 rest framework 추가 해준다. INSTALLED_APPS = [ 'django.contrib.admin', 'django.contri..

freekim.tistory.com

위의 예제에서 작성된 django 서버와 연동하는 예제이다.

Server 쪽의 예제가 필요하면 위의 링크를 참고 하시길.

 

Retrofit ?

 - 서버에 Restful API 에 맞는 요청을 보내기 위해 사용하는 위한 통신 인터페이스이다. AsyncTask 와 HttpUrlConnection 을 사용하여 구현 할 수는 있지만 기본적으로 필요한 요구사항들을 쉽게 처리할 수 있게하는 도구이다.

 

 

Retrofit의 github 이다 예제도 잘 나와 있다.

http://devflow.github.io/retrofit-kr/

 

Retrofit - 한글 문서

A type-safe HTTP client for Android and Java

devflow.github.io

1. build.gradle 추가하기

현재 2.6.1 이 최신 버전이라 최신버전을 추가 했다.

Module: app


dependencies {
    ....
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
    ....
}

2. Manifest Permission 등록

internet을 사용하므로 internet permission 을 추가해준다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.retrofitexample">

    <uses-permission android:name="android.permission.INTERNET"/>
    
    ...
    
</manifest>

3. Data 객체 정의

우리가 server의 model에서 구성했던것과 동일하게 구성해준다.

PostItem.java

package com.example.retrofitexample;

public class PostItem {
    private String title;
    private String text;

    public String getText() {
        return text;
    }

    public String getTitle(){
        return title;
    }

    public void setTitle(String s){
        title = s;
    }

    public void setText(String s){
        text = s;
    }
}

4. Retrofit Interface 구성

Call 을 통해서 웹서버에 요청을 보낼 수 있다.

1. Http요청을 어노테이션으로 명시

2. URL Parameter와 Query Parameter 사용이 가능하다.

 ex) @GET("/group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort)

3. 객체는 Body 로 json형태로 전달한다.

url 끝에 / 를 빼먹으면 error 가 발생할 수 있으니 유의바란다.

 

MyAPI.java

package com.example.retrofitexample;

import java.util.List;

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.GET;
import retrofit2.http.PATCH;
import retrofit2.http.POST;
import retrofit2.http.Path;

public interface MyAPI{

    @POST("/posts/")
    Call<PostItem> post_posts(@Body PostItem post);

    @PATCH("/posts/{pk}/")
    Call<PostItem> patch_posts(@Path("pk") int pk, @Body PostItem post);

    @DELETE("/posts/{pk}/")
    Call<PostItem> delete_posts(@Path("pk") int pk);

    @GET("/posts/")
    Call<List<PostItem>> get_posts();

    @GET("/posts/{pk}/")
    Call<PostItem> get_post_pk(@Path("pk") int pk);
}

5. 동작 확인을 위한 초기화 및 간단한 동작 구현

단순 동작 확인 을 위하여 MainActivity 에서 모두 구현하였다.

부분 부분 설명 후 전체 코드는 밑에 있다. 하나씩 따라해 보자.

 

Retrofit 객체를 생성하고 이 객체를 이용해서, API service 를 create 해준다.

private void initMyAPI(String baseUrl){
    Log.d(TAG,"initMyAPI : " + baseUrl);
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    mMyAPI = retrofit.create(MyAPI.class);
}

내가 정의한 api 중에서 get 동작을 살펴보자.

mMyAPI.get_posts(); 함수를 이용해서 Call 객체를 불러온다.

Call 객체에 Callback을 넣어준다. 응답이 왔을 때에는 onResponse 함수로 가게 되고 응답이 안왔으면 Throwable 객체에 메시지를 담아서 onFailure 가 호출된다.

성공적인 response 가 도착 했다면 response.body() 에 우리가 서버로부터 받아온 데이터가 들어있다.

Call<List<PostItem>> getCall = mMyAPI.get_posts();
getCall.enqueue(new Callback<List<PostItem>>() {
    @Override
    public void onResponse(Call<List<PostItem>> call, Response<List<PostItem>> response) {
        if( response.isSuccessful()){
            List<PostItem> mList = response.body();
            String result ="";
            for( PostItem item : mList){
                result += "title : " + item.getTitle() + " text: " + item.getText()+ "\n";
            }
            mListTv.setText(result);
        }else {
            Log.d(TAG,"Status Code : " + response.code());
        }
    }

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

GET Button Listener 를 달아 위의 event를 등록을 하고 GET Button을 누르면 아래와 같은 결과를 볼 수 있다.

나머지 동작은 아래 Code로 첨부하니 동작 확인 해보시길..

모든 동작을 확인 한 후 서버의 상태이다.

 

MainActivity.java

package com.example.retrofitexample;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private final  String TAG = getClass().getSimpleName();

    // server의 url을 적어준다
    private final String BASE_URL = "http://27ad1bf3.ngrok.io";
    private MyAPI mMyAPI;

    private TextView mListTv;

    private Button mGetButton;
    private Button mPostButton;
    private Button mPatchButton;
    private Button mDeleteButton;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mListTv = findViewById(R.id.result1);

        mGetButton = findViewById(R.id.button1);
        mGetButton.setOnClickListener(this);
        mPostButton = findViewById(R.id.button2);
        mPostButton.setOnClickListener(this);
        mPatchButton = findViewById(R.id.button3);
        mPatchButton.setOnClickListener(this);
        mDeleteButton = findViewById(R.id.button4);
        mDeleteButton.setOnClickListener(this);

        initMyAPI(BASE_URL);
    }

    private void initMyAPI(String baseUrl){

        Log.d(TAG,"initMyAPI : " + baseUrl);
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        mMyAPI = retrofit.create(MyAPI.class);
    }


    @Override
    public void onClick(View v) {
        if( v == mGetButton){
            Log.d(TAG,"GET");
            Call<List<PostItem>> getCall = mMyAPI.get_posts();
            getCall.enqueue(new Callback<List<PostItem>>() {
                @Override
                public void onResponse(Call<List<PostItem>> call, Response<List<PostItem>> response) {
                    if( response.isSuccessful()){
                        List<PostItem> mList = response.body();
                        String result ="";
                        for( PostItem item : mList){
                            result += "title : " + item.getTitle() + " text: " + item.getText() + "\n";
                        }
                        mListTv.setText(result);
                    }else {
                        Log.d(TAG,"Status Code : " + response.code());
                    }
                }

                @Override
                public void onFailure(Call<List<PostItem>> call, Throwable t) {
                    Log.d(TAG,"Fail msg : " + t.getMessage());
                }
            });
        }else if(v == mPostButton){
            Log.d(TAG,"POST");


            PostItem item = new PostItem();
            item.setTitle("Android title");
            item.setText("Android text");
            Call<PostItem> postCall = mMyAPI.post_posts(item);
            postCall.enqueue(new Callback<PostItem>() {
                @Override
                public void onResponse(Call<PostItem> call, Response<PostItem> response) {
                    if(response.isSuccessful()){
                        Log.d(TAG,"등록 완료");
                    }else {
                        Log.d(TAG,"Status Code : " + response.code());
                        Log.d(TAG,response.errorBody().toString());
                        Log.d(TAG,call.request().body().toString());
                    }
                }

                @Override
                public void onFailure(Call<PostItem> call, Throwable t) {
                    Log.d(TAG,"Fail msg : " + t.getMessage());
                }
            });
        }else if( v == mPatchButton){
            Log.d(TAG,"PATCH");
            PostItem item = new PostItem();
            item.setTitle("android patch title");
            item.setText("android patch text");
            //pk 값은 임의로 하드코딩하였지만 동적으로 setting 해서 사용가능
            Call<PostItem> patchCall = mMyAPI.patch_posts(1,item);
            patchCall.enqueue(new Callback<PostItem>() {
                @Override
                public void onResponse(Call<PostItem> call, Response<PostItem> response) {
                    if(response.isSuccessful()){
                        Log.d(TAG,"patch 성공");
                    }else{
                        Log.d(TAG,"Status Code : " + response.code());
                    }
                }

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


        }else if( v == mDeleteButton){
            Log.d(TAG,"DELETE");
            // pk 값은 임의로 변경가능
            Call<PostItem> deleteCall = mMyAPI.delete_posts(2);
            deleteCall.enqueue(new Callback<PostItem>() {
                @Override
                public void onResponse(Call<PostItem> call, Response<PostItem> response) {
                    if(response.isSuccessful()){
                        Log.d(TAG,"삭제 완료");
                    }else {
                        Log.d(TAG,"Status Code : " + response.code());
                    }
                }

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

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/result1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="get"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="post"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="patch"/>
    <Button
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="delete"/>




</LinearLayout>

'Android' 카테고리의 다른 글

[Android] Retrofit http 400 Error 확인 방법  (0) 2019.09.25

장고 server 를 테스트 하기 위해서 웹을 사용한다면 로컬에서도 충분히 확인을 할 수 있지만

IOS 나 Android 개발을 할 때 에 서버가 필요한 경우가 있다.

물론 돈을 내고 호스팅을 하거나 집에 서버 컴퓨터가 있으면 걱정이 없겠지만

단순히 테스트 용도로 사용하기에는 무리가 있다.

여러 방법을 찾던중 ngrok 을 사용하여 5분만에 내 컴퓨터를 테스트용 홈서버로 만드는 방법을 소개 하고자 한다.

 

1. ngrok download

https://ngrok.com/download 에 접속하여 ngork 을 다운로드 받는다.

 

2. django server 실행

 - 아래의 명령어로 장고 서버를 실행해 준다. 0.0.0.0:8000 의미는 현재 요청된 호스트의 IP나 이름에 상관없이 실행한다는 의미이다.

$ python manage.py runserver 0.0.0.0:8000

 - django project의 settings.py 수정

ALLOWED_HOSTS = ['*']

3. ngrok 실행

 - ngrok을 실행해보면 아래의 커맨드 화면이 보인다.

그리고 내가 연 포트번호를 넣어 아래의 명령어를 실행해준다.

>ngrok http 8000

 주소가 부여되는데 핸드폰 단말에서 해당 주소로 들어가면 내가 구성해놓은 rest api 페이지를 볼 수 있다.

 

ngrok 은 도메인을 할당하는 것 처럼 ngrok 을 통해서 고정 아이피와 나의 서버를 바인딩 해준다. 다른 서버를 들렸다 오는 것이므로 속도가 느리고 테스트용도로만 사용하자.

1. Restframework 설치

 - 터미널에 아래의 명령어를 입력시켜 준다.

pip install djangorestframework

 - settings.py 에 rest framework 추가 해준다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
]

2. 어플리케이션 생성

 - 어플리케이션을 생성한다.

$ python manage.py startapp myapp

 - setting.py 에 추가한다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'myapp'
]

3. 모델 생성

 myapp/models.py 에 간단하게 아래의 post model class 를 추가해준다.

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    text = models.TextField()

아래의 명령어로 데이터 베이스에 모델을 위한 테이블을 형성한다.

$ python manage.py makemigrations myapp
$ python manage.py migrate

4. Serializers

쿼리셋, 모델 인스턴스 등의 복잡한 데이터를 json, xml 등의 데이터로 변환해준다.

-  myapp/serializers.py 생성 후 아래의 코드를 입력한다.

from rest_framework import  serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('title', 'text')

 

5. Views

 Viewset 하나에 다양한 method를 포함하고 있다.

 - myapp/views.py 수정한다.

from rest_framework import viewsets
from .serializers import PostSerializer
from .models import Post

class PostViewset(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

 

6. urls 

router 를 설정해 준다. router 는 URL을 자동으로 맵핑해준다.

 - project/urls

from rest_framework import routers
from myapp import views
from django.conf.urls import url, include

router = routers.DefaultRouter()
router.register(r'posts', views.PostViewset)


urlpatterns = [
    url(r'^',include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

 

7. 서버 실행 후 동작 확인

$ python manage.py runserver

 - http://127.0.0.1:8000/

서버를 실행 후 위 주소로 들어가면 Default router에 의해서 접근할 수 있는 URL 인 http://127.0.0.1:8000/posts가 표시된다.

http://127.0.0.1:8000/posts에 접속을 해보면 아래와 같이 post list를 볼 수 있다. 현재는 데이터를 하나도 등록을 안시켰으므로 비어 있다.

하단의 title 과 text 에 test 문구를 넣어주고 post 버튼을 눌러보자. post 를 누르면 post된 data만 보이게되고

다시 오른쪽 상단의 get버튼으로 list를 다시 받아오면 아래와 같이 내가 등록했던 data list를 볼 수 있다.

 

기본적으로 rest framework 을 구성해 보았다. 

다음 시간에는 안드로이드와 연결되서 실제로 어떻게 쓸 수 있는지 알아보고자 한다.

+ Recent posts