
복습하면서 적어놓은 노트
가독성이 심하게 떨어지니까 혹시라도 검색을 통해 들어왔다면 다른 정리글을 보는것을 추천..
안드로이드에서 REST API를 통한 Http 통신을 할 수 있게 도와준다.
자원을 이름으로 구분하여 해당 자원의 상태(정보)를 주고 받는다.
요청을 보내는 주소만으로도 어떤 요청인지를 알아볼 수 있게 만든 API
REST API에서는 Http URI를 통해 자원을 명시하고
Http 메서드를 통해 해당 자원에 대한 CRUD OPERATION을 수행한다.
웹의 모든 자원에는 고유한 ID인 Http URI 를 부여한다.
/orders/order_id/1 이런 형태이다.
GET
데이터를 READ, 조회하는데 사용한다.
https://도메인/academy/a/students 에 GET 요청을 보낸다면 학원의 A반 학생들을 조회하는 요청이다.
POST
데이터를 CREATE, 새로운 정보를 추가하는데 사용한다.
https://도메인/academy/a/students에 POST 요청을 보낼때는
BODY에 {"idx" : 12, "name" : "배유준", "sex" : "male"}
이런식으로 정보를 담아서 보내주면 된다.
PUT
데이터를 UPDATE, 수정하는데 사용한다.
https://도메인/academy/a/students/15
BODY에 {"idx" : 15, "name" : "이도현", "sex" : "male"}
이런식으로 수정할 정보를 담아서 보내주면 된다.
DELETE
데이터를 DELETE, 삭제하는데 사용한다.
https://도메인/academy/a/students/15 는
15번 학생을 삭제하는 요청이다.
간단하다!
다양한 플러그인과 컨버터 지원
-1. 어떤 주소로 요청을 보내야 하는가?
-2. 어떤 형태로 응답을 받는가?
-3. 어떤 형태로 요청을 해야 하는가?
-4. 어떤 파라미터를 가지고 요청해야 하는가?
오늘의코드(https://todaycode.tistory.com/38) 님의 게시글을 참고하여 정리했다.
URL은 배민의 공지사항 api를 활용하였다.
이를 화면에 출력하는 안드로이드 앱을 구현하며 기본개념을 복습해 보는 시간을 가졌다.

개발자모드 - Network에서 공지사항이 담긴 응답을 찾는다.

Header에 들어가면 Request URL을 알아낼 수 있다.
Request의 응답은 JSON 또는 XML 등의 형태이다.
배민의 경우엔 JSON 형태의 응답을 사용한다.

요청의 메서드의 형태는 앞서 말했듯 GET, POST, PUT, DELETE 4가지가 있다.
위와같이 Request Method가 GET으로 설정 되어있으면, GET 요청을 해야만 응답을 받을 수 있다.
즉, 서버측에서 정해놓은 방법대로 요청을 해야 한다.

URL의 파라메터 목록은 Header 탭에서 한 눈에 볼 수있다.
저기서 필수 파라메터를 골라내야 한다.
Postman에 Request URL을 넣고 파라메터를 하나씩 제외하면서 요청을 보내다보면
응답 결과를 통해 필수 파라미터를 구분할 수 있다.
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.6.2")
implementation("com.squareup.retrofit2:converter-gson:2.6.0")
converter-gson은 응답 결과가 JSON일 때 객체로 변환해주는 데이터 변환 컨버터아다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.RestulApiRetrofit"
tools:targetApi="31"
android:usesCleartextTraffic="true"/>
</manifest>
인터넷 권한을 허용해준다.
그리고 Http로 시작하는 사이트에 접근할수 있도록 usesCleartextTraffic="true"로 설정해준다.
(안드로이드는 기본적으로 http 접근을 허용하지 않는다)

응답 JSON에 맞는 data class를 생성해야한다.
data class Notice(val timestamp: Long, val statusCode: String,
val statusMessage: String, val data: Data)
data class Data(val content: ArrayList<Content>)
data class Content(val seq: Long, val title: String, val startDisplayTime: String)
사실 이렇게 구조를 똑같이 다 옮길 필요는 없다.
사용하지 않을 파라메터는 지워준다.
data class Notice(val data: Data)
data class Data(val content: ArrayList<Content>)
data class Content(
@SerializedName("title")
val title: String,
@SerializedName("startDisplayTime")
val date: String
)
JSON과 동일한 변수명을 사용하면 자동으로 컨버터를 거치며 직렬화가 된다.
변수명을 다르게 하고싶다면 어노테이션을 사용하면 된다.
@SerializedName("")에 JSON의 변수명을 넣어주고, 변수를 선언할 때 원하는 변수명을 사용하면 된다.
저렇게 표시해두면 JSON을 받아오면서 GSON converter가 어노테이션을 보고 매핑해준다.
interface BaeminService {
@GET("contents?typeCode=notice&size=10")
fun loadNotice(@Query("page") page: String): Call<Notice>
}
우선 위와 같은 API interface를 만든다.
이곳에는 내가 어떤 요청을 어떻게 보낼건지 메서드를 정의해주면 된다.
@GET
Request Method를 어노테이션으로 정의한다.
contents?type=notice&size=10
어노테이션 안에 URL의 End Point를 넣어주면 되는데, 필수 파라미터가 있다면 같이 넣어줘야 한다.
@Query("page") page: String
page처럼 값을 동적으로 변경해야 하는 파라미터는
@Query 어노테이션을 이용해서 메서드를 호출할 때 값을 넘겨받아 주소에 포함시켜야 한다.
Call<T>
응답 받을 타입을 적으면 된다.
서버 호출이 필요할 때마다 인터페이스를 구현하는 것은 비효율적이기 때문에
Retrofit 객체는 싱글톤(Object)으로 구현하면 좋다.
object BaeminClient {
private const val baseUrl = "https://ceo.baemin.com/cms/v1/"
private val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(BaeminService::class.java)!!
}
baseUrl() 메서드에 어디로 요청을 보낼 것인지를 넘겨준다.
이때 주소의 끝은 항상 '/'로 끝나야 한다.
addConverterFactory()는 데이터를 파싱 할 converter를 추가하는 메서드이다.
JSON과 같은 데이터는 자바나 코틀린에서 바로 사용할 수 있는 데이터 형식이 아니기 때문에
이를 변환해주기 위해 converter를 사용해야 한다.
(여기서 사용한 Gson은 구글에서 만든 라이브러리이다.
다른 컨버터를 원한다면 그걸 사용해도 되고, 여러 개를 추가할 수도 있다.)
retrofit.create(BaeminService::class.java)
만들어진 Retrofit 객체를 이용해 Interface를 구현한다.
Repository에서는 Http 요청을 보내고 응답을 받는 작업을 한다.
class BaeminRepository {
fun loadBaeminNotice(page: Int, mCallback: MainActivity) {
val call = BaeminClient.service.loadNotice(page.toString())
call.enqueue(object : Callback<Notice> {
override fun onResponse(call: Call<Notice>, response: Response<Notice>) {
if(response.isSuccessful()){
mCallback.loadComplete(response.body()!!.data)
} else {
// 응답에 문제가 있음
}
}
override fun onFailure(call: Call<Notice>, t: Throwable) {
// 통신 실패
}
})
}
}
비동기 실행이므로 LiveData나 콜백등을 이용해서 받아온 응답을 처리해야 한다.
코루틴을 사용해도 된다.
여기서는 콜백을 사용했기때문에 액비비티의 콜백함수를 호출하여 응답을 처리한다.
(LiveData 사용시에는 LiveData를 통해 값을 받고 액티비티에서 옵저버를 통해 처리하면 된다.)
onFailure()와 response.isSuccessful()이 아닐경우의 차이점?
콜백 방식으로 응답을 처리할 때의 메서드들을 정리해보았다.
이것들은 코루틴을 사용하면 사용할 필요가 없다.
코루틴을 사용하자.
Call<T>는 Retrofit에서 HTTP 요청을 나타내는 객체이다.
아래 코드에서는 BaeminClient.service.loadNotice(page)가 반환하는 Call<Notice> 객체 call을 얻는다.
val call = BaeminClient.service.loadNotice(page.toString())
하지만 이 단계에서는 아직 네트워크 요청이 실행되지 않는다.
Call 객체는 기본적으로 네트워크 요청을 준비하는 역할을 한다.
실제 요청을 보내려면 execute() 또는 enqueue()를 호출해야 한다.
execute()또는enqueue()를 사용하여 요청을 실행할 수 있다.
enqueue: 비동기 방식
execute: 동기 방식
enqueue()는 Call<T>의 메서드로
비동기 네트워크 요청을 실행하는 역할을 한다.
비동기 요청이므로 별도의 스레드에서 실행된다.
enqueue()를 호출하면 Retrofit이 내부적으로 백그라운드에서 네트워크 요청을 실행하고,
완료되면 onResponse() 또는 onFailure()를 호출한다.
요청이 완료되면 Callback<T>을 통해 결과를 받을 수 있다.
Callback<T>는 Retrofit에서 비동기 요청의 결과를 처리하기 위한 인터페이스다.
T는 API 응답의 데이터 타입이다.
onResponse()와onFailure()두 가지 메서드를 반드시 구현해야 한다.
interface Callback<T> {
fun onResponse(call: Call<T>, response: Response<T>)
fun onFailure(call: Call<T>, t: Throwable)
}

참고자료