[1.안드로이드 - AndroidManifest.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloworld">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".subActivity"
            android:label="@string/login_page"></activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>
 
 

[2.안드로이드 - MainActivity.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.helloworld;
 
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        Button button = (Button) findViewById(R.id.login_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), subActivity.class);
                startActivity(intent);
            }
        });
    }
}
 
 

[3.안드로이드 - subActivity]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
package com.example.helloworld;
 
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
 
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
 
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutionException;
 
 
public class subActivity extends AppCompatActivity {
    private EditText idEditText;
    private EditText pwdEditText;
    private String idText,pwdText;
    private String mJsonString;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sub);
 
        idEditText = (EditText) findViewById(R.id.input_id);
        pwdEditText = (EditText) findViewById(R.id.input_pwd);
 
        idEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int id, KeyEvent event) {
                if(id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL){
                    return true;
                }
                return false;
            }
        });
 
        pwdEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int pwd, KeyEvent event) {
                if(pwd == EditorInfo.IME_ACTION_DONE || pwd == EditorInfo.IME_NULL){
                    // 함수 실행
                    return true;
                }
                return false;
            }
        });
 
        Button login_btn = (Button) findViewById(R.id.btn_login);
        Button sign_up_btn = (Button) findViewById(R.id.btn_sign_up);
 
        login_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                idText = idEditText.getText().toString();
                pwdText = pwdEditText.getText().toString();
                if(idText != "" && pwdText != ""){
                    // AsyncTask 를 통해 HttpURLConnection 수행.
                    try{
                        loginTask task = new loginTask();
                        task.execute("http://아이피/android_server/connectDB.php","m_id",idText,"m_pwd",pwdText);
                        String callBackValue = task.get();
 
                        AlertDialog.Builder builder = new AlertDialog.Builder(subActivity.this);
 
                        // fail
                        if(callBackValue.isEmpty() || callBackValue.equals(""|| callBackValue == null || callBackValue.contains("Error")) {
                            builder.setTitle("로그인 실패").setMessage("입력한 회원 정보가 없습니다.");
                            AlertDialog alertDialog = builder.create();
                            alertDialog.show();
                        }
                        // success
                        else {
                            System.out.println(callBackValue);
                            try{
                                JSONObject jsonObject = new JSONObject(callBackValue);
                                JSONArray jsonArray = jsonObject.getJSONArray("member");
 
                                for(int i = 0 ; i<jsonArray.length(); i++){
                                    JSONObject item = jsonArray.getJSONObject(i);
 
                                    String id = item.getString("m_id");
                                    String password = item.getString("m_pwd");
                                    String phone = item.getString("m_phone");
                                    String address = item.getString("m_address");
                                    System.out.println("id : "+ id);
                                    System.out.println("password : "+ password);
                                    System.out.println("phone : "+ phone);
                                    System.out.println("address : "+ address);
                                }
                            }catch (JSONException e) {
                                System.out.println("json_error : "+ e);
                            }
                            /*
                            if(callBackValue == "1"){
                                builder.setTitle("로그인 성공").setMessage("메인 페이지로 이동합니다.");
                                AlertDialog alertDialog = builder.create();
                                alertDialog.show();
                            }else{
                                builder.setTitle("로그인 실패").setMessage("입력한 회원 정보가 없습니다.");
                                AlertDialog alertDialog = builder.create();
                                alertDialog.show();
                            }
                            */
                            // TODO : callBackValue 를 이용해서 코드기술
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    }
 
                }
                /*
                // 알림창
                AlertDialog.Builder builder = new AlertDialog.Builder(subActivity.this);
                builder.setTitle("알림창").setMessage("로그인 버튼 클릭");
 
                builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //Toast.makeText(getApplicationContext(),"Ok click", Toast.LENGTH_SHORT).show();
                    }
                });
 
                builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //Toast.makeText(getApplicationContext(),"Cancel click", Toast.LENGTH_SHORT).show();
                    }
                });
 
                AlertDialog alertDialog = builder.create();
                alertDialog.show();
                */
            }
        });
 
        sign_up_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 알림창
                AlertDialog.Builder builder = new AlertDialog.Builder(subActivity.this);
                builder.setTitle("알림창").setMessage("로그인 버튼 클릭");
 
                builder.setPositiveButton("Ok"new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(getApplicationContext(),"OK click", Toast.LENGTH_SHORT).show();
                    }
                });
 
                builder.setNegativeButton("Cancel"new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(getApplicationContext(),"Cancel click", Toast.LENGTH_SHORT).show();
                    }
                });
 
                AlertDialog alertDialog = builder.create();
                alertDialog.show();
            }
        });
    }
    private class loginTask extends AsyncTask<String,String,String>{
        @Override
        protected String doInBackground(String... params) {
            String serverURL = (String) params[0];
 
            String key = (String) params[1];
            String value = (String) params[2];
 
            String key2 = (String) params[3];
            String value2 = (String) params[4];
            String postParameters = key + "=" + value + "&" + key2 + "=" + value2;
            try{
                URL url = new URL(serverURL); // 주소가 저장된 변수를 이곳에 입력합니다.
 
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setReadTimeout(5000); //5초안에 응답이 오지 않으면 예외가 발생합니다.
                httpURLConnection.setConnectTimeout(5000); //5초안에 연결이 안되면 예외가 발생합니다.
                httpURLConnection.setRequestMethod("POST"); //요청 방식을 POST로 합니다.
                httpURLConnection.connect();
 
                OutputStream outputStream = httpURLConnection.getOutputStream();
                outputStream.write(postParameters.getBytes("UTF-8")); //전송할 데이터가 저장된 변수를 이곳에 입력합니다.
                outputStream.flush();
                outputStream.close();
 
                // 응답을 읽습니다.
 
                int responseStatusCode = httpURLConnection.getResponseCode();
                InputStream inputStream;
                if (responseStatusCode == HttpURLConnection.HTTP_OK) {
                    // 정상적인 응답 데이터
                    inputStream = httpURLConnection.getInputStream();
                } else {
                    // 에러 발생
                    inputStream = httpURLConnection.getErrorStream();
                }
 
                InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
 
                StringBuilder sb = new StringBuilder();
                String line = null;
 
                while ((line = bufferedReader.readLine()) != null) {
                    sb.append(line);
                }
 
                bufferedReader.close();
 
                return sb.toString();
 
            } catch (Exception e) {
                return new String("Error: " + e.getMessage());
            }
        }
 
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
 
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            //System.out.println("결과값 : "+ s);
            /*
            mJsonString = s;
            showResult();
            */
        }
 
    }
 
}
 
 
 

[4.안드로이드 - activity_main.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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"
    android:weightSum="9">
    <Button
        android:id="@+id/login_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="로그인"
        android:textColor="#ffffff"
        android:textSize="40sp"
        android:background="#0066ff"
        android:gravity="center"
        android:layout_weight="3"/>
    <Button
        android:id="@+id/prevent_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="상품 예약"
        android:textColor="#ffffff"
        android:textSize="40sp"
        android:background="#ffcc33"
        android:gravity="center"
        android:layout_weight="3"/>
    <Button
        android:id="@+id/question_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="상담 문의"
        android:textColor="#ffffff"
        android:textSize="40sp"
        android:background="#66cc00"
        android:gravity="center"
        android:layout_weight="3"/>
</LinearLayout>
 
 
 

[5.안드로이드 - activity_sub.xml]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal">
 
    <LinearLayout
        android:id="@+id/email_login_form"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginRight="20sp"
        android:layout_marginLeft="20sp"
        android:gravity="center"
        android:orientation="vertical">
 
        <EditText
            android:id="@+id/input_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/user_id"
            android:inputType="textEmailAddress"
            android:maxLines="1"
            android:singleLine="true" />
 
        <EditText
            android:id="@+id/input_pwd"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/user_pwd"
            android:inputType="textPassword"
            android:maxLines="1"
            android:singleLine="true" />
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
 
            <Button
                android:id="@+id/btn_login"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:layout_weight="1"
                android:text="@string/action_login"
                android:textStyle="bold" />
 
            <Button
                android:id="@+id/btn_sign_up"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:layout_weight="1"
                android:text="@string/action_sign_up"
                android:textStyle="bold" />
        </LinearLayout>
 
    </LinearLayout>
</LinearLayout>
 
 

[6.conectDB.php]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
 
    header('content-type: text/html; charset=utf-8'); 
    //MYSQL 접속 정보
    $db = mysqli_connect("서버주소","디비사용자","디비 비번","디비 이름");
    if(!$db){
        die("Error ".mysqli_connect_error());
    }
    //한글
    mysqli_set_charset($db'UTF8');
 
    $id = $_POST['m_id'];
    $pwd = $_POST['m_pwd'];
    $result_arr = array();
    $result = $db->query("SELECT * FROM member WHERE m_id='$id' AND m_pwd = '$pwd'");
    $num = $result->num_rows;
    if($num > 0){
        $low = $result->fetch_assoc();
        array_push($result_arr,$low);
        header('Content-Type: application/json; charset=utf8');
        echo json_encode(array("member"=>$result_arr));
    }else{
        echo false;
    }
 
    mysqli_close($db);
?>
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs

[7.결과 화면]

'안드로이드' 카테고리의 다른 글

안드로이드 웹뷰 사용하기  (0) 2019.06.04
안드로이드 REST API 사용법  (0) 2019.06.03

1. 웹뷰 란?

웹페이지를 안드로이드 어플리케이션 화면 상에 보여주는 컴포넌트 입니다. 여기에 플레이 스토어에 출시하기 위한 네이티브 기능들을 덧붙이면 하이브리드 앱이 됩니다.

 

웹뷰의 장점은 웹에서 작성한 코드를 어플리케이션으로 다시 작성 할 필요가 없다는 점입니다.


2. 웹뷰 사용하기

AndroidManifest.xml 에서 인터넷 접속 권한을 허용하는 아래와 같은 코드 추가

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

activity_main.xml 에서 웹뷰 레이아웃 생성

1
2
3
4
5
6
<WebView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/webView"
        >
</WebView>
 

 

styles.xml 에서 타이틀바 비활성화

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<resources>
 
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
 
        <!--No Title Bar-->
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
 
    </style>
 
</resources>
 
 

 

 

MainActivity.java 전체 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.example.mywebview;
 
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 
public class MainActivity extends AppCompatActivity {
 
    private WebView mWebView;
    private WebSettings mWebSettings;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // 웹뷰 셋팅
        mWebView = (WebView) findViewById(R.id.webView);                //xml 자바코드 연결
 
        mWebSettings = mWebView.getSettings();
        mWebSettings.setJavaScriptEnabled(true);
        mWebSettings.setSupportMultipleWindows(false);                  //새창 띄우기 허용 여부
        mWebSettings.setJavaScriptCanOpenWindowsAutomatically(false);   //자바스크립트 새창(멀티뷰) 띄우기 허용 여부
        mWebSettings.setUseWideViewPort(true);                          //화면 사이즈 맞추기 허용 여부
        mWebSettings.setSupportZoom(false);                             //화면 줌 허용 여부
        mWebSettings.setBuiltInZoomControls(false);                     //화면 확대 축소 허용 여부
        mWebSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);     //컨텐츠 사이즈 맞추기
        mWebSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);           //브라우저 캐시 허용 여부
        mWebSettings.setDomStorageEnabled(true);                        //로컬저장소 허용 여부
        mWebSettings.setSaveFormData(true);                             //입력된 데이터 저장 허용 여부
 
        mWebView.loadUrl("http://192.168.0.2:80");                      //웹뷰 실행
        mWebView.setWebChromeClient(new WebChromeClient());             //웹뷰에 크롬 사용 허용//이 부분이 없으면 크롬에서 alert가 뜨지 않음
        mWebView.setWebViewClient(new WebViewClientClass());            //새창열기 없이 웹뷰 내에서 다시 열기//페이지 이동 원활히 하기위해 사용
    }
 
    //뒤로가기 버튼 이벤트
    //웹뷰에서 뒤로가기 버튼을 누르면 뒤로가짐
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
            mWebView.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
 
    // 페이지 이동
    private class WebViewClientClass extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Log.d("check URL",url);
            view.loadUrl(url);
            return true;
        }
    }
}
 
 
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

실행화면

 

 

 

원본 글 https://code.tutsplus.com/ko/tutorials/android-from-scratch-using-rest-apis--cms-27117

 

안드로이드 처음부터 배우기: REST API 사용하기

인터넷은 우리 삶의 중요한 한 부분으로서 우리 중 대부분은 새로운 정보에 대한 탐욕적인 욕구를 발달시켜왔습니다. 우리의 주의 지속 시간도 그 어느 때보다도 짧아서 콘텐츠가 정적인 안드로이드 애플리케이션을 개발하는 것은 좋지 않은 생각일 수 있습니다. 대신 사용자가 열 때마다 새로운 내용을 표시할 수 있는 애플리케이션을 만드는 것을 고려해야 합니다....

code.tutsplus.com


1. 인터넷 접속 활성화

 

REST API를 사용하려면 당연히 인터넷을 이용할 수 있어야 합니다. 하지만 안드로이드 애플리케이션은 android.permission.INTERNET 권한이 있는 경우에만 인터넷에 접속할 수 있습니다. 따라서 네트워킹 코드 작성을 시작하기 전에 프로젝트의 매니페스트 파일에 다음과 같은 uses-permission 태그가 있는지 확인해야 합니다.

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

android.permission.INTERNET은 위험한 권한으로 간주되지 않으므로 런타임 시 API 레벨 23 이상을 실행하는 기기를 대상으로는 요청할 필요가 없습니다.


2. 백그라운드 스레드 생성

안드로이드 플랫폼에서는 애플리케이션의 메인 스레드에서 네트워크 작업을 수행하는 것을 허용하지 않습니다. 따라서 모든 네트워킹 코드는 백그라운드 스레드에 속해야 합니다. 그러한 스레드를 만드는 가장 쉬운 방법은 AsyncTask 클래스의 execute() 메서드를 사용하는 것입니다. execute() 메서드는 유일한 인자로 Runnable 객체를 받습니다.

1
2
3
4
5
6
7
AsyncTask.execute(new Runnable() {
    @Override
    public void run() {
        // All your networking logic
        // should be here
    }
});
 
 

백그라운드 스레드에서 작업을 실행하는 것에 대해 자세히 알고 싶다면 '안드로이드 처음부터 배우기(Android From Scratch)' 시리즈의 백그라운드 작업에 대한 튜토리얼을 읽어보길 바랍니다.

 

Android From Scratch: Background Operations

Aside from the most basic of Android applications, everything you build will require at least some use of background threading to perform an operation. This is because Android has something known...

code.tutsplus.com


3. HTTP 연결 생성

URL 클래스의 openConnection() 메서드를 이용하면 어떤 REST 엔드포인트에 대한 연결도 빠르게 설정할 수 있습니다. 엔드포인트에 HTTP 또는 HTTPS를 통해 접속하는지 여부에 따라 openConnection()의 반환값을 HttpURLConnection이나 HttpsURLConnection 인스턴스로 형변환해야 합니다. HttpURLConnection HttpsURLConnection 모두 요청 헤더를 추가하거나 응답을 읽는 것과 같은 작업을 허용합니다.

 

다음 코드는 GitHub API의 루트 엔드포인트와의 연결을 설정하는 방법을 보여줍니다.

1
2
3
4
5
6
// Create URL
URL githubEndpoint = new URL("https://api.github.com/");
 
// Create connection
HttpsURLConnection myConnection =
        (HttpsURLConnection) githubEndpoint.openConnection();
 

참고로 HttpsURLConnection HttpURLConnection 클래스의 하위 클래스입니다.


4. 요청 헤더 추가

REST API를 제공하는 대부분의 웹 사이트에서는 앱을 고유하게 식별할 수 있기를 원합니다. 그러한 웹 사이트를 돕는 가장 쉬운 방법은 모든 요청에 고유한 User-Agent 헤더를 포함하는 것입니다.

요청에 User-Agent 헤더를 추가하려면 HttpURLConnection 객체의 setRequestProperty() 메서드를 사용해야 합니다. 예를 들어, User-Agent 헤더를 my-rest-app-v0.1로 설정하는 방법은 다음과 같습니다.

1
myConnection.setRequestProperty("User-Agent""my-rest-app-v0.1");
 

setRequestProperty() 메서드를 여러 번 호출해서 요청에 헤더를 여러 개 추가할 수 있습니다. 예를 들어, 다음 코드는 Accept 헤더와 사용자 정의 Contact-Me 헤더를 추가합니다.

1
2
3
4
myConnection.setRequestProperty("Accept"
        "application/vnd.github.v3+json");
myConnection.setRequestProperty("Contact-Me"
        "hathibelagal@example.com");
 

5. 응답 읽기

모든 요청 헤더를 전달하고 나면 HttpURLConnection 객체의 getResponseCode() 메서드를 이용해 유효한 응답이 있는지 확인할 수 있습니다.

1
2
3
4
5
6
if (myConnection.getResponseCode() == 200) {
    // Success
    // Further processing here
else {
    // Error handling code goes here
}
 
 

HttpURLConnection 클래스가 301과 같은 리디렉션 응답 코드를 받으면 이를 자동으로 처리하고 리디렉션을 따릅니다. 따라서 일반적으로 리디렉션을 확인하는 추가 코드를 작성할 필요는 없을 것입니다.

오류가 없는 경우 이제 getInputStream() 메서드를 호출해서 연결의 입력 스트림에 대한 참조를 가져올 수 있습니다.

1
InputStream responseBody = myConnection.getInputStream();
 

요즘 대부분의 REST API는 유효한 JSON 문서 형식의 데이터를 반환합니다. 따라서 InputStream 객체에서 직접 읽는 대신 InputStreamReader를 만들기를 제안합니다.

1
2
InputStreamReader responseBodyReader = 
        new InputStreamReader(responseBody, "UTF-8");
 

6. JSON 응답 파싱

안드로이드 SDK에는 JsonReader라는 클래스가 있어서 이 클래스로 JSON 문서를 손쉽게 파싱할 수 있습니다. JsonReader 클래스의 생성자에 InputStreamReader 객체를 전달해서 새 인스턴스를 만들 수 있습니다.

1
JsonReader jsonReader = new JsonReader(responseBodyReader);
 

JSON 문서에서 특정 정보를 추출하는 방법은 구조에 따라 다릅니다. 예를 들어, GitHub REST API의 루트 엔드포인트에서 반환된 JSON 문서는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
  "current_user_url""https://api.github.com/user",
  "current_user_authorizations_html_url""https://github.com/settings/connections/applications{/client_id}",
  "authorizations_url""https://api.github.com/authorizations",
  "code_search_url""https://api.github.com/search/code?q=1{&page,per_page,sort,order}",
  "emojis_url""https://api.github.com/emojis",
  "events_url""https://api.github.com/events",
  "feeds_url""https://api.github.com/feeds",
  "followers_url""https://api.github.com/user/followers",
  "following_url""https://api.github.com/user/following{/target}",
  "gists_url""https://api.github.com/gists{/gist_id}",
  "hub_url""https://api.github.com/hub",
  "issue_search_url""https://api.github.com/search/issues?q=1{&page,per_page,sort,order}",
  "issues_url""https://api.github.com/issues",
  "notifications_url""https://api.github.com/notifications",
  "organization_repositories_url""https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
  "organization_url""https://api.github.com/orgs/{org}",
  "public_gists_url""https://api.github.com/gists/public",
  "rate_limit_url""https://api.github.com/rate_limit",
  "repository_url""https://api.github.com/repos/{owner}/{repo}",
  "repository_search_url""https://api.github.com/search/repositories?q=1{&page,per_page,sort,order}",
  "current_user_repositories_url""https://api.github.com/user/repos{?type,page,per_page,sort}",
  "starred_url""https://api.github.com/user/starred{/owner}{/repo}",
  "starred_gists_url""https://api.github.com/gists/starred",
  "team_url""https://api.github.com/teams",
  "user_url""https://api.github.com/users/{user}",
  "user_organizations_url""https://api.github.com/user/orgs",
  "user_repositories_url""https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
  "user_search_url""https://api.github.com/search/users?q=1{&page,per_page,sort,order}"
}
 
 

보다시피 응답은 여러 키가 포함된 하나의 커다란 JSON 객체에 불과합니다. 여기서 organization_url이라는 키의 값을 추출하려면 다음과 같은 코드를 작성해야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
jsonReader.beginObject(); // Start processing the JSON object
while (jsonReader.hasNext()) { // Loop through all keys
    String key = jsonReader.nextName(); // Fetch the next key
    if (key.equals("organization_url")) { // Check if desired key
        // Fetch the value as a String
        String value = jsonReader.nextString();
         
        // Do something with the value
        // ...
                 
        break// Break out of the loop
    } else {
        jsonReader.skipValue(); // Skip values of other keys
    }
}
 
 

위 코드에서는 JSON 응답을 토큰 스트림으로 처리합니다. 따라서 메모리를 거의 소비하지 않습니다. 하지만 모든 토큰을 하나씩 처리해야 하기 때문에 큰 응답을 처리하는 동안 속도가 느려질 수 있습니다.

필요한 모든 정보를 추출하고 나면 JsonReader 객체의 close() 메서드를 호출해서 보유한 모든 리소스를 해제해야 합니다.

1
jsonReader.close();
 

또한 HttpURLConnection 객체의 disconnect() 메서드를 호출해서 연결을 닫아야 합니다.

1
myConnection.disconnect();
 

7. 다양한 HTTP 메서드 사용하기

HTTP 기반 REST 인터페이스는 HTTP 메서드를 사용해 자원을 대상으로 수행해야 하는 연산의 유형을 결정합니다. 이전 단계에서는 HTTP GET 메서드를 사용해 읽기 연산을 수행했습니다. HttpURLConnection 클래스는 기본적으로 GET 메서드를 사용하기 때문에 명시적으로 지정하지 않아도 됩니다.

HttpURLConnection 객체의 HTTP 메서드를 변경하려면 setRequestMethod() 메서드를 사용해야 합니다. 예를 들어, 다음 코드는 httpbin.org에 속한 엔드포인트에 대한 연결을 열고 HTTP 메서드를 POST로 설정합니다.

1
2
3
4
5
URL httpbinEndpoint = new URL("https://httpbin.org/post");
HttpsURLConnection myConnection
        = (HttpsURLConnection) httpbinEndpoint.openConnection();
 
myConnection.setRequestMethod("POST");
 

이미 알고 계실 수도 있겠지만 POST 요청은 서버에 데이터를 보내는 데 사용됩니다. 연결의 출력 스트림에 쓰는 식으로 POST 요청의 본문에 어떤 데이터도 손쉽게 추가할 수 있습니다. 그러나 그렇게 하기 전에 HttpURLConnection 객체의 setDoOutput() 메서드를 호출하고 true를 전달해야 합니다.

1
2
3
4
5
6
7
8
// Create the data
String myData = "message=Hello";
 
// Enable writing
myConnection.setDoOutput(true);
 
// Write the data
myConnection.getOutputStream().write(myData.getBytes());
 

8. 응답 캐싱하기

HTTP 응답을 캐싱하는 것은 언제나 좋은 생각입니다. 그렇게 함으로써 앱의 대역폭 소비를 줄일 수 있을뿐더러 응답 속도도 향상시킬 수 있습니다. API 레벨 13부터 안드로이드 SDK에서는 HttpResponseCache라는 클래스를 제공하므로 네트워킹 로직을 변경하지 않고도 손쉽게 캐싱을 구현할 수 있습니다.

애플리케이션에 캐시를 설치하려면 HttpResponseCache 클래스의 install() 메서드를 호출해야 합니다. 이 메서드는 캐시가 설치될 위치를 나타내는 절대 경로와 캐시의 크기를 나타내는 숫자를 인자로 받습니다. 수동으로 절대 경로를 지정하고 싶지 않다면 getCacheDir() 메서드를 이용하면 됩니다.

 

다음 코드는 크기가 100,000바이트인 캐시를 설치합니다.

 

1
2
HttpResponseCache myCache = HttpResponseCache.install(
                                getCacheDir(), 100000L);
 

캐시가 설치되면 HttpURLConnection 클래스가 캐시를 자동으로 사용하기 시작합니다. 캐시가 작동하는지 확인하려면 캐시에서 제공된 HTTP 응답의 개수를 반환하는 getHitCount() 메서드를 사용하면 됩니다. 

1
2
3
if (myCache.getHitCount() > 0) {
    // The cache is working
}
 
 

결론

안드로이드 앱에서 자유롭게 사용할 수 있는 REST API는 수천 개에 달합니다. 이러한 REST API를 이용하면 앱을 더욱 유익하고, 흥미롭고, 기능이 풍부하게 만들 수 있습니다. 이번 튜토리얼에서는 HttpURLConnection 클래스를 이용해 이러한 REST API를 사용하는 법을 배웠습니다. 또한 앱의 대역폭 사용량을 낮게 유지하는 HTTP 응답 캐시를 만드는 방법도 배웠습니다.

HttpURLConnection을 사용하는 것이 어렵다면 Volley와 같은 서드파티 라이브러리를 사용해 보십시오. 이 같은 라이브러리는 내부적으로 HttpURLConnection 클래스를 사용하지만 코드를 좀 더 간결하고 읽기 쉽게 만들 수 있는 편리한 메서드를 많이 제공합니다.

안드로이드 플랫폼의 네트워킹에 대한 자세한 내용은 안드로이드 네트워크 작업 가이드를 참고합니다.

 

+ Recent posts