BuNa_
IT Story
BuNa_
전체 방문자
오늘
어제
  • 분류 전체보기 (117)
    • CS (14)
      • 운영체제 (8)
      • 네트워크 (0)
      • Design Pattern (1)
      • OOP (4)
    • 대외활동 (24)
      • 우아한테크코스 (14)
      • DND 동아리 (4)
      • UMC 동아리 (5)
      • 해커톤 (1)
    • Android (29)
      • MVVM (2)
      • 스터디 (11)
      • Compose (3)
      • Unit Test (1)
    • Project (5)
      • 어따세워 (5)
      • DnD 과외 서비스 (0)
    • Programming (11)
      • Kotlin (4)
      • 파이썬 (7)
    • Git (1)
    • 인공지능 (22)
    • 백준 (8)
    • 기타 (3)
      • IntelliJ (1)
      • 일상 (0)

블로그 메뉴

  • 홈

공지사항

인기 글

태그

  • Baekjoon
  • 셀레니움
  • 운영체제
  • External fragmentation
  • RecyclerView
  • MVVM
  • 우테코
  • Ai
  • Android
  • 인공지능
  • UMC
  • k-means++
  • 백준
  • 어따세워
  • 다이나믹 프로그래밍
  • 우아한테크코스
  • 파이썬
  • 인공지능 분류
  • 안드로이드
  • 딥러닝
  • K-means
  • 선형회귀
  • 원시값 포장
  • 우테코 프리코스
  • 우테코 5기
  • 객체지향 생활체조
  • 컴공선배
  • ViewModel
  • Compose
  • 외부 단편화

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
BuNa_

IT Story

[Android][어따세워] Retrofit2를사용하여 주차장 공공 데이터를 가져오자!
Project/어따세워

[Android][어따세워] Retrofit2를사용하여 주차장 공공 데이터를 가져오자!

2021. 12. 4. 23:17

 

'어따세워' 앱의 핵심 기능인 주차장 정보를 보여주기 위해서는 많은 양의 주차장 데이터가 필요하다.

그렇다면 이런 데이터들은 어떻게 얻을 수 있을까..?

 

'공공데이터 포털 사이트'

바로 '공공데이터 포털'에서 제공받을 수 있다.

https://www.data.go.kr/index.do

 

공공데이터 포털

국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase

www.data.go.kr

 

공공 데이터 포털에는 주차장 데이터뿐만 아니라 다양한 데이터를 제공해주고 있으므로,

여러 데이터를 잘 활용하면 훌륭한 아이디어와 앱을 만들어낼 수 있다.

 

그럼 이제 본격적으로, 안드로이드에서 이러한 데이터들을 가져오는 방법을 살펴보도록 하자!

 

'주차장 데이터 활용 신청 결과 화면'

 

우선, 데이터를 사용하기 위해서는 '저는 이 데이터를 사용하겠습니다!' 라는 활용 신청 과정이 필요하다.

어려운 것은 아니고, 간단하게 활용 목적을 작성하고 제출하면 왠만한 데이터들은 바로 사용이 가능하다.

신청을 완료하면 위 사진처럼 '일반 인증키'를 제공해준다.

반드시 일반 인증키가 있어야 데이터를 가져올 수 있기에 절대 분실해서는 안되며, 노출 시 보안상 문제가 될 수 있으므로 가급적 private 하게 보관하는 것이 최선이다.

 

안드로이드에서 데이터를 보다 쉽게 가져올 수 있도록 제공해주는 Retrofit2 라이브러리가 있다.

이번 프로젝트를 진행하면서 Retrofit을 처음 사용해봤는데, 너무 간단하고 효율적이라 더욱 매료되었다.

Retrofit에 대해 처음 들어본 분들을 위해 짧게 설명하자면, Square사에서 제공해주는 REST API로, 서버와 클라이언트간의 Http 통신을 위한 인터페이스이다.

자세한 정보는 아래 링크에서 확인이 가능하다.

Retrofit - http://square.github.io/retrofit

 

dependencies {
	...
    
    // Retrofit2
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.google.code.gson:gson:2.8.6'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
    
    ...
}

 

우선 build.gradle(app level)의 dependencies 블록에 위 라이브러리들을 작성해준다.

여기서 gson은 retrofit을 통해 받아온 데이터를 json 형태로 변환할 수 있도록 도와주는 라이브러리이다.

 

위 코드를 작성하였다면, 이제 공공데이터에서 받아올 데이터를 담을 DTO(Data Transfer Object)를 생성해준다.

(코드가 너무 길기때문에 글의 흐름을 망칠 수 있으므로 접어놓았다.)

(더보기 클릭)

더보기
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName

data class ParkingLot(
    @Expose
    @SerializedName("response")
    val response: Response
)

data class Response(
    @Expose
    @SerializedName("header")
    val header: Header,
    @Expose
    @SerializedName("body")
    val body: Body
)

data class Body(
    @Expose
    @SerializedName("items")
    val items: List<Items>,
    @Expose
    @SerializedName("totalCount")
    val totalcount: String,
    @Expose
    @SerializedName("numOfRows")
    val numofrows: String,
    @Expose
    @SerializedName("pageNo")
    val pageno: String
)

data class Items(
    @Expose
    @SerializedName("prkplceNo")
    val prkplceno: String,
    @Expose
    @SerializedName("prkplceNm")
    val prkplcenm: String,
    @Expose
    @SerializedName("prkplceSe")
    val prkplcese: String,
    @Expose
    @SerializedName("prkplceType")
    val prkplcetype: String,
    @Expose
    @SerializedName("rdnmadr")
    val rdnmadr: String,
    @Expose
    @SerializedName("lnmadr")
    val lnmadr: String,
    @Expose
    @SerializedName("prkcmprt")
    val prkcmprt: String,
    @Expose
    @SerializedName("feedingSe")
    val feedingse: String,
    @Expose
    @SerializedName("enforceSe")
    val enforcese: String,
    @Expose
    @SerializedName("operDay")
    val operday: String,
    @Expose
    @SerializedName("weekdayOperOpenHhmm")
    val weekdayoperopenhhmm: String,
    @Expose
    @SerializedName("weekdayOperColseHhmm")
    val weekdayopercolsehhmm: String,
    @Expose
    @SerializedName("satOperOperOpenHhmm")
    val satoperoperopenhhmm: String,
    @Expose
    @SerializedName("satOperCloseHhmm")
    val satoperclosehhmm: String,
    @Expose
    @SerializedName("holidayOperOpenHhmm")
    val holidayoperopenhhmm: String,
    @Expose
    @SerializedName("holidayCloseOpenHhmm")
    val holidaycloseopenhhmm: String,
    @Expose
    @SerializedName("parkingchrgeInfo")
    val parkingchrgeinfo: String,
    @Expose
    @SerializedName("basicTime")
    val basictime: String,
    @Expose
    @SerializedName("basicCharge")
    val basiccharge: String,
    @Expose
    @SerializedName("addUnitTime")
    val addunittime: String,
    @Expose
    @SerializedName("addUnitCharge")
    val addunitcharge: String,
    @Expose
    @SerializedName("dayCmmtktAdjTime")
    val daycmmtktadjtime: String,
    @Expose
    @SerializedName("dayCmmtkt")
    val daycmmtkt: String,
    @Expose
    @SerializedName("monthCmmtkt")
    val monthcmmtkt: String,
    @Expose
    @SerializedName("metpay")
    val metpay: String,
    @Expose
    @SerializedName("spcmnt")
    val spcmnt: String,
    @Expose
    @SerializedName("institutionNm")
    val institutionnm: String,
    @Expose
    @SerializedName("phoneNumber")
    val phonenumber: String,
    @Expose
    @SerializedName("latitude")
    val latitude: String,
    @Expose
    @SerializedName("longitude")
    val longitude: String,
    @Expose
    @SerializedName("referenceDate")
    val referencedate: String,
    @Expose
    @SerializedName("insttCode")
    val insttcode: String
)

data class Header(
    @Expose
    @SerializedName("resultCode")
    val resultcode: String,
    @Expose
    @SerializedName("resultMsg")
    val resultmsg: String,
    @Expose
    @SerializedName("type")
    val type: String
)

 

이렇게 ParkingLot DTO를 작성했다면, 이젠 본격 Retrofit을 사용할 시간이다.

 

interface ParkingLotAPI {

    @GET("/openapi/tn_pubr_prkplce_info_api")
    fun getParkingLot(
        @Query("serviceKey") apiKey: String,
        @Query("pageNo") pageNo: Int = 0,
        @Query("numOfRows") numOfRows: Int = 100,
        @Query("type") type: String = "json"
    ): Call<ParkingLot>
}

 

이는 class가 아닌 interface로 생성해주어야 하며, 공공 데이터 포털에서 데이터를 가져와야 하는 상황이기 때문에 GET방식을 사용하였다.

@GET 안에는 base url 이후에 나올 uri를 추가로 작성해주면 된다.

그리고 @Query에는 api에서 요청하는 Request Parameter를 작성해주면 된다.

 

open class BaseRetrofitBuilder {
    open val baseUrl = "본인이 사용할 Base Url"
    private val gson = GsonBuilder()
        .setLenient()
        .create()

    private val clientBuilder = OkHttpClient.Builder().addInterceptor(
        HttpLoggingInterceptor().apply {
            // body 로그를 출력하기 위한 interceptor
            level = HttpLoggingInterceptor.Level.BODY
        }
    )

    private val retrofit = Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .client(clientBuilder.build())
        .build()

    protected fun getRetrofit(): Retrofit {
        return retrofit
    }

}

 

본인은 이번 프로젝트에서 액티비티, 프래그먼트, 뷰모델 모두 Base Class를 만들어 유지보수성을 높이는 훈련을 하고 있다.

따라서 RetrofitBuilder 클래스도 Base로 만들어 다른 상황에서도 쉽게 사용할 수 있도록 하였다.

우선 baseUrl 변수에는 본인이 사용할 base url을 입력해주면 된다.

공공 데이터 포털에서는 데이터를 제공해주는 url이 적혀있기 때문에 참고해서 작성하기 바란다.

또한, converter로 gson을 지정해주고, 요청한 값이 body부분의 로그를 출력하기 위해서 interceptor까지 적용해주었다.

 

object RetrofitParkingAPIBuilder : BaseRetrofitBuilder() {

    // base url을 공공 데이터 포털 사이트로 지정
    override val baseUrl: String
        get() = "http://api.data.go.kr"
    const val API_KEY = "공공 데이터 포털에서 제공받은 API KEY"

    fun getParkingLots() {
        val api = getRetrofit().create(ParkingLotAPI::class.java)

        api.getParkingLot(API_KEY).enqueue(object : Callback<ParkingLot> {
            override fun onResponse(call: Call<ParkingLot>, response: Response<ParkingLot>) {
                
                val resultCode = response.body()?.response?.header?.resultcode
                val resultMessage = response.body()?.response?.header?.resultmsg

                // result code가 00이면 정상적으로 데이터를 가져옴
                if(resultCode == "00") {
                    response.body()?.response?.body?.items?.forEach { data ->
                        // 주차장 이름 출력
                        Timber.d("주차장 명 : ${data.prkplcenm}")
                    }
                } else { // 데이터를 정상적으로 가져오지 못했을 때
                    Timber.d("에러 발생 : ${resultCode.toString()}")
                    Timber.d("에러 발생 : ${resultMessage.toString()}")
                }

            }

            override fun onFailure(call: Call<ParkingLot>, t: Throwable) {
                Timber.e("데이터 불러오기 실패 : ${t.message}")
            }
        })
    }
}

 

API_KEY 변수란에 정확한 키값을 입력해주지 않으면 오류가 나기때문에 반드시 확실한 복붙이 필요하다..!! 😂

retrofit 객체의 create() 메서드 안에는 우리가 아까 만들어둔 ParkingLotAPI 인터페이스를 입력해준다.

그렇게 만든 api의 getParkingLot() 메서드에 API_KEY를 전달해주고 enqueue에 콜백과 함께 코드를 작성해주면 끝이다.

공공 데이터 포털에서 result code를 확인한 결과 "00" 일 경우에만 정상적으로 데이터를 가져온 것이므로, 데이터의 주차장 이름만 가져와서 로그를 찍어보았다.

그렇지 않은 경우는 에러가 발생한 상황이므로 예외 처리를 해주면 된다.

 

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 주차장 데이터 가져오기
        RetrofitParkingAPIBuilder.getParkingLots()
        
        ...
}

 

이제 Activity에서 테스트로 getParkingLots() 메서드를 실행해보자!

 

'Retrofit을 사용하여 주차장 데이터를 가져와 이름만 출력한 결과'

 

다행히 정상적으로 데이터를 모두 가져온 것을 확인할 수 있었다.

 

'Retrofit의 interceptor에 걸린 Body 부분 로그 자동 출력'

 

또한, interceptor에서 Body단의 내용을 출력하도록 지정해두었기 때문에 위 사진처럼 정상적으로 로그가 출력되는 모습까지 확인하였다.

만약 문제가 있다면 200 OK라고 나오지 않고 다른 메세지가 출력될 것이다.

 

이렇게 성공적으로 데이터를 가져왔지만, 프로젝트에서 이 데이터를 활용하는 데에 있어 가장 큰 문제가 따로 있었으니..

이 내용은 다음 포스팅에서 다루도록 하겠다..!!

저작자표시 비영리 변경금지 (새창열림)

'Project > 어따세워' 카테고리의 다른 글

[Android][어따세워] 앱 아이콘 제작 / 해상도별 사이즈 조절(포토샵)  (0) 2021.12.09
[Android][어따세워] [1] Android MVVM 회원가입&로그인 - 파이어베이스를 사용하여 계정을 등록해보자!  (0) 2021.12.05
[Android][어따세워] 메타버스 스터디룸(게더타운) 개설!  (0) 2021.11.30
[Android][어따세워] 토이 프로젝트 주제 정하기  (6) 2021.11.29
    'Project/어따세워' 카테고리의 다른 글
    • [Android][어따세워] 앱 아이콘 제작 / 해상도별 사이즈 조절(포토샵)
    • [Android][어따세워] [1] Android MVVM 회원가입&로그인 - 파이어베이스를 사용하여 계정을 등록해보자!
    • [Android][어따세워] 메타버스 스터디룸(게더타운) 개설!
    • [Android][어따세워] 토이 프로젝트 주제 정하기
    BuNa_
    BuNa_
    안드로이드 개발자를 향해 달리고 있는 공대생입니다! 🧑 Android, Kotlin, Java, Python 등 학습하고 있는 내용과 프로젝트를 주로 업로드하고 있습니다. 지적과 조언은 언제나 환영입니다!😊 github : https://github.com/tmdgh1592

    티스토리툴바