ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android/Kotlin] 네트워크(Network)
    Android/Kotlin 2022. 7. 9. 19:53
    반응형

    [모든 포스팅은 개인적 공부를 위해 작성된 글입니다]

    <서버와 안드로이드 통신>

    - 주로 JSON(JavaScript Object Notation) 사용

    - INTERNET 권한 추가해 줘야 함

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

     

    <JSON 형식>
    - [] -> List 의미

    - {} -> 객체 의미

    ex) json response 예제

    {
      "squadName": "Super hero squad",
      "homeTown": "Metro City",
      "formed": 2016,
      "secretBase": "Super tower",
      "active": true,
      "members": [
        {
          "name": "Molecule Man",
          "age": 29,
          "secretIdentity": "Dan Jukes",
          "powers": [
            "Radiation resistance",
            "Turning tiny",
            "Radiation blast"
          ]
        },
        {
          "name": "Madame Uppercut",
          "age": 39,
          "secretIdentity": "Jane Wilson",
          "powers": [
            "Million tonne punch",
            "Damage resistance",
            "Superhuman reflexes"
          ]
        },
        {
          "name": "Eternal Flame",
          "age": 1000000,
          "secretIdentity": "Unknown",
          "powers": [
            "Immortality",
            "Heat Immunity",
            "Inferno",
            "Teleportation",
            "Interdimensional travel"
          ]
        }
      ]
    }

    *json 객체 출처*

    https://developer.mozilla.org/ko/docs/Learn/JavaScript/Objects/JSON

     

    JSON으로 작업하기 - Web 개발 학습하기 | MDN

    JavaScript Object Notation (JSON)은 Javascript 객체 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷입니다. 웹 어플리케이션에서 데이터를 전송할 때 일반적으로 사용합니다(서버에서

    developer.mozilla.org

    <JSON Parsing>
    - json 객체를 kotlin이나 Java가 알아들을 수 있게 변환(해석)하는 작업

    - Serializable(직렬화) 필요

     - JSON 객체의 정보를 Kotlin이나 Java가 이해할 수 있는 틀(ex class)에 넣어주는 작업

     

    <자주 사용되는 Request Type>

    - 1. GET

     - 지정한 URI의 정보(리소스) 요청

     - 잘 처리된 경우 Status Code : 200 OK

    - 2. POST

     - 요청과 데이터 담아 전송 -> 정보 추가(해당 URI에 리소스 생성) 요청

     - 잘 처리된 경우 Status Code : 201 Created 

    - 3. DELETE

     - 지정한 URI의 정보 삭제 요청

    - 4. PUT

     - 지정한 URI의 정보 수정 요청

     

    <Status Code>

    - 100번대 : 요청 받아 작업 진행 중

    - 200번대 : 작업 성공

    - 300번대 : 요청 완료 위해 리다이렉션 해야 함

    - 400번대 : 클라이언트 오류, 요청 올바르지 않음

    - 500번대 : 서버 오류, 요청은 올바르지만 서버가 응답할 수 없음

     

    <setRequestProperty()>

    - setRequestProperty("Key", "Value")

    Key Value 의미
    Accept application/xml 응답 결과를 xml 형태로 받음으로 요청
    Accept application/json 응답 결과를 json 형태로 받음으로 요청
    Content-Type application/xml 요청 시 application/xml 형태로 전달
    Content-Type application/json 요청 시 application/json 형태로 전달
    Content-Type text/html 요청 시 text/html 형태로 전달
    Cache-Control no-cache 캐시 설정

     

    <가져온 데이터 담을 틀(Class) 만들기>

    - Serializable 상속 받아야 함

    class Person(
        var id: Int? = null,//서버에서 자동 생성
        var name: String? = null,
        var age: Int? = null,
        var intro: String? = null
    ):Serializable

     

    <요청한 데이터 가져와서 읽기>

    - GSON 사용

     - GSON : JSON 파싱, 생성 위해 사용되는 구글 오픈소스(Java Object를 JSON 문자열로 변환, JSON 문자열을 Java Object로 변환할 수 있음)

    - gralde app 수준

    implementation 'com.google.code.gson:gson:2.8.5'
    class NetworkTask(): AsyncTask<Any?, Any?, Any?>(){
        override fun doInBackground(vararg p0: Any?): Any? {
            val urlString: String = "주소"
            val url: URL = URL(urlString)
    
            val connection: HttpURLConnection = url.openConnection() as HttpURLConnection//요청 보냄
            connection.requestMethod="GET"//GET 방식으로 요청(requestMethod == 요청 방식 설정)
            connection.setRequestProperty("Content-Type","application/json")//헤더 작성
    
            var buffer = ""
            if(connection.responseCode == HttpURLConnection.HTTP_OK){
                val reader = BufferedReader(
                    InputStreamReader(connection.inputStream, "UTF-8")
                )//통신은 Byte 형태로 이루어 지는데 이를 사람이 읽을 수 있는 형태로 가져옴
                buffer = reader.readLine()
            }//responseCode == StatusCode, HTTP_OK == 200 OK
    
            val data = Gson().fromJson(buffer, Array<Person>::class.java)
            /*
             * Array<> 붙이지 않은 경우
             * - Gson이 기대하는 객체는 Array 형태인데 person::class.java로 하면 객체 형식으로 가져오겠다는 의미이므로 Array 붙여줘야 함
             */
            val age = data[0].age//가져온 데이터 출력
    
            return null
        }
    }

    - reader를 생성하는 부분(BufferReader, InputStreamReader 부분)은 거의 똑같이 사용됨

     

    <서버 통신 지원하는 라이브러리>

    - Volly

    - Retrofit

     

    <Retrofit 사용하기(비동기 방식)>

    - gradle app 수준

    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    //도메인(baseURL) 뒷부분 URL(변하는 부분) 관리할 수 있는 서비스 생성(interface로 생성)
    
    interface RetrofitService {
        //GET 호출하여 서버로부터 데이터 얻기 위한 위한 함수 생성(GET 요청)
        @GET("baseURL 뒷부분")//baseURL 뒷부분 입력
        fun getStudentsList(): Call<ArrayList<Person>>//GET안에 입력한 URL에 데이터를 요청하는데 그 response의 타입이 ArrayList<Person>이다라는 뜻
    
        //서버 데이터 업데이트 하기 위한 함수 생성(POST 요청)
        @POST("baseURL 뒷부분")
        fun createStudent(
            @Body params: HashMap<String, Any>
        /*
         * HashMap<Key, Value>
         * 예제로 든 테이블의 Key 값은 모두 String 형태지만 Value 값은 String, Int 등 여러가지로 다양하기 때문에 모든 자료형 들어갈 수 있는 Any로 설정
         */
        ): Call<Person>
    
        @POST("baseURL 뒷부분")
        fun createStudentEasy(
            @Body person: Person
        ): Call<Person>
    }
    val retrofit = Retrofit.Builder().baseUrl("도메인 주소").addConverterFactory(GsonConverterFactory.create()).build()//Retrofit 생성
    //baseUrl -> 도메인 주소
    //addConverterFactory -> 서버로 부터 데이터 받아와 원하는 데이터 타입으로 바꾸는 Converter 추가
    
    val service = retrofit.create(RetrofitService::class.java)
    /* retrofit 안에 RetrofitService(서비스 클래스명) 넣어 service 만들어줌
     * service 통해 데이터 통신 가능
     */
    
    //GET
    service.getStudentsList().enqueue(object: Callback<ArrayList<Person>> {//enqueue -> 해당 통신을 통신 대기줄에 올려 놓음
        override fun onResponse(
            call: Call<ArrayList<Person>>,
            response: Response<ArrayList<Person>>
        ) {
            if(response.isSuccessful){
                val personList = response.body()//데이터 가져옴
                val statusCode = response.code()//Status Code 받아옴
                val error = response.errorBody()//통신 성공했지만 에러 발생한 경우 에러 받아옴
                val header = response.headers()//헤더 가져옴
            }//통신 잘 된 경우
        }//통신 성공한 경우
    
        override fun onFailure(call: Call<ArrayList<Person>>, t: Throwable) {
    
        }//통신 실패한 경우
    })
    
    //POST
    val params = HashMap<String, Any>()
    params.put("name","홍길동")
    params.put("age", 20)
    params.put("intro","안녕하세요")
    service.createStudent(params).enqueue(object : Callback<Person>{
        override fun onResponse(call: Call<Person>, response: Response<Person>) {
            if(response.isSuccessful){
                val person = response.body()
                val name = person?.name
            }
        }
    
        override fun onFailure(call: Call<Person>, t: Throwable) {
    
        }
    })
    
    //POST 요청 간단한 버전(HashMap 사용x)
    val person = Person(name="김철수", age=12, intro = "반갑습니다")//id값은 서버가 자동으로 생성함
    service.createStudentEasy(person).enqueue(object : Callback<Person>{
        override fun onResponse(call: Call<Person>, response: Response<Person>) {
            if(response.isSuccessful){
                val person = response.body()
                val name = person?.name
            }
        }
    
        override fun onFailure(call: Call<Person>, t: Throwable) {
    
        }
    })

     

    <서버에서 객체 받지 않는 경우 POST 요청하기>

    @POST("baseURL 뒷부분")
    @FormUrlEncoded
    fun createStudent(
        @Field("value1") 변수명1: String,
        @Field("value2") 변수명2: String,
        @Field("value3") 변수명3: String
    ):Call<Person>

     

    <Retrofit 장점>

    - AsyncTask를 사용한 통신 방식이나 Volley에 비해 응답 속도 매우 빠름

    - 동기/비동기 선택 가능

    - Call 요청 취소 가능

     

    <Retrofit 동기 실행 방법>

    - 이 글을 처음 쓴게 꽤 오래 되었는데 쓴 당시에만 해도 Retrofit이 동기(execute() 사용), 비동기(enqueue() 사용) 방식으로 나뉘어져 있는지 몰랐다.. 그래서 서버로 부터 값을 받아 리턴하는 메서드 구현 당시에도 왜 비동기로 동작하는지 이해하지 못했다 오늘도 더 열심히 해야겠다고 다짐하고 간다..ㅜ

     

    val okHttpClient = OkHttpClient.Builder()
        .readTimeout(5, TimeUnit.SECONDS)
        .connectTimeout(5, TimeUnit.SECONDS)
        .build()
    
    val retrofit = retrofit2.Retrofit.Builder().baseUrl("http://apis.data.go.kr")
        .addConverterFactory(ScalarsConverterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttpClient)
        .build()
    
    val service = retrofit.create(RetrofitService::class.java)
    
    override fun getHoliday(): ArrayList<String> {
        var list = ArrayList<String>()
    
        runBlocking {
            CoroutineScope(Dispatchers.IO).async {
                val response = service.get_holiday().execute()//동기 실행
    
                if (response.isSuccessful) {
                    list = Json_to_List().HolidayJson_to_List(response.body().toString())
                }
            }.await()
        }
    
        return list
    
    }//getHoliday

     

    반응형

    'Android > Kotlin' 카테고리의 다른 글

    [Android/Kotlin] ViewModel, LiveData, 화면 회전 처리  (0) 2022.07.11
    [Android/Kotlin] 권한(Permission)  (0) 2022.07.10
    [Android/Kotlin] AsyncTask  (0) 2022.07.09
    [Android/Kotlin] Realm  (0) 2022.07.08
    [Android/Kotlin] Sharedpreference  (0) 2022.07.08

    댓글

Designed by Tistory.