78일차(1)/Android App(41) : Http POST방식 요청하기(4) - JSONArray 활용
- 이전 예제 코드리뷰
AsyncTask

- 앱을 run하면 onCreate가 순식간에 실행되었다가 종료되면서 앱이 활성화된다.
- 이후 여기에 등록한 리스너(전송버튼)를 누르면 어떤 객체로 실행의 흐름이 들어온다.
- 위의 파란색 스레드도 UI 스레드이다. 앱이 처음에 활성화될때 사용된 스레드와 같은 스레드이다.
- UI는 이 메인스레드 안에서만 업데이트할 수 있다.
- 진행에 시간이 소요되는 불확실한 작업을 한다면? -> 원칙적으로 메인스레드에서는 하면 안된다.
- 일반적으로 메인 스레드는 사용자의 입력에 반응해서 빠르게 반응해야 하는데, 그러지 못하기때문에...
그런 작업은 새 스레드에서 해야한다.

- 새 스레드에서 이런 작업을 해준다.
- AsyncTask가 왜 필요한지, 어떻게 사용하는지 알아두기!

- 현재 서버컴퓨터 안에 스프링부트로 웹서버 앱을 만들어서 사용하고 있다.

- 클라이언트가 될 수 있는 것은?
→ Window 데스크탑 / Mac OS 데스크탑 / 안드로이드 폰
- 각각 모두에 웹브라우저가 있다. 즉 서버 컴퓨터에 요청할 능력이 있다.


- 웹브라우저가 아닌 우리가 만든 MyAndroid 어플리케이션에서 브라우저처럼 요청할 수 있는지?
- 데이터 전송, 받아오기, 기기의 데이터 수정, 파일 업로드... 등등의 작업을 하려고 한다.
- 때로는 웹서버와 연동해서 로그인 처리도 할 수 있다.
- 브라우저는 폼이 만들어져 있기 때문에 요청을 편리하게 할 수 있는데,
앱을 만드는 입장에서는 이런 작업을 따로 코딩해주어야 한다.
- 웹서버에 요청하는 방법은? 특정서버에 어떻게 http 요청을 하고 응답 데이터를 받아올 것인가?
→ HttpURLConnection 객체를 통해서 가능하다.

- UI 스레드가 아닌 서브 스레드에서 작업한다.
- http://xxx/xxx/getData 가 저 위치에 들어간다.
- 브라우저가 아니므로 이 요청하려는 내용을 넣어서 객체를 생성하고, 이를 통해 openConnection() 한다.
- 원하는 요청 방식(get/post)를 정해서 요청할 수 있다.
- 정상적인 응답(200번) 이 되면 반복문을 돌면서 응답한 값을 읽어올 수 있다.
- Utility를 사용해서도 이런 방식으로 작성할 수 있다.
- 대부분의 코드는 유틸리티에 들어가 있다. 이것을 받아와서 쓸 수 있으면 된다.
- get 요청 : sendGetRequest()
post 요청 : sendPostRequest()
- 결과를 특정 메소드에서 받아볼 수 있게끔 한다.
- 코틀린으로도 이런 코드를 작성 가능하다.
- 유틸리티를 코틀린으로 바꾸어서 적용하기.
(자체 코틀린 변환을 사용하면 static class를 가지고있는 경우 object로 바꾸어 버린다.)


- 이 예제를 돌리기 위해서는 스프링부트 서버가 필요하다.
- 해당 경로에 대한 안드로이드 컨트롤러도 만들어야 한다.
- 스프링 웹 서버를 돌려두고 안드로이드 run!
- 이 버튼을 누르면 이곳으로 실행순서가 들어오고, msg에 담긴 값이 맵에 담겨서 반환된다.

- 만약 html 폼이었다면 msg 에는 input요소의 name="msg" 로 지정된 값이 담길 것이다.
- input에 입력한 문자열을 읽어내는 형식이었을 것이다.
- spring의 컨트롤러 자체는 이전에 만든 컨트롤러와 다르지 않다.
- 유틸 클래스 내부에서 위와 같은 html 폼을 제출한 것과 같은 효과를 내준 것이다.

- 앱의 요청에 대한 일반적인 응답은 보통 xml or JSON 문자열이다.
- @Responsebody 어노테이션을 붙이고 + map/list/dto 로 리턴해주기

- 웹브라우저로 직접 요청할 수도 있다. 그러면 이렇게 응답해준다.
- 안드로이드 앱에서 이런 요청을 하게 되면 안드로이드는 이런 JSON 문자열을 응답받게 된다.
- 이것을 응답받아서 어떻게 사용할 것인지?
- JSON문자열은 본래 javascript 표기법을 모방했기 때문에, javascript에서 다루기 쉽다.
(자동으로 object로 변환되어 들어온다)
- 하지만 java, kotlin에 친화적이지는 않다. 그러면 어떻게 적용할 수 있을까?

- 응답한 문자열을 JSONObject 객체를 생성하면서 담아오고,
obj.getXXX 메소드로 들어있는 XXX타입의 값을 불러내 추출하여 사용한다.

- JSON 문자열의 형식을 보면 아래와 같다.
- 유형1: { "num":1, "name":"kim" }
- 유형2: [ "kim", "lee", "park" ]
- 유형3: { "num":2, "name":"park", "hobby": [ "game","reading","movie" ] }
- 유형4: [ { }, { }, { }, ... ]
- 중괄호 { }, 대괄호 [ ] 로 JSON 문자열을 표기한다.
- 3번:특정 키값으로 여러개의 값을 가지고 있을 수 있다.
- 4번: object 형태가 여러개 있을 수 있다.
- 유형1, 4를 가장 많이 사용한다.
<학습목표>
1. Spring Boot 서버에서 원하는 유형의 json 문자열을 출력하기
-> 간단하다. 어노테이션+dto/map/list 리턴
controller의 return type에 따라서 출력되는 json문자열의 형식이 정해진다.
Map, Dto=> { }
List => [ ]
2. android app에서 다양한 유형의 json 문자열 활용하는 방법 익히기
{ } => JSONObject 타입 객체로 접근
[ ] => JSONArray 타입 객체로 접근
- 안드로이드에서도 받아서 활용하기
MainActivity
package com.example.step17httprequest3
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import org.json.JSONArray
import org.json.JSONObject
class MainActivity : AppCompatActivity() , Util.RequestListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//EditText 객체의 참조값 얻어오기
val editText=findViewById<EditText>(R.id.inputMsg)
//Button 객체의 참조값 얻어와서 리스너 등록
val sendBtn=findViewById<Button>(R.id.sendBtn)
sendBtn.setOnClickListener {
//EditText에 입력한 문자열을 읽어와서
val msg=editText.text.toString()
Util.sendPostRequest(
999,
"http://192.168.0.34:9000/boot07/api/send",
mapOf("msg" to msg), //"msg" 라는 키값으로 입력한 메세지를 담은 Map
this)
}
val getListBtn=findViewById<Button>(R.id.getListBtn);
getListBtn.setOnClickListener {
Util.sendGetRequest(
1000,
"http://192.168.0.34:9000/boot07/api/list",
mapOf("pageNum" to "1"),
this
)
}
}
override fun onSuccess(requestId: Int, result: Map<String, Any?>?) {
if(requestId == 999){
//웹서버가 응답한 json문자열 { }
val jsonStr = result?.get("data").toString()
Log.d("#### json 문자열 ####", jsonStr)
val obj=JSONObject(jsonStr)
//key 값을 이용해서 Boolean, String, Int 값을 추출할 수 있다.
val isSuccess=obj.getBoolean("isSuccess")
val response=obj.getString("response")
val num=obj.getInt("num")
}else if (requestId == 1000){
val jsonStr= result?.get("data").toString();
Log.d("#### json 문자열 ####", jsonStr);
val arr=JSONArray(jsonStr);
//반복문 돌면서 i 값을 0에서부터 JSONArray 의 방의 사이즈 -1 까지 변화시킨다.
for(i in 0..arr.length()-1){
val tmp=arr.getString(i);
Log.d("json array", tmp)
}
}
}
override fun onFail(requestId: Int, result: Map<String, Any?>?) {
}
}

- JSONArray 객체가 있다.

- 서버에서 { } 문자열을 응답했다면 JSONObject 객체를 생성하는 것이 맞고,
[ ] 문자열을 응답했다면 JsonArray 객체를 생성하면 된다.
- 각각 다른 곳에 들어가면 에러가 난다. 1:1 대응관계를 기억하기!
- 백엔드 서버 개발자이면서 안드로이드 앱개발자라면 이 서버연동-데이터전송 내용을 잘 알아야 한다.
AndroidController
package com.sy.boot07.api;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class AndroidController {
/*
* JSON 문자열
* 1. @ResponseBody 어노테이션
* 2. Map 혹은 List 혹은 Dto 를 리턴하면 자동으로 JSON 문자열로 변환 되어서 응답된다.
*/
@RequestMapping("/api/send")
@ResponseBody
public Map<String, Object> send(String msg){
System.out.println(msg);
Map<String, Object> map=new HashMap<>();
map.put("isSuccess", true);
map.put("response", "hello client!");
map.put("num", 1);
return map;
}
@RequestMapping("/api/list")
@ResponseBody
public List<String> list(int pageNum){
List<String> names=new ArrayList<>();
names.add("바나나");
names.add("딸기");
names.add("복숭아");
return names;
}
}
- Spring에서 안드로이드 컨트롤러 메소드 추가

- 주소창에 입력해보면 이렇게 출력된다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/inputMsg"
android:hint="서버에 할말 입력" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/sendBtn"
android:text="전송"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/getListBtn"
android:text="목록 받아오기"/>
</LinearLayout>
- 안드로이드에서 목록을 받아오는 버튼을 추가

- 제너릭<> 은 String이므로 1도 "1"로 문자로 넣어주어야 한다.
- 목록 보기 버튼을 누르면, 이쪽으로 실행순서가 들어오고 이 경로요청이 들어갈 것이다.

- else if 추가 : id가 1000일 경우
- { }면 JSONObject, [ ] 이면 JSONArray

- array 타입이면 인덱스를 통해서 접근한다.
(object 는 키 값을 통해서 접근했다.)
- Array안에 Object가 들어있으면 JSONObject 타입을 리턴한다.(getJSONObject)

- for 반복문 돌면서 담아준다. i값은 0에서부터 length-1 이다.
- getString(i) 로 하나씩 반복문을 돈다. i값을 인덱스로 활용한다.

- 안드로이드 로그에 이렇게 추출된다.

- 유형3: { "num":2, "name":"park", "hobby": [ "game","reading","movie" ] }
- 유형3 같은 경우에는 jsonArray를 사용

- 유형4: [ { }, { }, { }, ... ]
- array안에 object가 있는 경우에는 getJSONObject 를 사용하면 된다.
'국비교육(22-23)' 카테고리의 다른 글
80일차(1)/Android App(43) : 로그인 기능 구현 (0) | 2023.02.03 |
---|---|
79일차(1)/Android App(42) : Http 요청으로 Oracle DB 출력하기 (0) | 2023.02.02 |
77일차(4)/Android App(40) : Http POST방식 요청하기(3) (0) | 2023.01.30 |
77일차(3)/Android App(39) : Http POST방식 요청하기(2) (0) | 2023.01.30 |
77일차(2)/Android App(38) : Http POST방식 요청하기(1) (0) | 2023.01.27 |