안드로이드에서 Serverless REST API 호출하기

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다. (예시 코드는 works on my machine 상태라 다르게 작동될 수 있습니다.)
  • Udemy강의(AWS Serverless APIs & Apps - A Complete Introduction)를 기반으로 작성되었습니다. 필자의 프로젝트 요구사항에 맞춰 일부 변형하였으니 자세한 이해를 원하신다면 결제해서 수강해보시길 권장드립니다. 좋은 강의라고 생각합니다. 항상 할인 중이니 저렴한 가격에 줍줍하세요
  • 안드로이드에서 Serverless REST API에 데이터 조회/삽입/삭제 요청합니다.

이번 글에서 할 일

AWS_Serverless_006_01

REST API를 안드로이드에서 호출하고 기능 체크를 하여 완성합니다.

안드로이드 코드 작성

MainActivity.xml 정의

이번 포스팅에 사용된 페이지 xml 코드로 최대한 간단하게 배치하기로 하였다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvUserId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        android:text="UserId" />

    <EditText
        android:id="@+id/edUserId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvUserId" />

    <TextView
        android:id="@+id/tvPrevScore"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="prevScore"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edUserId" />

    <EditText
        android:id="@+id/edPrevScore"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvPrevScore"
        android:ems="10"
        android:inputType="number" />

    <TextView
        android:id="@+id/tvPrevRank"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edPrevScore"
        android:text="prevRank"
        tools:layout_editor_absoluteY="64dp" />

    <EditText
        android:id="@+id/edPrevRank"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvPrevRank"
        android:ems="10"
        android:inputType="number" />

    <TextView
        android:id="@+id/tvCurrScore"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edPrevRank"
        android:text="currScore"
        tools:layout_editor_absoluteY="129dp" />

    <EditText
        android:id="@+id/edCurrScore"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvCurrScore"
        android:ems="10"
        android:inputType="number" />

    <TextView
        android:id="@+id/tvCurrRank"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edCurrScore"
        android:text="currRank"
        tools:layout_editor_absoluteY="45dp" />

    <EditText
        android:id="@+id/edCurrRank"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tvCurrRank"
        android:ems="10"
        android:inputType="number" />

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/edCurrRank"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btnGet"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="GET" />

        <Button
            android:id="@+id/btnPost"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="POST" />

        <Button
            android:id="@+id/btnDelete"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="DELETE" />

    </LinearLayout>

    <TextView
        android:id="@+id/tvMainText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/linearLayout"
        android:text="MAIN TEXT" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.java 정의

API 호출하여 데이터를 다루는 데 사용한 액티비티 코드이다. GET 메소드의 경우 UserId 값을 입력하지 않으면 경로 파라미터에 all을 붙이고 입력된 경우 single 을 붙이고 입력받은 UserId 값을 쿼리 문자열로 더한 뒤 URL을 구성한다.

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.app.ProgressDialog;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    private TextView tvMainText;
    private EditText edUserId, edPrevScore, edPrevRank, edCurrScore, edCurrRank;
    private Button btnGet, btnPost, btnDelete;
    private ProgressDialog progressDialog;
    private static final String TAG = "basic REST API";
    private String connMethod;
    private String mURL = "https://imdto253jj.execute-api.ap-northeast-2.amazonaws.com/dev/basic-api-res";
    private String API_KEY = "";
    private String bodyJson;
    private static final int LOAD_SUCCESS = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnGet = findViewById(R.id.btnGet);
        btnPost = findViewById(R.id.btnPost);
        btnDelete = findViewById(R.id.btnDelete);
        tvMainText = findViewById(R.id.tvMainText);
        edUserId = findViewById(R.id.edUserId);
        edPrevScore = findViewById(R.id.edPrevScore);
        edPrevRank = findViewById(R.id.edPrevRank);
        edCurrScore = findViewById(R.id.edCurrScore);
        edCurrRank = findViewById(R.id.edCurrRank);
        Button.OnClickListener ButtonListener = new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v==btnGet){
                    connMethod = "GET";
                    if (edUserId.getText().toString().equals("")) {
                        mURL = "https://imdto253jj.execute-api.ap-northeast-2.amazonaws.com/dev/basic-api-res/" + "all";
                    } else if(!edUserId.getText().toString().equals("")) {
                        mURL = "https://imdto253jj.execute-api.ap-northeast-2.amazonaws.com/dev/basic-api-res/" + "single?UserId=" + edUserId.getText().toString();
                    }
                    getJSON(mURL, connMethod);
                } else if (v==btnPost) {
                    connMethod = "POST";
                    getJSON(mURL, connMethod);
                } else if (v==btnDelete) {
                    if (edUserId.getText().toString().equals("")){
                        Toast.makeText(getApplicationContext(),"check UserId", Toast.LENGTH_SHORT);
                    } else {
                        connMethod = "DELETE";
                        getJSON(mURL, connMethod);
                    }
                }
            }
        };
        btnGet.setOnClickListener(ButtonListener);
        btnPost.setOnClickListener(ButtonListener);
        btnDelete.setOnClickListener(ButtonListener);
    }

    private final MyHandler mHandler = new MyHandler(this);
    private static class MyHandler extends Handler {
        private final WeakReference<MainActivity> weakReference;

        public MyHandler(MainActivity mainactivity) {
            weakReference = new WeakReference<MainActivity>(mainactivity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainactivity = weakReference.get();
            if (mainactivity != null) {
                switch (msg.what) {
                    case LOAD_SUCCESS:
                        String jsonString = (String)msg.obj;
                        mainactivity.tvMainText.setText(jsonString);
                        break;
                }
            }
        }
    }
    public void  getJSON(final String mUrl, final String connMethod) {
        Thread thread = new Thread(new Runnable() {
            public void run() {
                String result;
                try {
                    URL url = new URL(mUrl);
                    HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                    httpURLConnection.setReadTimeout(3000);
                    httpURLConnection.setConnectTimeout(3000);
                    httpURLConnection.setDoInput(true);
                    httpURLConnection.setRequestMethod(connMethod);
                    httpURLConnection.setRequestProperty("Content-Type", "application/json");
                    httpURLConnection.setUseCaches(false);

                    if (connMethod.equals("POST")){
                        httpURLConnection.setDoOutput(true);
                        JSONObject body = new JSONObject();
                        body.put("prevScore", Integer.parseInt(edPrevScore.getText().toString()));
                        body.put("prevRank", Integer.parseInt(edPrevRank.getText().toString()));
                        body.put("currScore", Integer.parseInt(edCurrScore.getText().toString()));
                        body.put("currRank", Integer.parseInt(edCurrRank.getText().toString()));
                        bodyJson = body.toString();
                        DataOutputStream wr = new DataOutputStream(httpURLConnection.getOutputStream());
                        wr.write(bodyJson.getBytes("EUC-KR"));
                        wr.flush();
                        wr.close();
                    } else if (connMethod.equals("DELETE")){
                        httpURLConnection.setDoOutput(true);
                        JSONObject body = new JSONObject();
                        body.accumulate("UserId", edUserId.getText());
                        bodyJson = body.toString();
                        Log.d("bodyJson",bodyJson);
                        OutputStream wr = httpURLConnection.getOutputStream();
                        wr.write(bodyJson.getBytes("EUC-KR"));
                        wr.flush();
                        wr.close();
                    }

                    httpURLConnection.connect();

                    int responseStatusCode = httpURLConnection.getResponseCode();

                    InputStream inputStream;
                    if (responseStatusCode == HttpURLConnection.HTTP_OK) {
                        inputStream = httpURLConnection.getInputStream();
                    } else {
                        inputStream = httpURLConnection.getErrorStream();
                    }

                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "EUC-KR");
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
                    StringBuilder sb = new StringBuilder();
                    String line;

                    while ((line = bufferedReader.readLine()) != null) {
                        sb.append(line);
                    }

                    bufferedReader.close();
                    httpURLConnection.disconnect();
                    result = sb.toString().trim();
                } catch (Exception e) {
                    result = e.toString();
                }
                Message message = mHandler.obtainMessage(LOAD_SUCCESS, result);
                mHandler.sendMessage(message);
            }
        });
        thread.start();
    }
}

안드로이드 어플리케이션 실행 후 REST API 호출

GET 메소드

우선 아무것도 입력하지 않아 all 경로 파라미터가 입력된 상태이며 그에 따라 하단 텍스트뷰에 DB에 저장된 모든 값이 조회되었다.

AWS_Serverless_006_02

조회하고자 하는 UserId값을 입력하여 해당 UserId 속성을 갖는 레코드의 데이터가 하단 테스트뷰에 출력되었다.

AWS_Serverless_006_03

POST 메소드

레코드 정보들을 입력하여 POST 메소드 요청을 하면 하단 텍스트뷰에 제대로 삽입되었다는 메세지가 출력된다.

AWS_Serverless_006_04

DynamoDB에 제대로 값이 삽입되어 있음을 확인하였다.

AWS_Serverless_006_05

DELETE 메소드

UserId 값에 삭제하고자 하는 값을 입력한 뒤 DELETE 메소드 요청을 하면 하단 테스트뷰에 삭제된 UserId 값이 출력된다.

AWS_Serverless_006_06

DynamoDB에 제대로 값이 삭제되었음을 확인하였다.

AWS_Serverless_006_07

마무리

  • 이로써 AWS API Gateway, AWS Lambda, AWS DynamoDB를 활용하여 Serverless REST API를 제작하고 Android Application에서 호출하는 과정까지 모두 끝냈습니다. 제작하면서 항상 웹 콘솔에서 직관적으로 데이터를 확인할 수 있다는 측면에서 DynamoDB가 확실한 이점을 가지고 있지만 엄밀하게 사전 정의된 데이터를 사용한다면 익숙한 문법을 가진 RDS와의 연동이 더 좋은 방법일 수 있지 않았을까라는 생각이 들었습니다.
  • 아무튼 AWS 서비스 관련 자료와 특히 Serverless 자료들은 많은 블로그와 자체 레퍼런스에서 소개하고 있습니다만 아예 저처럼 생기초부터 모르는 (개)초보들이 참고하기에는 조금 허들이 있다고 느껴 정리하게 되었습니다. 특히 node.js와 웹 애플리케이션 위주로 다들 사용하고 계셔서 python을 사용하려는 입장에선 더욱 동기부여가 되었습니다.
  • 굉장히 초보적인 포스팅이었기에 오류가 군데군데 있을 수 있으니 의견 주시면 지속적으로 반영하겠습니다.

+ Recent posts