amplitude.init("2e2e5a386856efdf3237cf254a9d14d9"

[개념 콕] 안드로이드 Retrofit

내일배움캠프 수료생이 개발에 꼭 필요한 핵심 개념만 콕 집어 드립니다.
Jun 07, 2024
[개념 콕] 안드로이드 Retrofit
✍🏼
안드로이드 입문자 여러분, 정보가 너무 많고 배워야 할 것도 산더미라 어디서부터 시작해야 할지 막막하신가요? 내일배움캠프 수료생들이 4개월 동안 배운 엄선된 안드로이드 핵심 개념을 직접 정리해서 알려 드립니다. 공부하다 막히거나 헷갈리는 개념이 있다면 개념 콕으로 정리해보세요.

Retrofit이란?

Retrofit은 Square Inc.에서 개발한, 안드로이드 및 자바 전용 HTTP 클라이언트 라이브러리입니다. 간단하게 말하면, 외부에 API를 요청하고, 받아들이는 작업을 자바 언어로 변환시켜주는 라이브러리입니다. 먼저 API는 무엇이고, Retrofit은 언제 사용하는지 알아봅시다.
 

API(Application Programming Interface)

API는 응용 프로그램 프로그래밍 인터페이스의 약자인데 이렇게만 말하면 이해가 잘 되지 않습니다.
간단하게 예시를 들어봅시다.
notion image
위의 사진처럼 클라이언트를 손님, 서버를 가게 주인이라 가정하고 손님이 메뉴를 주문하는 상황이라 생각해봅시다. 손님(클라이언트)은 가게 주인(서버)에게 메뉴를 요청합니다. 이 때, 메뉴를 요청할 때에는 메뉴판에 나와 있는 메뉴대로 주문해야 합니다. 가게 주인은 요청에 맞춰서 음식을 제공합니다.
예를 들어서, 카페를 운영하는 주인은 손님이 아메리카노를 주문하면 아메리카노를, 카페라떼를 주문하면 카페라떼를 제공하지만, 짜장면 혹은 초밥을 주문해도 보통 카페 메뉴판에는 없는 메뉴이니 주문을 받지 않습니다. 이렇듯, API는 서버에서 클라이언트에게 제공하는 일종의 메뉴판이라고 보시면 됩니다. API에는 각 서버마다 고유 형식이 있으며, 그 형식에 맞춰서 요청을 해야지만 그 형식에 맞는 정보를 제공해줍니다.
 
예를 들어 스타벅스에서 주문하는 경우를 API로 바꾸면 아래와 같이 정리할 수 있습니다.
<호스트: http://starbucks.com>
요청
URL 예제
아메리카노 한 잔 주세요
/coffee/americano
망고 프라푸치노 한 잔 주세요
/frappuccino/mango
콜드브루 두 잔 주세요
/coffee/coldbrew?quantity=2
아메리카노 두 잔 전부 헤이즐넛 시럽 넣어주세요
/coffee/americano?quantity=2&syrup=hazelnet
 

XML & JSON (JavaScript Object Notation)

여러분들이 API를 요청한 뒤 데이터 받아올 때 2가지 형식으로 받을 수 있는데 그것이 바로 xml과 json 입니다. 그렇다면, 이 xml과 json은 아래 코드를 통해 알아봅시다.
 

XML (eXtensible Markup Language)

W3C에서 개발된, 다른 특수한 목적을 갖는 마크업 언어를 만드는 데에 사용하도록 권장하는 다목적 마크업 언어입니다. 구조를 가지면서 메타데이터를 기술할 수 있는 다목적 문서 형식 표준입니다.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE recipe PUBLIC "-//Happy-Monkey//DTD RecipeBook//EN" "http://www.happy-monkey.net/recipebook/recipebook.dtd"> <recipe> <title>Peanutbutter On A Spoon</title> <ingredientlist> <ingredient>Peanutbutter</ingredient> </ingredientlist> <preparation>Stick a spoon in a jar of peanutbutter, scoopand pull out a big glob of peanutbutter.</preparation> </recipe>
 

JSON (JavaScript Object Notation)

Javascript 객체 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷입니다.
{ "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" ] } ] }
 

XML과 JSON의 차이점 비교

JSON
XML
형식
JSON은 키-값 페어가 있는 맵과 유사한 구조를 사용합니다.
XML은 다양한 데이터 범주에 대한 네임스페이스가 있는 트리 구조로 데이터를 저장합니다.
구문
JSON의 구문은 더 간결하고 읽고 쓰기가 더 쉽습니다.
XML 구문은 일부 문자를 엔티티 참조로 대체하여 더 자세한 정보를 제공합니다.
구문 분석
표준 JavaScript 함수를 사용하여 JSON을 구문 분석할 수 있습니다.
XML 구문 분석기를 사용하여 XML을 구문 분석해야 합니다.
스키마 문서
JSON은 간단하고 유연합니다.
XML은 복잡하고 유연성이 떨어집니다.
데이터 유형
JSON은 숫자, 객체, 문자열 및 부울 배열을 지원합니다.
XML은 모든 JSON 데이터 유형과 부울, 날짜, 이미지 및 네임스페이스와 같은 추가 유형을 지원합니다.
사용 편의성
JSON은 파일 크기가 더 작고 데이터 전송 속도가 더 빠릅니다.
XML 태그 구조는 쓰고 읽기가 더 복잡하고, 파일 용량을 더 크게 만듭니다.
보안
JSON은 XML보다 안전합니다.
잠재적인 보안 위험을 줄이려면 XML로 작업할 때는 DTD를 비활성화해야 합니다.
JSON은 간결한 형식으로 데이터 전송 속도가 더 빠르고, XML은 여러 데이터 유형을 여러 변수와 함께 저장하며 복잡한 데이터의 오류를 검사하는 데에 유리하다는 점을 꼭 기억해주세요.
 

Retrofit의 장점

  1. 코드의 간결성
      • 복잡한 HTTP API 요청을 쉽고 간결하게 만들 수 있습니다.
      • 간단한 어노테이션을 통해 요청 메서드와 URL을 정의할 수 있습니다.
  1. 안정성과 확장성
      • 내부적으로 OkHttp 라이브러리를 사용하여 통신, 이를 통해 안정적인 통신이 가능합니다.
      • 인터셉터를 사용하여 요청/응답 프로세스를 확장하거나 수정할 수 있습니다.
  1. 다양한 플러그인과 컨버터 지원
      • 다양한 데이터 형식(JSON, XML 등)에 대해 데이터 변환 컨버터를 제공합니다.
      • RxJava, Coroutines와 같은 비동기 프로그래밍 라이브러리와 연동 가능합니다.
 

Retrofit 사용법

이제 Retrofit을 어떻게 사용하는지 알아봅시다. 우선은 build.gradle에서 라이브러리를 추가해줍니다.
// build.gradle (Module: app) dependencies { implementation 'com.squareup.retrofit2:retrofit:2.x.x' implementation 'com.squareup.retrofit2:converter-gson:2.x.x' // Gson 컨버터 추가 }
 
그리고 API 인터페이스를 만듭니다.
interface ApiService { @GET("users/{id}") fun getUser(@Path("id") id: Int): Call<User> }
여기서 User는 서버 응답으로 받아올 데이터 모델 클래스입니다.
 
그 다음, Retrofit 인스턴스를 생성합니다.
val retrofit = Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build() val apiService = retrofit.create(ApiService::class.java)
 
마지막으로 응답을 요청하면 되는데 요청 방법은 동기식 요청과 비동기식 요청이 있습니다.
동기와 비동기의 차이는 동기란 현재 스레드에서 실행되며, 응답이 올 때까지 다음 코드의 실행이 중단되는 것, 비동기란 콜백을 사용하여 백그라운드에서 실행되며, 응답이 오면 해당 콜백이 호출되는 것을 의미합니다.
 
  • 동기식 요청
val response: Response<User> = apiService.getUser(id).execute()
 
  • 비동기식 요청
apiService.getUser(id).enqueue(object: Callback<User> { override fun onResponse(call: Call<User>, response: Response<User>) { if (response.isSuccessful) { val user: User? = response.body() } } override fun onFailure(call: Call<User>, t: Throwable) { // 오류 처리 Log.e("API_ERROR", t.message ?: "Unknown error") } })
그런데 이렇게 말은 했지만, 처음에 Retrofit을 실제로 써보게 되면 꽤나 헤맬 수 있습니다. 그렇기 때문에 이걸 실제로는 어떻게 사용하는지 익히는 것도 중요한데요. 실전을 통해서 알아봅시다.
 

실제 사용 예 (Feat. 카카오 API)

카카오 API를 통해 실제로 Retrofit을 어떻게 활용하는지 알아봅시다. 단, 이것은 방법의 한 가지일 뿐 정답이 아닙니다. 참고로 봐주세요.
 
카카오 API를 이용하기 위해서는 아래의 사이트에서 회원가입을 진행하면 됩니다.
그리고 여러 API 중에서 저희는 검색 API를 사용하려고 합니다. 그 중에서 이미지 검색을 한번 해보도록 하죠.
 
카카오에 들어가게 되면 다음과 같이 API 사용법을 알려줍니다.
notion image
notion image
여기서 기본적인 URL, 즉 Base URL은 "https://dapi.kakao.com"입니다. 이걸 어떻게 알 수 있냐면, 검색 API도 여러가지가 있는데 위의 URL이 반복적으로 적혀 있습니다. 따라서 나중에 인터페이스에서 @GET 다음 () 안에는 “v2/search/image”를 집어넣어야 합니다.
요청 부분은 API 인터페이스에 집어넣을 때 적어야하는 부분입니다. 카카오 API를 불러올 때 필수적으로 적어야하는 사항은 헤더의 “Authorization”, 그리고 쿼리의 “query”가 있겠네요. 그 이외의 부분은 필요한 것만 적으시면 됩니다.
 
notion image
notion image
그 다음은 응답 부분입니다. 응답 부분은 데이터를 받아올 때 뭘 받아올지 적는 부분인데, 위에서도 말했다시피 API를 데이터로 받을 때 xml 혹은 json 형식으로 받게 됩니다.
 
카카오에서는 (친절하게) 제공한 응답 예시를 살펴봅시다.
notion image
형식을 보니 json으로 보내주네요. 따라서, 저희는 json을 받는 data class를 생성하면 됩니다.
 
 
이제 이걸 토대로, 어떻게 사용해야 하는지 알아봅시다.
우선, API 인터페이스를 만들어야겠죠?
interface NetworkInterface { @GET("v2/search/image") fun getSearchResult( @Header("Authorization") apiKey: String = "KakaoAK $REST_API_KEY", @Query("query") query: String, @Query("size") size: Int = 80 ): Call<Search> }
이 때, REST_API_KEY는 가급적이면 공개하지 않는 것이 좋습니다. 이 API KEY는 일종의 개인정보이기 때문에, 이걸 드러낸다는 것은 본인의 개인정보를 남에게 보여주는 것과 마찬가지입니다. 개인이 사용할 목적이면 굳이 그럴 필요는 없지만, 앱을 배포할 때에는 이 키를 가급적이면 숨기는 것이 좋습니다.
 
다음은 인스턴스를 생성합니다.
object NetworkClient { private const val BASE_URL = "https://dapi.kakao.com" private fun createOkHttpClient() : OkHttpClient { val interceptor = HttpLoggingInterceptor() if (BuildConfig.DEBUG) { interceptor.level = HttpLoggingInterceptor.Level.BODY } else { interceptor.level = HttpLoggingInterceptor.Level.NONE } private val searchRetrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(createOkHttpClient()) .build() val searchApi: NetworkInterface = searchRetrofit.create(NetworkInterface::class.java) }
 
그 다음, JSON을 받는 data class를 생성합니다.
data class Search( @SerializedName("documents") val documents: MutableList<Document> ) data class Document ( @SerializedName("thumbnail_url") val thumbnailUrl: String, @SerializedName("display_sitename") val siteName: String, @SerializedName("datetime") val datetime: String )
여기서는 Document 중에서 저희가 받을 부분만 따로 받아오는 것이 가능하므로 필요한 것만 골라 적으시면 됩니다. 그런데 잠깐만. 한 가지 이상한 점이 있습니다. 카카오 API에서는 datetime의 형식이 datetime으로 되어 있는데 여기는 string으로 되어 있네요? 이것은 Android Studio, 즉 Kotlin에서는 datetime이라는 자료형은 취급하지 않기 때문입니다. 곧바로 사용할 수 없는 것이죠. 따라서 이건 String으로 받은 다음에 변환하는 작업이 한번 더 필요합니다.
 
이제 이것들을 토대로, 요청하는 일만 남았습니다. 어디서 요청을 할지는 자유지만, 저는 Fragment에서 비동기 요청을 했습니다. MVVM의 경우 Fragment에서 받지 않고 ViewModel에서 받을 수 있습니다.
private fun callKeyword(keyword: String) { NetworkClient.searchApi.getSearchResult("KakaoAK $REST_API_KEY", query = keyword) .enqueue(object : Callback<Search> { @SuppressLint("NotifyDataSetChanged") override fun onResponse(call: Call<Search>, response: Response<Search>) { val body = response.body() body?.let { response.body()!!.documents.forEach{document -> val title = document.siteName val datetime = document.datetime val url = document.thumbnailUrl mItems.add(SearchItem(title, datetime, url)) } } adapter.items = mItems adapter.notifyDataSetChanged() Log.d("api검사", "$mItems") } override fun onFailure(call: Call<Search>, t: Throwable) { Log.d("api검사", "네트워크 오류 / 데이터변환 오류.") } }) }
이렇게 하면 카카오 API에서 이미지를 받아올 수 있게 됩니다. 이제 여기서 어댑터를 만들고, 레이아웃을 만들어서 이미지를 검색하는 앱을 만드는 과정은 여러분이 직접 완성하시면 됩니다!
 
 
 

내일배움캠프는 개발에 필요한 핵심만 배웁니다

지금까지 꼭 필요한 안드로이드 지식에 대해 알아보았습니다. 내일배움캠프에서는 전문가들이 선별한 핵심 안드로이드 개발 지식으로 개발 공부도, 취업도 보다 효율적으로 할 수 있는데요. 국내 유수의 IT기업 출신 튜터님들과 실습 위주의 독보적인 커리큘럼으로 개발자 취업을 체계적으로 준비해보세요. 내일배움캠프 4개월, 여러분 인생의 가장 큰 터닝 포인트입니다.
 
 
참고 문헌
 
CREDIT
글 | 김기원 내일배움캠프 수료생 편집 | 정효재 팀스파르타 에디터
 
 

취업 준비, 어디서부터 시작해야 할지 모르겠다면?

 
🧐비전공자인데 IT 업계 취업할 수 있을까?
😟프로젝트 경험이 부족한데, 어떻게 준비해야 할까?
🥺IT 기업으로 이직하고 싶은데 뭐부터 시작해야 할까?
 
이런 고민을 하고 있다면, 내일배움캠프의 IT 취업 컨설팅을 받아보세요.
취업 코칭 전문가들이 여러분의 고민을 해결해 드립니다.
 
다음 링크에 이메일을 입력하시면 메일로 1:1 커리어 상담권과 취준 자료집을 보내드릴게요.
 
Share article
Subscribe to our newsletter
RSSPowered by inblog