Android 공부/Android Library Study

서울시 공공데이터 API를 활용한 Paging Library 사용하기 - 1


0. 페이징을 공부해보자. 

https://developer.android.com/topic/libraries/architecture/paging

역시 시작은 Developer를 참고하자. 


간단한 소개에 따르면, 페이징은 한 번에 데이터를 작은 단위로 로드하고, 보여주기 위해서 사용하는 라이브러리고 소개를 하고 있다.


1. 페이징

알고 있는 페이징의 개념은 아래와 같다. data를 별도의 통신을 통해서 뭉텅이로 가져온다면, 그것을 보여줄 때, 우리는 무한 스크롤링, 페이지 단위, 앞 뒤 단위 등으로 해서 보여주는데, 페이징 라이브러리는 우리가 이러한 것을 RecyclerView를 통해서 만드는 것을 일정한 규칙을 만들어서, 통신을 제어하고, 보여주고, 데이터를 관리하는 행위들을 처리할 때, 페이징 라이브러리를 사용하게 되면 많은 이점을 얻을 수 있다.

2. API 선정

- 청년들을 위한, 정책을 포인트 개념으로 보여주기 위해서 프로젝트를 진행하고 있습니다.

- 예를 들면, 'JobCafe' 사회 정책을 통해서 청년들이 직업에 대한 상담을 받고, 공간을 대여합니다.

- 그래서, 이러한 JobCafe List를 얻고 싶었고, 현재 공공데이터에서는 

https://data.seoul.go.kr/dataList/datasetView.do?infId=OA-15356&srvType=S&serviceKind=1&currentPageNo=1

라는 API를 제공하고 있습니다.


3. Test를 통해서 API 실행여부 확인하기.

3-1. Data 클래스

API의 규격에 맞게 Data 클래스를 관리하고 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
data class WrappingJobCafeList(@SerializedName("jobCafeOpenInfo") val jobCafeList: JobCafeList)
 
data class JobCafeList(
    @SerializedName("list_total_count") val listTotalCount:Int,
    @SerializedName("RESULT") val SeoulApiResult:ApiResult,
    @SerializedName("row") val jobCafes:List<JobCafe>
)
 
data class ApiResult(
    @SerializedName("CODE") val code:String,
    @SerializedName("MESSAGE") val message:String)
 
data class JobCafe(
    @SerializedName("CAFE_NM") val cafeNM:String,
    @SerializedName("SMPL_INTRO") val smplIntro:String,
    @SerializedName("USE_DT") val useDt:String,
    @SerializedName("HOLI_DD") val holiDd:String,
    @SerializedName("FACLT_INFO1") val facltInfo1:String,
    @SerializedName("FACLT_INFO2") val facltInfo2:String,
    @SerializedName("FACLT_INFO3") val facltInfo3:String,
    @SerializedName("FACLT_INFO4") val facltInfo4:String,
    @SerializedName("FACLT_INFO5") val facltInfo5:String,
    @SerializedName("FACLT_INFO6") val facltInfo6:String,
    @SerializedName("FACLT_INFO7") val facltInfo7:String,
    @SerializedName("FACLT_INFO8") val facltInfo8:String,
    @SerializedName("FACLT_INFO9") val facltInfo9:String,
    @SerializedName("FACLT_INF10") val facltInfo10:String,
    @SerializedName("RSRV_SGGST1") val rsrvSggst1:String,
    @SerializedName("RSRV_SGGST2") val rsrvSggst2:String,
    @SerializedName("RSRV_SGGST3") val rsrvSggst3:String,
    @SerializedName("RSRV_SGGST4") val rsrvSggst4:String,
    @SerializedName("RSRV_SGGST5") val rsrvSggst5:String,
    @SerializedName("RSRV_SGGST6") val rsrvSggst6:String,
    @SerializedName("RSRV_SGGST7") val rsrvSggst7:String,
    @SerializedName("RSRV_SGGST8") val rsrvSggst8:String,
    @SerializedName("RSRV_SGGST9") val rsrvSggst9:String,
    @SerializedName("RSRV_SGGST10") val rsrvSggst10:String,
    @SerializedName("GUGUN") val gugun:String,
    @SerializedName("ROAD_ADRES2_CN") val roadAdres2Cn:String,
    @SerializedName("APPR_MTHD_NM") val apprMthdNm:String,
    @SerializedName("FILE_NM") val fileNm:String,
    @SerializedName("CAFE_TYPE_NM") val cafeTypeNm:String
)
cs


3-2. API Interface

레트로핏을 사용하기 위해서, API를 아래와 같이 구조를 잡았습니다.

application/json 형태이며, 해당 api를 다른 서울시 공공데이터 API에 사용하기 위해서 path를 촘촘히 나누었습니다.


1
2
3
4
5
6
7
8
9
10
11
interface SeoulAPI{
    @Headers("Content-Type: application/json")
    @GET("{apiKey}/{format}/{apiTitle}/{startIndex}/{endIndex}")
    fun getJobCafeList(
        @Path("apiKey") apiKey:String,
        @Path("format"format:String,
        @Path("apiTitle") apiTitle:String,
        @Path("startIndex") startIndex:String,
        @Path("endIndex") endIndex:String
    ) : Observable<WrappingJobCafeList>
}
cs


3-3. RetrofitProvider 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Singleton
object RetrofitProvider{
    private val loggingInterceptor = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BASIC
    }
    private val httpClient = OkHttpClient.Builder().addInterceptor(loggingInterceptor).build()
    val retrofit = Retrofit.Builder()
        .baseUrl("http://openapi.seoul.go.kr:8088/")
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .client(httpClient)
        .build()
 
    fun provideSeoulApi(): SeoulAPI = RetrofitProvider.retrofit.create(SeoulAPI::class.java)
}
cs


3-4. JobCafeRepository 및 JobCafeDataSource만들기

JobCafe의 리스트를 가져오는 Repository를 만들었고, JobCafe에서 사용되어지는 UseCase를 만들어서, Repository를 별도로 관리하고 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface JobCafeRepository{
    fun getJobCafeList(
        apiKey: String,
        formatString,
        appTitle: String,
        startIndex: String,
        endIndex: String
    ): Observable<WrappingJobCafeList>
}
 
class JobCafeRepositoryImpl(private val seoulAPI: SeoulAPI) : JobCafeRepository{
    override fun getJobCafeList(apiKey:Stringformat:String, appTitle:String, startIndex:String, endIndex:String): Observable<WrappingJobCafeList> {
        return seoulAPI.getJobCafeList(apiKey, format, appTitle, startIndex, endIndex)
    }
}
 
interface JobCafeDataSource {
    fun getJobCafeList() : Observable<WrappingJobCafeList>
}
 
class JobCafeDataSourceImpl(private val jobCafeRepository: JobCafeRepository) : JobCafeDataSource{
    override fun getJobCafeList() : Observable<WrappingJobCafeList> {
        return jobCafeRepository.getJobCafeList("input your api key")
    }
}
cs


4. 테스트 코드

4-1. 테스트 코드 작성

테스트 코드를 작성해서, 정상적으로 API가 호출이되는 지, 확인해주었습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ExampleUnitTest {
    private lateinit var jobCafeUseCase: JobCafeDataSource
    @Before
    fun setupTest() {
        jobCafeUseCase = JobCafeDataSourceImpl(
            JobCafeRepositoryImpl(
                RetrofitProvider.provideSeoulApi()
            )
        )
    }
 
    @Test
    fun getJobCafe() {
        jobCafeUseCase
            .getJobCafeList()
            .subscribe { wrappingJobCafeList ->
                println(wrappingJobCafeList.jobCafeList.jobCafes.toString())
            }
    }
}
cs


4-1. 결과 값 화인

5. 결과

 접근하려고 하는 API를 분석했고, 테스트 코드를 통해서 해당 API의 내용을 확인해보는 시간이었다. 페이징을 이용한 리사이클러뷰를 접근하기 전까지의 과정이었고, 다음 글에서는 페이징에 대해서 제대로 다뤄보도록 하겠다.