Retrofit은 안드로이드 및 Java용 HTTP클라이언트 라이브러리입니다. 주로 RESTful API를 통신하기 위한 간편하고 강력한 도구로 사용됩니다.
Retrofit2 API는 주로 인터페이스를 정의하는 것으로 시작합니다. 이 인터페이스는 서버와의 통신을 위한 HTTP요청 메소드와 해당 요청에 필요한 매개변수를 정의합니다.
예시
interface ApiService {
@GET("posts")
suspend fun getPosts(): Response<List<Post>>
@GET("posts/{id}")
suspend fun getPostById(@Path("id") postId: Int): Response<Post>
@POST("posts")
suspend fun createPost(@Body post: Post): Response<Post>
@PUT("posts/{id}")
suspend fun updatePost(@Path("id") postId: Int, @Body post: Post): Response<Post>
@DELETE("posts/{id}")
suspend fun deletePost(@Path("id") postId: Int): Response<Void>
@GET("search")
suspend fun searchPosts(
@Query("q") query: String,
@Query("page") page: Int,
@Query("limit") limit: Int
): Response<List<Post>>
}
✨ 여기서 헤더란? HTTP 요청이나 응답 메시지에 추가적인 정보를 포함하는데 사용되는 부분입니다. HTTP헤더는 클라이언트와 서버 간의 통신을 원활하게 하고, 요청이나 응답에 대한 다양한 메타데이터를 제공합니다.
fun provideMapApiService(retrofit: Retrofit): MapApiService{
return retrofit.create(MapApiService::class.java)
}
fun provideFoodApiService(retrofit: Retrofit): FoodApiService{
return retrofit.create(FoodApiService::class.java)
}
// provideMapApiService, provideFoodApiService는 각각 매개변수로 Retrofit 객체를 받고, 이를 사용하여 각각 MapApiService, FoodApiService 인터페이스를 생성합니다.
fun provideMapRetrofit(
okHttpClient: OkHttpClient,
gsonConverterFactory: GsonConverterFactory
): Retrofit{
return Retrofit.Builder()
.baseUrl(Url.TMAP_URL)
.addConverterFactory(gsonConverterFactory)
.client(okHttpClient)
.build()
}
fun provideFoodRetrofit(
okHttpClient: OkHttpClient,
gsonConverterFactory: GsonConverterFactory
): Retrofit{
return Retrofit.Builder()
.baseUrl(Url.FOOD_URL)
.addConverterFactory(gsonConverterFactory)
.client(okHttpClient)
.build()
}
// provideMapRetrofit, provideFoodRetrofit는 OkHttpClient 및 GsonConverterFactory를 사용하여 지도용 Retrofit 인스턴스를 설정하는 함수입니다.
// GsonConverterFactory를 추가하여 JSON 데이터를 변환합니다.
fun provideGsonConvertFactory(): GsonConverterFactory{
return GsonConverterFactory.create()
}
// GsonConverterFactory를 생성하여 Gson을 사용하여 JSON을 직렬화하고 역직렬화하는 데 사용됩니다.
fun buildOkHttpClient(): OkHttpClient{
val interceptor = HttpLoggingInterceptor()
if (BuildConfig.DEBUG){
interceptor.level = HttpLoggingInterceptor.Level.BODY
}else{
interceptor.level = HttpLoggingInterceptor.Level.NONE
}
return OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.build()
}
// OkHttpClient를 설정하는 함수입니다. HttpLoggingInterceptor를 사용하여 네트워크 요청 및 응답을 로깅합니다.
interface MapApiService {
@GET(Url.GET_TMAP_POIS_AROUND)
suspend fun getSearchLocationAround(
@Header("appKey") appKey: String = Key.TMAP_API,
@Query("version") version: Int = 1,
@Query("categories") categories: String? = null,
@Query("callback") callback: String? = null,
@Query("count") count: Int = 20,
@Query("searchKeyword") keyword: String? = null,
@Query("areaLLCode") areaLLCode: String? = null,
@Query("areaLMCode") areaLMCode: String? = null,
@Query("resCoordType") resCoordType: String? = null,
@Query("searchType") searchType: String? = null,
@Query("multiPoint") multiPoint: String? = null,
@Query("searchtypCd") searchtypCd: String? = null,
@Query("radius") radius: String? = null,
@Query("reqCoordType") reqCoordType: String? = null,
@Query("centerLon") centerLon: String? = null,
@Query("centerLat") centerLat: String? = null
): Response<SearchResponse>
@GET(Url.GET_TMAP_REVERSE_GEO_CODE)
suspend fun getReverseGeoCode(
@Header("appKey") appKey: String = Key.TMAP_API,
@Query("version") version: Int = 1,
@Query("callback") callback: String? = null,
@Query("lat") lat: Double,
@Query("lon") lon: Double,
@Query("coordType") coordType: String? = null,
@Query("addressType") addressType: String? = null
): Response<AddressInfoResponse>
}
class DefaultMapRepository(
private val mapApiService: MapApiService,
private val ioDispatcher: CoroutineDispatcher
) : MapRepository {
override suspend fun getReverseGeoInformation(locationLatLngEntity: LocationLatLngEntity): AddressInfo? = withContext(ioDispatcher){
// 지리적 좌표에 해당하는 주소 정보를 가져오는 메서드
val response = mapApiService.getReverseGeoCode( // mapApiService를 사용하여 지리적 좌표를 기반으로 역지오코딩 요청
lat = locationLatLngEntity.latitude,
lon = locationLatLngEntity.longitude
)
if (response.isSuccessful){ // 요청이 성공적이면 응답 바디에서 AddressInfo 객체를 추출한다.
response.body()?.addressInfo
}else{ // 실패한 경우 null
null
}
}
}
data class AddressInfo(
// 역지오코딩 결과를 나타내는 데이터 클래스
// 각 필드는 주소와 관련된 다양한 정보를 포함하고 있으며, Gson라이브러리의 @SerializedName 어노테이션을 사용하여 JSON키와 매핑된다.
// @Expose 어노테이션은 해당 필드가 직렬화 및 역직렬화에 포함되어야 함을 나타낸다.
// fullAddress, addressType과 같은 부분은 모두 주소의 다양한 요소를 나타내는 옵셔널(String?)필드이다.
@SerializedName("fullAddress")
@Expose
val fullAddress: String?,
@SerializedName("addressType")
@Expose
val addressType: String?,
@SerializedName("city_do")
@Expose
val cityDo: String?,
@SerializedName("gu_gun")
@Expose
val guGun: String?,
@SerializedName("eup_myun")
@Expose
val eupMyun: String?,
@SerializedName("adminDong")
@Expose
val adminDong: String?,
@SerializedName("adminDongCode")
@Expose
val adminDongCode: String?,
@SerializedName("legalDong")
@Expose
val legalDong: String?,
@SerializedName("legalDongCode")
@Expose
val legalDongCode: String?,
@SerializedName("ri")
@Expose
val ri: String?,
@SerializedName("bunji")
@Expose
val bunji: String?,
@SerializedName("roadName")
@Expose
val roadName: String?,
@SerializedName("buildingIndex")
@Expose
val buildingIndex: String?,
@SerializedName("buildingName")
@Expose
val buildingName: String?,
@SerializedName("mappingDistance")
@Expose
val mappingDistance: String?,
@SerializedName("roadCode")
@Expose
val roadCode: String?
){
fun toSearchInfoEntity(locationLatLngEntity: LocationLatLngEntity) = MapSearchInfoEntity( // AddressInfo 객체를 MapSearchInfoEntity 객체로 변환하는 함수.
fullAddress = fullAddress ?: "주소 정보 없음", // 주소 정보
name = buildingName ?: "빌딩 정보 없음", // 건물 이름 정보
locationLatLng = locationLatLngEntity, // 위치 데이터
)
}
interface MapRepository {
// 인터페이스는 특정 기능을 구현해야 하는 메서드의 시그니처를 정의한다.
suspend fun getReverseGeoInformation(locationLatLngEntity: LocationLatLngEntity): AddressInfo?
// LocationLatLngEntity의 매개변수를 받아, 이를 기반으로 역지로코딩 수행.
// 수행하여 얻은 정보를 AddressInfo?타입으로 반환.
}
val addressInfo = mapRepository.getReverseGeoInformation(currentLocation) // mapRepository를 사용하여 현재 위치에 대한 역지오코딩 수행
다음과 같이 Retrofit2으로 TMAP API와 손쉽게 통신할 수 있습니다.
TMAP API로 부터 위치 데이터를 가져와서 최종적으로 HomeViewModel에 전달합니다.
이제 가져온 데이터를 자유롭게 가공하여 사용하면 됩니다.
저는 공부용 배달앱 클론 코딩에 사용해 보았습니다.. 많은 곳에 쓰일것 같으니 더욱 공부를 해놔야겠군요