티스토리 뷰

졸업작품 프로젝트를 진행하다 AWS 서버에 있는 데이터를 안드로이드로 전송받아야 되는 상황이 생겼다.

이번 포스팅에서는 php를 경유하여 서버 DB 데이터를 안드로이드로 보내는법을 설명한다.


왜 중간에 php를 경유해야 하는가

안드로이드에서 외부 DB에 있는 데이터를 받기 위해서는 웹 서버의 중계가 필요하다.

이는 보안상의 이유 때문이다. (무슨 보안상의 이유인지 모르겠다..ㅠㅠ)

하여.. 안드로이드 어플리케이션<->php 아파치서버<->mysql 이렇게 DB데이터를 가져와야된다.

(이 방법 외에 소켓 통신을 해도 된다. 이 방법은 다른 포스팅에서 설명)


시나리오

안드로이드가 http요청을 서버로-> 아파치 웹서버가 DB에 접근해 데이터를 읽고 echo로 데이터를 띄운다-> 안드로이드는 echo로 출력한 json을 읽고 파싱하여 사용한다.


해봅시다 

  • 서버
    • 위에서 설명했듯이 우선 php서버에서 DB데이터를 가져오고 echo로 출력해야된다.
    • php 코드는 아래와 같다.
<?php
$host="localhost";//현재 EC2에서 DB돌리고있으므로 로컬호스트
$user="user이름";
$password="DB비밀번호";
$DB_name="DB이름";
$conn=mysqli_connect($host,$user,$password,$DB_name);
$data=array();//여기에 데이터를 넣습니다
if(mysqli_connect_error($conn)){
    echo"db 연결 실패","<br>";
    echo"원인:",mysqli_connect_error();
    exit();
}
else{
    $result=mysqli_query($conn,"select * from 테이블명");//DB의 모든값을 읽어서 result에 넣음
    while($row=mysqli_fetch_array($result)){//한 행씩
       array_push($data,array('time'=>$row['TIME']
                    ,'data1'=>$row['DATA1']
                    ,'data2'=>$row['DATA2']
                    ,'data3'=>$row['DATA3']
                    ,'data4'=>$row['DATA4']
                    ,'data5'=>$row['DATA5']
                    ,'data6'=>$row['DATA6']
    ));//json형태 만들기
    }
    header('Content-Type: application/json; charset=utf8');
    $json = json_encode(array("테이블이름"=>$data), JSON_PRETTY_PRINT+JSON_UNESCAPED_UNICODE);
    echo $json; // => 출력되는 값이 이 코드로 하여금 android로 전송된다..
}
mysqli_close($conn);
?>

위는 php 서버에서 echo로 출력하는 모습을 웹페이지에서 확인한것, 이 출력되는 json을 안드로이드에서 읽어가는것이다.

  • header, content-type?
    • header: 서버와 클라이언트가 송수신하기 전에 필요한 정보를 서로 사전에 나누는것
    • content-type: 클라이언트 브라우저로 어떤 자원을 보낼때(파일, 문서 등) 웹 서버는 일련의 http헤더로 파일이나 자원을 포함하는 바이트의 stream을 앞에 보낸다. 이런 헤더는 클라이언트에게 웹서버의 소프트웨어 타입, 서버 날짜, 시간, http프로토콜 등을 지정한다. 암튼 content-type은 헤더에 들어가는 보내는 자원 타입을 적어주는것인듯..(사실 잘 모르겠다 하다보니까 갑자기 궁금해서 찾아봤는데 php까지 다 하려니까 힘듦^^)

  • 안드로이드 
    • AndroidManifest.xml에 아래 두개를 추가한다.
      • 윗부분은 인터넷 사용을 허용하는부분이고 usesClear~ 는 http 접근을 허용하는 부분이다. 
      • 안드로이드는 기본적으로 http접근을 허용하지 않기때문에 http접근을 하려면 아래처럼 한다.
<uses-permission android:name="android.permission.INTERNET" />
...
<application
android:usesCleartextTraffic="true"
...
  • 안드로이드 이어서^^(들여쓰기 어케하냐)
    • 읽어온 json을 출력할 액티비티 xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    >

    <TextView
        android:id="@+id/tv_outPut"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="출력 공간"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</RelativeLayout>
  • MainActivity.java
import androidx.appcompat.app.AppCompatActivity;

import android.content.ContentValues;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
//출처: https://blog.naver.com/PostView.nhn?blogId=phj8498&logNo=221346945899
public class MainActivity extends AppCompatActivity {
    private TextView tv_outPut;

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

        // 위젯에 대한 참조.
        tv_outPut = (TextView) findViewById(R.id.tv_outPut);

        // URL 설정.
        String url = "http://퍼블릭IPv4주소/어쩌고.php";//아까 echo확인한 그 주소.. 

        // AsyncTask를 통해 HttpURLConnection 수행.
        NetworkTask networkTask = new NetworkTask(url, null);
        networkTask.execute();
    }

    public class NetworkTask extends AsyncTask<Void, Void, String> {

        private String url;
        private ContentValues values;

        public NetworkTask(String url, ContentValues values) {

            this.url = url;
            this.values = values;
        }

        @Override
        protected String doInBackground(Void... params) {

            String result; // 요청 결과를 저장할 변수.
            RequestHttpURLConnection requestHttpURLConnection = new RequestHttpURLConnection();
            result = requestHttpURLConnection.request(url, values); // 해당 URL로 부터 결과물을 얻어온다.

            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            //doInBackground()로 부터 리턴된 값이 onPostExecute()의 매개변수로 넘어오므로 s를 출력한다.
            tv_outPut.setText(s);
        }
    }

}
  • RequestHttpURLConnection.java(http연결을 함, 
//출처:https://blog.naver.com/PostView.nhn?blogId=phj8498&logNo=221346945899
import android.content.ContentValues;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

public class RequestHttpURLConnection {

    public String request(String _url, ContentValues _params){

        // HttpURLConnection 참조 변수.
        HttpURLConnection urlConn = null;
        // URL 뒤에 붙여서 보낼 파라미터.
        StringBuffer sbParams = new StringBuffer();

        /**
         * 1. StringBuffer에 파라미터 연결
         * */
        // 보낼 데이터가 없으면 파라미터를 비운다.
        if (_params == null)
            sbParams.append("");
            // 보낼 데이터가 있으면 파라미터를 채운다.
        else {
            // 파라미터가 2개 이상이면 파라미터 연결에 &가 필요하므로 스위칭할 변수 생성.
            boolean isAnd = false;
            // 파라미터 키와 값.
            String key;
            String value;

            for(Map.Entry<String, Object> parameter : _params.valueSet()){
                key = parameter.getKey();
                value = parameter.getValue().toString();

                // 파라미터가 두개 이상일때, 파라미터 사이에 &를 붙인다.
                if (isAnd)
                    sbParams.append("&");

                sbParams.append(key).append("=").append(value);

                // 파라미터가 2개 이상이면 isAnd를 true로 바꾸고 다음 루프부터 &를 붙인다.
                if (!isAnd)
                    if (_params.size() >= 2)
                        isAnd = true;
            }
        }

        /**
         * 2. HttpURLConnection을 통해 web의 데이터를 가져온다.
         * */
        try{
            URL url = new URL(_url);
            urlConn = (HttpURLConnection) url.openConnection();

            // [2-1]. urlConn 설정.
            urlConn.setRequestMethod("POST"); // URL 요청에 대한 메소드 설정 : POST.
            urlConn.setRequestProperty("Accept-Charset", "UTF-8"); // Accept-Charset 설정.
            urlConn.setRequestProperty("Context_Type", "application/x-www-form-urlencoded;cahrset=UTF-8");

            // [2-2]. parameter 전달 및 데이터 읽어오기.//현재는 보낼게 없어서 주석처리(이게맞나)
            //String strParams = sbParams.toString(); //sbParams에 정리한 파라미터들을 스트링으로 저장. 예)id=id1&pw=123;
            //OutputStream os = urlConn.getOutputStream();
            //os.write(strParams.getBytes("UTF-8")); // 출력 스트림에 출력.
            //os.flush(); // 출력 스트림을 플러시(비운다)하고 버퍼링 된 모든 출력 바이트를 강제 실행.
            //os.close(); // 출력 스트림을 닫고 모든 시스템 자원을 해제.

            // [2-3]. 연결 요청 확인.
            // 실패 시 null을 리턴하고 메서드를 종료.
            if (urlConn.getResponseCode() != HttpURLConnection.HTTP_OK)
                return null;

            // [2-4]. 읽어온 결과물 리턴.
            // 요청한 URL의 출력물을 BufferedReader로 받는다.
            BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream(), "UTF-8"));

            // 출력물의 라인과 그 합에 대한 변수.
            String line;
            String page = "";

            // 라인을 받아와 합친다.
            while ((line = reader.readLine()) != null){
                page += line;
            }

            return page;

        } catch (MalformedURLException e) { // for URL.
            e.printStackTrace();
        } catch (IOException e) { // for openConnection().
            e.printStackTrace();
        } finally {
            if (urlConn != null)
                urlConn.disconnect();
        }

        return null;

    }

}

 

 

 

 

 

참고한 블로그들

-서버 코드 작성할때 content type이 뭔지 궁금해서 찾아본 블로그

https://juyoung-1008.tistory.com/4

 

MIME-Type,Content-Type이란?

MIME이란? MIME이란? Multipurpose Internet Mail Extensions의 약자로 간략히 말씀을 드리면 파일 변환을 뜻한다. MIME는 이메일과 함께 동봉할 파일을 텍스트 문자로 전환해서 이메일 시스템을 통해 전달하기

juyoung-1008.tistory.com

-왜 중간에 php 경유하는지, 하는 방법 참고한 블로그들

https://ititit1.tistory.com/99

 

안드로이드 앱 외부 데이터베이스 연동(Android<-> PHP <-> Maria DB) 1. 기본 설치

안드로이드 어플리케이션은 외부 DB에 바로 접근해서 데이터를 불러오지 못합니다. 보안상의 이유때문에 '외부' 데이터베이스에 바로 접근은 하지못합니다. 따라서 위와같은 방식으로 애플리케

ititit1.tistory.com

https://blog.naver.com/PostView.nhn?blogId=phj8498&logNo=221346945899 

 

안드로이드+MYSQL+PHP 웹서버 연동하기 -2

웹서버에 있는 db값을 읽어 json형태로 화면에 보여주는 것을 먼저 해야합니다. 1234567891011121314151617...

blog.naver.com

-http 관련해서 찾아본 블로그

https://codechacha.com/ko/android-cleartext-http-traffic-issue/

 

안드로이드 - Cleartext HTTP ... not permitted 예외 해결 방법

Exoplayer에서 Http의 mp3파일을 스트리밍하려고 했는데, Cleartext HTTP traffic to www.soundhelix.com not permitted와 같은 에러를 만났습니다. 원인은 Android Pie부터 Http 접근을 허용하지 않기 때문입니다. usesCleart

codechacha.com

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
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
글 보관함