티스토리 뷰
졸업작품 프로젝트를 진행하다 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접근을 하려면 아래처럼 한다.
- AndroidManifest.xml에 아래 두개를 추가한다.
<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
-왜 중간에 php 경유하는지, 하는 방법 참고한 블로그들
https://ititit1.tistory.com/99
https://blog.naver.com/PostView.nhn?blogId=phj8498&logNo=221346945899
-http 관련해서 찾아본 블로그
https://codechacha.com/ko/android-cleartext-http-traffic-issue/
'공부 > Android' 카테고리의 다른 글
socket.io 사용할때 뒤로가기 눌렀을때 앱 종료되는 이유? (0) | 2022.11.07 |
---|---|
MPAndroidChart 안드로이드에서 그래프 그리기 (0) | 2022.11.03 |
배경 그라데이션 (0) | 2022.11.02 |
안드로이드에서 socket.io 사용하기(Node.js, http, 멀티코어에서 유의점) (0) | 2022.10.29 |
안드로이드 url, api key를 숨기는법(깃 레포지토리에서^^) (0) | 2022.10.29 |