본문 바로가기

프로그래밍/Android

Apache HttpClient 라이브러리를 통한 HTTP 통신 JSON 파싱 (1/3)



네트워크 통신에는 크게 2가지 방법이 존재 합니다.

 - 소켓 통신 (Socket)

 - URL통신 (HTTP)

 

두 통신에 대한 자세한 사항은 인터넷에서도 많이 있으니 따로 설명하지 않겠습니다.

다만, 이번 강의에서 다룰 내용인 HTTP통신을 간단하게 살펴보자면 클라이언트와 서버간

비연결 지향 통신 방법 입니다.

 

또한, HTTP통신을 할때 클라이언트에서 서버로 데이터를 요청 방법에는 크게 GET, POST 두가지 방식이

존재합니다.

 


GET, POST 두가지 방식 모두, 클라이언트에서 서버로 데이터를 요청(Request) 하고 서버로 부터 응답 (Responce) 를 받게되면 해당 연결을 종료 합니다. 


 

자바 SDK 에서는 java.net 패키지 안에 URLConnection 및 자식 클래스인 HttpURLConnection 을 통해

스트림을 얻어 데이터를 얻어올수 있지만, Android SDK 에서 기본적으로 제공하는 Apache 라이브러리를 통해

HTTP 통신을 구현해보고자 합니다.


우선 안드로이드는 메인스레드 에서 네트워크 작업을 사용할 수 없습니다.

따라서, 반드시 백그라운드에서 처리하도록 해야 합니다. (Thread 사용)


※ 주의 : Internet 퍼미션을 반드시 활성화 해주시기 바랍니다.

 

이제 아래와 같은 데이터를 응답받는다 가정하에 아래 데이터를 파싱해보는 연습을 해보겠습니다.


{

     "name":"Kim Pack",

     "age":19

}


 

본 강좌에서 가독성 및 코드복잡성 최소화를 위해 클래스를 나누도록 하겠습니다.

Network 클래스를 생성하도록 합니다.

Network 클래스는 네트워크에 요청하고 데이터를 응답받는 역할을 하는 클래스 입니다.

Get, Post 요청 두가지를 위한 메서드가 존재합니다.

 

그리고, Network 클래스를 실행하여 실질적 데이터를 요청하는 소스코드는 아래와 같습니다.

		new Thread(new Runnable() {
			
			@Override
			public void run() {
				
				Network network = new Network();
				
				ArrayList params = new ArrayList();
				params.add(new BasicNameValuePair("id", "1"));				    
				
				String responceBody = network.requestGet("http://192.168.0.4/DEPOL/TestMain.php", params);				
				
				Log.d("data", responceBody);
			}
			
		}).start();

 

package com.example.network;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

public class Network {
	
	
	private ResponseHandler responseHandler = new ResponseHandler() {

		@Override
		public String handleResponse(HttpResponse response) throws IOException {

			int status = response.getStatusLine().getStatusCode(); // HTTP 상태코드			
						
			if (status == HttpStatus.SC_OK) { // 200 인경우 성공
				HttpEntity entity = response.getEntity();
				return entity != null ? EntityUtils.toString(entity) : null;
			} else {
				ClientProtocolException e = new ClientProtocolException("Unexpected response status: " + status);
				throw e; // 상태코드 200이 아닌경우 예외발생
			}

		}
		
	};
	
	public String requestGet(String url, List params) {
		
		String responseBody = null;	
		HttpClient httpclient = new DefaultHttpClient();
		
		try {
			
			String queryParams = URLEncodedUtils.format(params, HTTP.UTF_8); //Parameter 구성 (인코딩 UTF-8)
			
			URI uri = new URI(url + "?" + queryParams);	//ex) http://noantech.cafe24.com/TestGet.php?id=1 형태 구성
			
			HttpGet httpget = new HttpGet(uri);				
			responseBody = httpclient.execute(httpget, responseHandler);
			
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		
		return responseBody;
	}
	

	public String requestPost(String url, List params) {
		
		String responseBody = null;	
		HttpClient httpclient = new DefaultHttpClient();
		
		try {
			
			HttpPost httppost = new HttpPost(url);			
			httppost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
			responseBody = httpclient.execute(httppost, responseHandler);			
			
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return responseBody;
	}
	
}

 

 

requestGet 메서드를 먼저 살펴보겠습니다.

이 메서드는 Get 요청을 하는 메서드로서, 웹서버의 URL 과 전달할 파라메터 두가지 인자를 받습니다.

즉, URL은 http://naver.com 같은 URL 이고, 전달할 파라메터는 http://naver.com?id=3&name=가나다

전형적인 GET 방식 요청의 구성 입니다.

 

GET 방식의 데이터 요청은, URL 뒤에 데이터가 붙어서 전송됩니다. (아래의 형태)

ex. http://naver.com?키=데이터&키=데이터

 

 

메서드의 구현부를 살펴보면,

 

String queryParams = URLEncodedUtils.format(params, HTTP.UTF_8);
URI uri = new URI(url + "?" + queryParams);

 

이러한 코드가 존재하는데, 위 코드의 역할은 URLEncodedUtils.format 을 통해 URL로 파라메터를 인코딩 합니다.

params 변수의 원형은 List<NameValuePair> 를 받게 되는데,

 

BasicNameValuePair 클래스는 org.apache.http.message 패키지에 포함된,

String Name, String Value 라는 인스턴스 변수를 가지고 있는 데이터 클래스 입니다.

Key-Value 코딩을 통해 데이터를 전달할때 사용하기 유용합니다.

 

즉, List 안에 저장된 키,벨류 데이터를 URL에 맞게 만들어주는 역할을 하는것이 URLEncodedUtils.format 입니다.

(ex. http://naver.com?id=3&name=가나다)

 

그리고 뒤에 HTTP.UTF_8 이라는 String 형태의 상수는 데이터를 UTF_8 로 인코딩해서 보내기 위해 적용한 것 입니다. (기본적으로 URL 뒤에 한글 그대로 보내는것이 아닌 UTF-8 로 인코딩하여 %5D%9D 형태로 보낸다는 의미)

 

그리고,

 

HttpGet httpget = new HttpGet(uri);    
responseBody = httpclient.execute(httpget, responseHandler);

위 코드를 보겠습니다.

 

아파치 라이브러리에서는 Get 요청으로 사용할 경우에는 HttpGet 클래스를 사용해야 합니다.

(반대로 Post 는 HttpPost)

 

생성자는 데이터를 요청할 URI 를 받습니다.

이제 여기까지 데이터를 요청할 준비는 끝났습니다.

 

이제 실질적인 데이터를 요청하고 결과를 어떻게 처리할것 인가? 를 구성해야 합니다.

그 역할을 하는 메서드가 HttpClient 클래스의 execute 메서드 입니다.

 

 

 

 

execute 메서드는 HttpUriRequest, ResponseHandler 두가지 인자를 받습니다. (둘다 인터페이스)

HttpUriRequest 는 어떤 데이터를 요청할것인지 에 대한 인터페이스를 구현해야 하고,

두번째 ResponseHandler는 응답에 대한 처리를 할 인터페이스를 구현해야 합니다.

 

HttpUriRequest 인터페이스는 서브 클래스 HttpGet, HttpPost 등 존재하며,

ResponseHandler 인터페이스는 서브 클래스 BasicResponseHandler 가 존재합니다.

BasicResponseHandler 클래스를 쓰셔도 되지만, 결국 handleResponse 를 오버라이드 하여 구현해 주어야 합니다.

 

데이터를 응답받고, 처리에 대한건 결국 handleResponse 에서 이루어 집니다.

위 코드에서는 POST, GET 방식 모두 동일한 결과를 처리해야 하기 때문에 인스턴스 객체로 선언함과 동시에 handleResponse 메서드를 오버라이드 하여 재정의 하였습니다.

 

 

	private ResponseHandler responseHandler = new ResponseHandler() {

		@Override
		public String handleResponse(HttpResponse response) throws IOException {
			
			int status = response.getStatusLine().getStatusCode(); // HTTP 상태코드			
						
			if (status == HttpStatus.SC_OK) { // 200 인경우 성공
				HttpEntity entity = response.getEntity();
				return entity != null ? EntityUtils.toString(entity) : null;
			} else {
				ClientProtocolException e = new ClientProtocolException("Unexpected response status: " + status);
				throw e; // 상태코드 200이 아닌경우 예외발생
			}

		}
		
	};

웹서버에 데이터를 요청하고 결과를 받아오면 HttpResponse 객체에서 정보를 가지고 있습니다. (HTTP 결과)

이때, HTTP 의 상태코드를 확인하고, 올바른 요청에 대한 응답인경우 httpBody 를 리턴하는 구조 입니다.

 

※ http 상태코드 정보 - http://ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C

 

String responceBody = network.requestGet("http://192.168.0.4/DEPOL/TestMain.php", params);

 

자 이제 해당 코드를 실행하면 responceBody 객체 에 웹서버에서 응답받은 데이터가 저장됩니다.

 

 

 

 

 

자 이제, 응답받은 JSON 데이터를 파싱하는 일만 남았습니다. (다음에 계속)