{"id":44684,"date":"2021-01-07T12:02:20","date_gmt":"2021-01-07T03:02:20","guid":{"rendered":"https:\/\/www.charlezz.com\/?p=44684"},"modified":"2021-01-07T12:02:20","modified_gmt":"2021-01-07T03:02:20","slug":"paging-3-%eb%9d%bc%ec%9d%b4%eb%b8%8c%eb%9f%ac%eb%a6%ac-%ec%99%84%eb%b2%bd-%ea%b0%80%ec%9d%b4%eb%93%9c-%ea%b3%b5%ec%8b%9d-%eb%ac%b8%ec%84%9c-%eb%b2%88%ec%97%ad","status":"publish","type":"post","link":"https:\/\/charlezz.com\/?p=44684","title":{"rendered":"Paging 3 \ub77c\uc774\ube0c\ub7ec\ub9ac \uc644\ubcbd \uac00\uc774\ub4dc (\uacf5\uc2dd \ubb38\uc11c \ubc88\uc5ed)"},"content":{"rendered":"<div id=\"translation\">\n<h1>\uac1c\uc694<\/h1>\n<div class=\"gtx-body\">\ud398\uc774\uc9d5 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\uba74 \ub85c\uceec \uc800\uc7a5\uc18c \ub610\ub294 \ub124\ud2b8\uc6cc\ud06c\ub97c \ud1b5\ud574 \ud070 \ub370\uc774\ud130\ub97c \uc798\uac8c \ucabc\uac1c\uc5b4 \ub85c\ub4dc\ud558\uace0 \ud45c\uc2dc \ud560 \uc218 \uc788\ub2e4. \uc774 \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uba74 \uc571\uc5d0\uc11c \ub124\ud2b8\uc6cc\ud06c \ub300\uc5ed\ud3ed\uacfc \uc2dc\uc2a4\ud15c \ub9ac\uc18c\uc2a4\ub97c \ub354 \ud6a8\uc728\uc801\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. Paging3 \ub77c\uc774\ube0c\ub7ec\ub9ac \ucef4\ud3ec\ub10c\ud2b8 Android \uc571 \uc544\ud0a4\ud14d\ucc98\uc5d0 \ub9de\uac8c \uc124\uacc4\ub418\uace0 \ub2e4\ub978 Jetpack \uad6c\uc131 \uc694\uc18c\uc640 \uae54\ub054\ud558\uac8c \ud1b5\ud569\ub418\uba70 Kotlin \uc6b0\uc120 \uc9c0\uc6d0\uc744 \uc81c\uacf5\ud55c\ub2e4.<\/div>\n<\/div>\n<div><\/div>\n<h2>\uc7a5\uc810<\/h2>\n<div id=\"translation\">\n<div class=\"gtx-body\">Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\uc5d0\ub294 \ub2e4\uc74c \uae30\ub2a5\uc774 \ud3ec\ud568\ub41c\ub2e4.<\/div>\n<ul>\n<li>\ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\uc5d0 \ub300\ud55c \uc778-\uba54\ubaa8\ub9ac \uce90\uc2f1\uc744 \uc9c0\uc6d0\ud55c\ub2e4. \uc571\uc774 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub85c \uc791\uc5c5\ud558\ub294 \ub3d9\uc548 \uc2dc\uc2a4\ud15c \ub9ac\uc18c\uc2a4\ub97c \ud6a8\uc728\uc801\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\uac8c \ub41c\ub2e4.<\/li>\n<li>\ub0b4\uc7a5\ub41c \uc694\uccad \uc911\ubcf5 \uc81c\uac70\ub97c \ud1b5\ud574 \uc571\uc774 \ub124\ud2b8\uc6cc\ud06c \ub300\uc5ed\ud3ed\uacfc \uc2dc\uc2a4\ud15c \ub9ac\uc18c\uc2a4\ub97c \ud6a8\uc728\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\ub3c4\ub85d \ud55c\ub2e4.<\/li>\n<li>\uc0ac\uc6a9\uc790\uac00 \ub85c\ub4dc \ud55c \ub370\uc774\ud130\uc758 \ub05d\uc73c\ub85c \uc2a4\ud06c\ub864 \ud560 \ub54c \ub370\uc774\ud130\ub97c \uc790\ub3d9\uc73c\ub85c \uc694\uccad\ud558\ub294 \uad6c\uc131\uc774 \uac00\ub2a5\ud55c RecyclerView \uc5b4\ub311\ud130\ub97c \uc81c\uacf5\ud55c\ub2e4.<\/li>\n<li>\ucf54\ud2c0\ub9b0 \ucf54\ub8e8\ud2f4\uacfc Flow\ub97c \uc6b0\uc120\uc801\uc73c\ub85c \uc9c0\uc6d0\ud558\uba70, LiveData \ubc0f RxJava\ub97c \uc9c0\uc6d0\ud55c\ub2e4.<\/li>\n<li>\uc0c8\ub85c \uace0\uce68 \ubc0f \uc7ac\uc2dc\ub3c4 \uae30\ub2a5\uc744 \ud3ec\ud568\ud558\uc5ec \uc624\ub958 \ucc98\ub9ac\ub97c \uc704\ud55c \uae30\ubcf8\uc801\uc778 \ubc29\ubc95\uc744 \uc81c\uacf5\ud55c\ub2e4.<\/li>\n<\/ul>\n<h2>\uc124\uc815\ud558\uae30<\/h2>\n<p>\ubaa8\ub4c8 \ub808\ubca8\uc758 build.gradle \uc5d0 \ub2e4\uc74c\uc758 \ub0b4\uc6a9\uc744 \ucd94\uac00\ud55c\ub2e4.<\/p>\n<p>Paging3\ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \uc544\uc9c1 alpha\ub2e8\uacc4\uc774\uba70 <a href=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/paging\/v3-overview#setup\">\ucd5c\uc2e0\ubc84\uc804<\/a>\uc744 \ud655\uc778\ud558\uc5ec \uc801\uc6a9\ud558\uc790.<\/p>\n<pre class=\"lang: decode:true \">dependencies {\r\n  def paging_version = \"3.0.0-alpha11\"\r\n\r\n  implementation \"androidx.paging:paging-runtime:$paging_version\"\r\n\r\n  \/\/ alternatively - without Android dependencies for tests\r\n  testImplementation \"androidx.paging:paging-common:$paging_version\"\r\n\r\n  \/\/ optional - RxJava2 support\r\n  implementation \"androidx.paging:paging-rxjava2:$paging_version\"\r\n\r\n  \/\/ optional - RxJava3 support\r\n  implementation \"androidx.paging:paging-rxjava3:$paging_version\"\r\n\r\n  \/\/ optional - Guava ListenableFuture support\r\n  implementation \"androidx.paging:paging-guava:$paging_version\"\r\n\r\n  \/\/ Jetpack Compose Integration\r\n  implementation \"androidx.paging:paging-compose:1.0.0-alpha04\"\r\n}<\/pre>\n<h2>\ub77c\uc774\ube0c\ub7ec\ub9ac \uad6c\uc870<\/h2>\n<p>Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 3\uac00\uc9c0 \uacc4\uce35\uc758 \ucef4\ud3ec\ub10c\ud2b8\ub85c \uad6c\uc131\ub41c\ub2e4.<\/p>\n<ul>\n<li>Repository \uacc4\uce35<\/li>\n<li>ViewModel \uacc4\uce35<\/li>\n<li>UI \uacc4\uce35<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-57.png\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-57.png\" alt=\"\" width=\"1660\" height=\"402\" class=\"aligncenter size-full wp-image-44685\" srcset=\"https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-57.png 1660w, https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-57-300x73.png 300w, https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-57-768x186.png 768w, https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-57-1024x248.png 1024w\" sizes=\"(max-width: 1660px) 100vw, 1660px\" \/><\/a><\/p>\n<h3>Repository \uacc4\uce35<\/h3>\n<p><span>Repository \uacc4\uce35\uc758 \uae30\ubcf8\uc774\ub418\ub294 \uad6c\uc131 \uc694\uc18c\ub294 PagingSource\ub2e4. \uac01 PagingSource \uac1c\uccb4\ub294 \ub370\uc774\ud130 \uc18c\uc2a4\uc640 \ud574\ub2f9 \uc18c\uc2a4\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uac80\uc0c9\ud558\ub294 \ubc29\ubc95\uc744 \uc815\uc758\ud55c\ub2e4. PagingSource \uac1c\uccb4\ub294 \ub124\ud2b8\uc6cc\ud06c \uc18c\uc2a4 \ubc0f \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \ud3ec\ud568\ud558\uc5ec \uc804\uccb4 \ub370\uc774\ud130\ub85c\ubd80\ud130 \ubd80\ubd84\uc801\uc73c\ub85c \ub370\uc774\ud130\ub97c \ub85c\ub4dc \ud560 \uc218 \uc788\ub2e4. <\/span><\/p>\n<p><span>\ub610 \ub2e4\ub978 \uad6c\uc131\uc694\uc18c\ub85c RemoteMediator\uac00 \uc788\ub2e4. RemoteMediator \uac1c\uccb4\ub294 \ub124\ud2b8\uc6cc\ud06c\ub85c \ubd80\ud130 \ubc1b\uc740 \ub370\uc774\ud130\ub97c \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \ud1b5\ud574 \uce90\uc2dc \ud558\ub294 \uacbd\uc6b0 \ud398\uc774\uc9d5\ud558\ub294\ub370 \ud568\uaed8 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.<\/span><\/p>\n<h3>ViewModel \uacc4\uce35<\/h3>\n<p><span>Pager\ub294 PagingSource \uac1c\uccb4 \ubc0f PagingConfig \uac1c\uccb4\ub97c \uae30\ubc18\uc73c\ub85c \ubc18\uc751\ud615 \uc2a4\ud2b8\ub9bc\uc5d0\uc11c \uc0ac\uc6a9\ub418\ub294 PagingData \uc778\uc2a4\ud134\uc2a4\ub97c \uad6c\uc131\ud558\uae30 \uc704\ud55c \uacf5\uc6a9 API\ub97c \uc81c\uacf5\ud55c\ub2e4. ViewModel \uacc4\uce35\uc744 UI\uc5d0 \uc5f0\uacb0\ud558\ub294 \uad6c\uc131 \uc694\uc18c\ub294 PagingData\ub2e4. PagingData \uac1c\uccb4\ub294 \ud398\uc774\uc9c0\uac00 \ub9e4\uaca8\uc9c4 \ub370\uc774\ud130\uc758 \uc2a4\ub0c5 \uc0f7\uc744 \uc704\ud55c \ucee8\ud14c\uc774\ub108\ub85c, PagingSource \uac1c\uccb4\ub97c \ucffc\ub9ac\ud558\uace0 \uacb0\uacfc\ub97c \uc800\uc7a5\ud55c\ub2e4.<\/span><\/p>\n<h3>UI\uacc4\uce35<\/h3>\n<p><span>UI \uacc4\uce35\uc758 \uae30\ubcf8 Paging \ub77c\uc774\ube0c\ub7ec\ub9ac \uad6c\uc131 \uc694\uc18c\ub294 PagingDataAdapter\ub85c \ud398\uc774\uc9c0\uac00 \ub9e4\uaca8\uc9c4 \ub370\uc774\ud130\ub97c \ucc98\ub9ac\ud55c\ub2e4. \ub9cc\uc57d PagingDataAdapter\uac00 \uc544\ub2cc RecyclerView.Adapter \ub4f1\uc744 \ud655\uc7a5\ud558\ub294 \ucee4\uc2a4\ud140 \uc5b4\ub311\ud130\ub97c \uad6c\ud604\ud558\ub824\uba74 AsyncPagingDataDiffer\ub97c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.<\/span><\/p>\n<h1>\ub370\uc774\ud130\ub97c \ubd88\ub7ec\uc624\uace0 \ud45c\uc2dc\ud558\uae30<\/h1>\n<div id=\"translation\">\n<div class=\"gtx-body\">\ud398\uc774\uc9d5 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\uace0 \ud45c\uc2dc\ud558\ub294 \uac15\ub825\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud55c\ub2e4. Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub124\ud2b8\uc6cc\ud06c\ub85c \ubd80\ud130 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \uc124\uc815\ud558\uace0 RecyclerView\uc5d0 \ud45c\uc2dc\ud558\ub294 \ubc29\ubc95\uc744 \uc54c\uc544\ubcf4\uc790.<\/div>\n<\/div>\n<\/div>\n<div><\/div>\n<h2>\ub370\uc774\ud130 \uc18c\uc2a4 \uc815\uc758\ud558\uae30<\/h2>\n<p><span>\uac00\uc7a5 \uba3c\uc800 \ud560 \uc77c\uc740 \ub370\uc774\ud130 \uc18c\uc2a4\ub97c \uc2dd\ubcc4\ud558\uae30 \uc704\ud574 PagingSource \uad6c\ud604\ud558\ub294 \uac83\uc774\ub2e4. \ud655\uc7a5\ud560 <\/span><span>PagingSource \ud074\ub798\uc2a4\uc5d0\uc11c\ub294 load() \uba54\uc18c\ub4dc\ub97c \uc7ac\uc815\uc758\ud558\uc5ec \ub370\uc774\ud130 \uc18c\uc2a4\ub85c\ubd80\ud130 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \uac00\uc838\uc628\ub2e4. <\/span><\/p>\n<p><span>\ube44\ub3d9\uae30 \ub85c\ub4dc\ub97c \uc704\ud55c Kotlin \ucf54\ub8e8\ud2f4\uc744 \uc0ac\uc6a9\ud558\uae30 \uc704\ud574\uc11c\ub294 PagingSource \ud074\ub798\uc2a4\ub97c \uc9c1\uc811\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uc790. Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \ub2e4\ub978 \ube44\ub3d9\uae30 \ud504\ub808\uc784\uc6cc\ud06c\ub97c \uc9c0\uc6d0\ud558\ub294 \ud074\ub798\uc2a4\ub3c4 \uc81c\uacf5\ud55c\ub2e4.<\/span><\/p>\n<ul>\n<li>RxJava\ub97c \uc0ac\uc6a9\ud558\uae30 \uc704\ud574\uc11c\ub294 RxPagingSource\ub97c \uc0ac\uc6a9\ud558\uc790.<\/li>\n<li>Guava\uc758 ListenableFuture\ub97c \uc0ac\uc6a9\ud558\uae30 \uc704\ud574\uc11c\ub294 ListenableFUtrePagingSource\ub97c \uc0ac\uc6a9\ud558\uc790.<\/li>\n<\/ul>\n<h3>Key\uc640 Value\uc758 \ud0c0\uc785 \uc124\uc815\ud558\uae30<\/h3>\n<p><span>PagingSource&lt;Key, Value&gt;\uc5d0\ub294 Key \ubc0f Value\uc758 \ub450 \uac00\uc9c0 \ub9e4\uac1c \ubcc0\uc218\uac00 \uc788\ub2e4. Key\ub294 \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\ub294\ub370 \uc0ac\uc6a9\ub418\ub294 \uc2dd\ubcc4\uc790\ub97c \uc815\uc758\ud558\uba70 Value\ub294 \ub85c\ub4dc\ud560 \ub370\uc774\ud130\uc758 \ud0c0\uc785\uc774 \ub41c\ub2e4. \uc608\ub97c \ub4e4\uc5b4, Int \ud0c0\uc785\uc758 \ud398\uc774\uc9c0 \ubc88\ud638\ub97c Retrofit\uc5d0 \uc804\ub2ec\ud558\uc5ec \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c User\uac1d\uccb4 \ud398\uc774\uc9d5\ud558\uc5ec \ub85c\ub4dc\ud558\ub294 \uacbd\uc6b0 Key \ud0c0\uc785\uc73c\ub85c Int\ub97c \uc120\ud0dd\ud558\uace0 Value \ud0c0\uc785\uc73c\ub85c User\ub97c \uc124\uc815\ud558\uba74 \ub41c\ub2e4.<\/span><\/p>\n<h3>PagingSource\u00a0 \uc815\uc758\ud558\uae30<\/h3>\n<p><span>\ub2e4\uc74c \uc608\uc81c\ub294 \ud398\uc774\uc9c0 \ubc88\ud638\ubcc4\ub85c \ud56d\ubaa9\ub4e4\uc744 \ud398\uc774\uc9d5\ud558\uc5ec \ub85c\ub4dc\ud558\ub294 PagingSource\ub97c \uad6c\ud604\ud558\uace0 \uc788\ub2e4. Key \ud0c0\uc785\uc740 Int\uc774\uace0 Value \ud0c0\uc785\uc740 User \uc774\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true\">class ExamplePagingSource(\r\n    val backend: ExampleBackendService,\r\n    val query: String\r\n) : PagingSource&lt;Int, User&gt;() {\r\n  override suspend fun load(\r\n    params: LoadParams&lt;Int&gt;\r\n  ): LoadResult&lt;Int, User&gt; {\r\n    try {\r\n      \/\/ \uc815\uc758 \ub418\uc9c0 \uc54a\uc558\ub2e4\uba74, page1\uc5d0\uc11c \ub9ac\ud504\ub808\uc2dc \ud55c\ub2e4.\r\n      val nextPageNumber = params.key ?: 1\r\n      val response = backend.searchUsers(query, nextPageNumber)\r\n      return LoadResult.Page(\r\n        data = response.users,\r\n        prevKey = null, \/\/ Only paging forward.\r\n        nextKey = response.nextPageNumber\r\n      )\r\n    } catch (e: Exception) {\r\n      \/\/ \ub124\ud2b8\uc6cc\ud06c \uc5d0\ub7ec \ub4f1\uacfc \uac19\uc740 \ub0b4\uc6a9\uc744 \ucc98\ub9ac\ud558\uace0 LoadResult.Error \ubc18\ud658\ud55c\ub2e4.\r\n    }\r\n  }\r\n}<\/pre>\n<p><span>\uc77c\ubc18\uc801\uc778 PagingSource \uad6c\ud604\uc740 \uc0dd\uc131\uc790\uc5d0 \uc81c\uacf5\ub41c \ub9e4\uac1c \ubcc0\uc218\ub97c load () \uba54\uc11c\ub4dc\uc5d0 \uc804\ub2ec\ud558\uc5ec \ucffc\ub9ac\uc5d0 \uc801\ud569\ud55c \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud55c\ub2e4. \uc704\uc758 \uc608\uc81c\uc5d0\uc11c \ub9e4\uac1c \ubcc0\uc218\uac00 \uc758\ubbf8 \ud558\ub294 \ub0b4\uc6a9\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/span><\/p>\n<ul>\n<li>backend: \ubc31\uc5d4\ub4dc \uc11c\ube44\uc2a4 \uc778\uc2a4\ud134\uc2a4\ub85c \ub370\uc774\ud130\ub97c \uc81c\uacf5\ud55c\ub2e4.<\/li>\n<li>query: backend \uc5d0 \uac80\uc0c9\ud560 \ub370\uc774\ud130\ub97c \uc9c8\uc758\ud558\uae30 \uc704\ud574 \uc0ac\uc6a9\ub41c\ub2e4.<\/li>\n<\/ul>\n<p><span>LoadParams \uac1d\uccb4\ub294 \ub85c\ub4dc \uc791\uc5c5\uc5d0 \ub300\ud55c \uc815\ubcf4\ub97c \ud3ec\ud568\ud55c\ub2e4. \uc5ec\uae30\uc5d0\ub294 \ub85c\ub4dc \ud560 Key\uc640 \ub85c\ub4dc \ud560 \ud56d\ubaa9\uc758 \uc0ac\uc774\uc988\uac00 \ud3ec\ud568\ub41c\ub2e4.\u00a0<\/span><\/p>\n<p><span>LoadResult \uac1c\uccb4\uc5d0\ub294 \ub85c\ub4dc \uc791\uc5c5\uc758 \uacb0\uacfc\uac00 \ud3ec\ud568\ub41c\ub2e4. LoadResult\ub294 load () \ud638\ucd9c\uc758 \uc131\uacf5 \uc5ec\ubd80\uc5d0 \ub530\ub77c \ub450 \uac00\uc9c0 \ud615\uc2dd \uc911 \ud558\ub098\ub97c \ucde8\ud558\ub294 sealed \ud074\ub798\uc2a4\ub2e4.<\/span><\/p>\n<ul>\n<li>\ub9cc\uc57d \ub85c\ub4dc\uc5d0 \uc131\uacf5\ud588\ub2e4\uba74 LoadResult.Page \uac1d\uccb4\ub97c \ubc18\ud658\ud55c\ub2e4.<\/li>\n<li>\ub9cc\uc57d \ub85c\ub4dc\uc5d0 \uc2e4\ud328\ud588\ub2e4\uba74 LoadResult.Error \uac1d\uccb4\ub97c \ubc18\ud658\ud55c\ub2e4.<\/li>\n<\/ul>\n<p>\ub2e4\uc74c \ub098\uc62c \uadf8\ub9bc\uc740 \uc608\uc81c \uc18d load() \ud568\uc218\uac00 \uc804\ub2ec \ubc1b\uc740 key\ub97c \ud1b5\ud574 \uc5b4\ub5bb\uac8c \ub370\uc774\ud130\ub97c \ubd88\ub7ec\uc624\ub294\uc9c0 \ubcf4\uc5ec\uc900\ub2e4.<a href=\"https:\/\/www.charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot.png\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/www.charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-780x1024.png\" alt=\"\" width=\"500\" height=\"656\" class=\"aligncenter wp-image-44686\" srcset=\"https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-780x1024.png 780w, https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-229x300.png 229w, https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot-768x1008.png 768w, https:\/\/charlezz.com\/wordpress\/wp-content\/uploads\/2021\/01\/www.charlezz.com-paging-3-screenshot.png 1042w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><\/a><\/p>\n<p>&nbsp;<\/p>\n<div id=\"translation\">\n<h3 id=\"translation\">\uc5d0\ub7ec \ucc98\ub9ac\ud558\uae30<\/h3>\n<p><span>\ub370\uc774\ud130 \ub85c\ub4dc \uc694\uccad\uc740 \ud2b9\ud788 \ub124\ud2b8\uc6cc\ud06c\ub97c \ud1b5\ud574\ub85c\ub4dc \ud560 \ub54c \uc5ec\ub7ec \uac00\uc9c0 \uc774\uc720\ub85c \uc2e4\ud328 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. load () \uba54\uc11c\ub4dc\uc5d0\uc11c LoadResult.Error \uac1d\uccb4\ub97c \ubc18\ud658\ud558\uc5ec \ub85c\ub4dc \uc911\uc5d0 \ubc1c\uc0dd\ud55c \uc624\ub958\ub97c \ubcf4\uace0\ud55c\ub2e4.<\/span><\/p>\n<p><span>\uc608\ub97c \ub4e4\uc5b4 load () \uba54\uc11c\ub4dc\uc5d0 \ub2e4\uc74c\uc744 \ucd94\uac00\ud558\uc5ec \uc774\uc804 \uc608\uc81c\uc758 ExamplePagingSource\uc5d0\uc11c \ub85c\ub4dc \uc624\ub958\ub97c \ud3ec\ucc29\ud558\uace0 \ubcf4\uace0 \ud560 \uc218 \uc788\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true \">catch (e: IOException) {\r\n  \/\/ IOException for network failures.\r\n  return LoadResult.Error(e)\r\n} catch (e: HttpException) {\r\n  \/\/ HttpException for any non-2xx HTTP status codes.\r\n  return LoadResult.Error(e)\r\n}<\/pre>\n<p><span>Retrofit \uc624\ub958 \ucc98\ub9ac\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 PagingSource API \uc0d8\ud50c\uc744 \ucc38\uc870\ud558\uc790. <\/span><\/p>\n<p><span>PagingSource\ub294 \uc0ac\uc6a9\uc790\uac00 \uc870\uce58\ub97c \ucde8\ud560 \uc218 \uc788\ub3c4\ub85d LoadResult.Error \uac1c\uccb4\ub97c \uc218\uc9d1\ud558\uc5ec UI\uc5d0 \uc804\ub2ec\ud55c\ub2e4. UI\uc5d0\uc11c \ub85c\ub4dc \uc0c1\ud0dc\ub97c \ub178\ucd9c\ud558\ub294 \ubc29\ubc95\uc5d0 \ub300\ud55c \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 \uc544\ub798\uc5d0 \ub098\uc62c &#8216;\ub85c\ub4dc \uc0c1\ud0dc \ud45c\uc2dc\ud558\uae30&#8217;\ub97c \ucc38\uc870\ud558\uc790.<\/span><\/p>\n<h2>\ud398\uc774\uc9d5 \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc \uc124\uc815\ud558\uae30<\/h2>\n<p><span>\ub2e4\uc74c\uc73c\ub85c \uad6c\ud604\ud574\uc57c \ud560 \ub0b4\uc6a9\uc740 PagingSource\ub85c\ubd80\ud130 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \uc124\uc815\ud558\ub294 \uac83\uc774\ub2e4. \uc77c\ubc18\uc801\uc73c\ub85c ViewModel\uc5d0\uc11c \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \uc124\uc815\ud55c\ub2e4. Pager \ud074\ub798\uc2a4\ub294 PagingSource\uc5d0\uc11c PagingData \uac1c\uccb4\uc758 \ubc18\uc751\ud615 \uc2a4\ud2b8\ub9bc\uc744 \uac00\uc838\uc624\ub294 \uba54\uc11c\ub4dc\ub97c \uc81c\uacf5\ud55c\ub2e4. Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 Flow, LiveData, RxJava\uc758 Flowable \ubc0f Observable \ud0c0\uc785\uc744 \ud3ec\ud568\ud55c \uc5ec\ub7ec \uc2a4\ud2b8\ub9bc \ud0c0\uc785 \uc0ac\uc6a9\uc744 \uc9c0\uc6d0\ud55c\ub2e4.<\/span><\/p>\n<p><span>\ubc18\uc751\ud615 \uc2a4\ud2b8\ub9bc\uc744 \uc124\uc815\ud558\uae30 \uc704\ud574 Pager \uc778\uc2a4\ud134\uc2a4\ub97c \ub9cc\ub4e4 \ub54c PagingConfig \uac1c\uccb4\uc640 PagingSource \uad6c\ud604\uc758 \uc778\uc2a4\ud134\uc2a4\ub97c \uac00\uc838 \uc624\ub294 \ubc29\ubc95\uc744 Pager\uc5d0 \uc54c\ub824\uc8fc\ub294 \ud568\uc218\ub97c \uc778\uc2a4\ud134\uc2a4\uc5d0 \uc81c\uacf5\ud574\uc57c\ud55c\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true\">val flow = Pager(\r\n  \/\/ \uc5b4\ub5bb\uac8c \ub370\uc774\ud130\ub97c \uac00\uc838\uc62c\uac74\uc9c0 \ucd94\uac00\uc801\uc778 \uc18d\uc131\uc744 \uc0ac\uc6a9\ud558\uc790.\r\n  PagingConfig(pageSize = 20)\r\n) {\r\n  ExamplePagingSource(backend, query)\r\n}.flow\r\n  .cachedIn(viewModelScope)<\/pre>\n<p><span>cachedIn () \uc5f0\uc0b0\uc790\ub294 \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \uacf5\uc720 \uac00\ub2a5\ud558\uac8c \ub9cc\ub4e4\uace0 \uc81c\uacf5\ub41c CoroutineScope\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub85c\ub4dc \ub41c \ub370\uc774\ud130\ub97c \uce90\uc2dc\ud55c\ub2e4. \uc774 \uc608\uc81c\ub294 Lifecycle lifecycle-viewmodel-ktx \uc544\ud2f0\ud329\ud2b8\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 viewModelScope\ub97c \uc0ac\uc6a9\ud55c\ub2e4<\/span><\/p>\n<p><span>Pager \uac1d\uccb4\ub294 PagingSource \uac1d\uccb4\uc5d0\uc11c load () \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud558\uc5ec LoadParams \uac1d\uccb4\ub97c \uc81c\uacf5\ud558\uace0 LoadResult \uac1d\uccb4\ub97c \ubc18\ud658\ubc1b\ub294\ub2e4.<\/span><\/p>\n<h2>RecyclerView \uc5b4\ub311\ud130 \uc815\uc758\ud558\uae30<\/h2>\n<p><span>RecyclerView\ub97c \ud1b5\ud574 \ub370\uc774\ud130\ub97c \ubcf4\uc5ec\uc8fc\uae30 \uc704\ud574\uc11c\ub294 \uc5b4\ub311\ud130\ub97c \uc124\uc815\ud574\uc57c \ud55c\ub2e4. Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \uc774\ub97c \uc704\ud574 PagingDataAdapter \ud074\ub798\uc2a4\ub97c \uc81c\uacf5\ud55c\ub2e4.<\/span><\/p>\n<p><span>\uc77c\ubc18\uc801\uc73c\ub85c PagingDataAdapter\ub97c \ud655\uc7a5\ud558\ub294 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4\uba74 \ub41c\ub2e4. \uc774 \uc608\uc81c\uc5d0\uc11c\ub294 PagingDataAdapter\ub97c \ud655\uc7a5\ud558\uc5ec UserAdapter\ub77c\ub294 \uc5b4\ub311\ud130\ub97c \ub9cc\ub4e4\uc5b4 User \ud0c0\uc785\uc758 \ubaa9\ub85d\uc5d0 \ub300\ud55c RecyclerView \uc5b4\ub311\ud130\ub97c \uc81c\uacf5\ud558\uace0 UserViewHolder\ub97c \ubdf0\ud640\ub354\ub85c \uc0ac\uc6a9\ud558\uace0 \uc788\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true\">class UserAdapter(diffCallback: DiffUtil.ItemCallback&lt;User&gt;) :\r\n  PagingDataAdapter&lt;User, UserViewHolder&gt;(diffCallback) {\r\n  override fun onCreateViewHolder(\r\n    parent: ViewGroup,\r\n    viewType: Int\r\n  ): UserViewHolder {\r\n    return UserViewHolder(parent)\r\n  }\r\n\r\n  override fun onBindViewHolder(holder: UserViewHolder, position: Int) {\r\n    val item = getItem(position)\r\n    \/\/ item\uc774 null\uc77c \uc218 \uc788\uc73c\ub2c8 \uc774\ub97c \uc801\uc808\ud788 \ucc98\ub9ac\ud558\uc790\r\n    holder.bind(item)\r\n  }\r\n}<\/pre>\n<p><span>\uc77c\ubc18\uc801\uc73c\ub85c \uc5b4\ub311\ud130\uc5d0\uc11c onCreateViewHolder () \ubc0f onBindViewHolder () \uba54\uc11c\ub4dc\ub97c \uc7ac\uc815\uc758\ud558\uac8c \ub418\ub294\ub370 \ucd94\uac00\uc801\uc73c\ub85c DiffUtil.ItemCallback\uc744 \uc9c0\uc815\ud574\uc57c \ud55c\ub2e4. \uc774\uac83\uc740 RecyclerView \ubaa9\ub85d \uc5b4\ub311\ud130\ub97c \uc815\uc758 \ud560 \ub54c \uc77c\ubc18\uc801\uc73c\ub85c \uc218\ud589\ud558\ub294 \uac83\uacfc \ub3d9\uc77c\ud558\uac8c \uc791\ub3d9\ud55c\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true \">object UserComparator : DiffUtil.ItemCallback&lt;User&gt;() {\r\n  override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {\r\n    \/\/ Id is unique.\r\n    return oldItem.id == newItem.id\r\n  }\r\n\r\n  override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {\r\n    return oldItem == newItem\r\n  }\r\n}<\/pre>\n<h2>UI\uc5d0\uc11c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130 \ud45c\uc2dc\ud558\uae30<\/h2>\n<p><span>\uc774\uc81c PagingSource\ub97c \uc815\uc758\ud558\uace0 \uc571\uc5d0\uc11c PagingData \uc2a4\ud2b8\ub9bc\uc744 \uc0dd\uc131\ud558\ub294 \ubc29\ubc95\uc744 \ub9cc\ub4e4\uace0 PagingDataAdapter\ub97c \uc815\uc758 \ud588\uc73c\ubbc0\ub85c \uc774\ub7ec\ud55c \uc694\uc18c\ub97c \ud568\uaed8 \uc5f0\uacb0\ud558\uace0 Activity\uc5d0 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \ud45c\uc2dc \ud560 \uc900\ube44\uac00 \ub418\uc5c8\ub2e4.<\/span><\/p>\n<p>Activity\uc758 onCreate \uba54\uc11c\ub4dc \ub610\ub294 Fragment\uc758 onViewCreated\uba54\uc11c\ub4dc\uc5d0\uc11c \ub2e4\uc74c \ub2e8\uacc4\ub97c \uc218\ud589\ud558\uc790.<\/p>\n<ol>\n<li><span>PagingDataAdapter \ud074\ub798\uc2a4\uc758 \uc778\uc2a4\ud134\uc2a4\ub97c \ub9cc\ub4e0\ub2e4.<\/span><\/li>\n<li><span>\ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \ud45c\uc2dc\ud558\ub824\ub294 RecyclerView \ubaa9\ub85d\uc5d0 PagingDataAdapter \uc778\uc2a4\ud134\uc2a4\ub97c \uc804\ub2ec\ud55c\ub2e4.<\/span><\/li>\n<li><span>PagingData \uc2a4\ud2b8\ub9bc\uc744 \uad00\ucc30\ud558\uace0 \uc0dd\uc131 \ub41c \uac01 \uac12\uc744 \uc5b4\ub311\ud130\uc758 submitData () \uba54\uc11c\ub4dc\uc5d0 \uc804\ub2ec\ud55c\ub2e4.<\/span><\/li>\n<\/ol>\n<pre class=\"lang: decode:true\">val viewModel by viewModels&lt;ExampleViewModel&gt;()\r\n\r\nval pagingAdapter = UserAdapter(UserComparator)\r\nval recyclerView = findViewById&lt;RecyclerView&gt;(R.id.recycler_view)\r\nrecyclerView.adapter = pagingAdapter\r\n\r\n\/\/ Activity\uc5d0\uc11c\ub294 lifecycleScope\ub97c \uc9c1\uc811\uc801\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc9c0\ub9cc \r\n\/\/ Fragment\ub294 viewLifecycleOwner.lifecycleScope\ub97c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4.\r\nlifecycleScope.launch {\r\n  viewModel.flow.collectLatest { pagingData -&gt;\r\n    pagingAdapter.submitData(pagingData)\r\n  }\r\n}<\/pre>\n<p><span>RecyclerView \ubaa9\ub85d\uc740 \uc774\uc81c \ub370\uc774\ud130 \uc18c\uc2a4\uc5d0\uc11c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \ud45c\uc2dc\ud558\uace0 \ud544\uc694\ud55c \uacbd\uc6b0 \ub2e4\ub978 \ud398\uc774\uc9c0\ub97c \uc790\ub3d9\uc73c\ub85c \ub85c\ub4dc\ud558\uac8c \ub41c\ub2e4.<\/span><\/p>\n<h2>\ub85c\ub4dc \uc0c1\ud0dc \ud45c\uc2dc\ud558\uae30<\/h2>\n<p><span>Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 LoadState \uac1c\uccb4\ub97c \ud1b5\ud574 UI\uc5d0\uc11c \uc0ac\uc6a9\ud560 \ub85c\ub4dc \uc0c1\ud0dc\ub97c \ub178\ucd9c\ud55c\ub2e4. LoadState\ub294 \ud604\uc7ac\ub85c\ub4dc \uc0c1\ud0dc\uc5d0 \ub530\ub77c \uc138 \uac00\uc9c0 \ud615\uc2dd \uc911 \ud558\ub098\ub97c \uc0ac\uc6a9\ud55c\ub2e4.<\/span><\/p>\n<ul>\n<li>\ub370\uc774\ud130\ub97c \ubd88\ub7ec\uc624\ub294 \uc0c1\ud0dc\uac00 \uc544\ub2c8\uace0 \uc5d0\ub7ec\uac00 \uc5c6\ub2e4\uba74 LoadState\ub294 LoadState.NotLoading\uac00 \ub41c\ub2e4.<\/li>\n<li>\ub370\uc774\ud130\ub97c \ubd88\ub7ec\uc624\ub294 \uc0c1\ud0dc\uba74 LoadState\ub294 LoadState.Loading\uc774 \ub41c\ub2e4.<\/li>\n<li>\uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud558\uba74 LoadState\ub294 LoadState.Error\uac00 \ub41c\ub2e4.<\/li>\n<\/ul>\n<\/div>\n<p><span>UI\uc5d0\uc11c LoadState\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uc5d0\ub294 \ub450 \uac00\uc9c0\uac00 \uc788\ub2e4. \ub9ac\uc2a4\ub108\ub97c \uc0ac\uc6a9\ud558\ub294 \ubc29\ubc95\uacfc \uc774\ub97c \ud45c\uc2dc\ud558\uae30 \uc704\ud55c \ud2b9\ubcc4\ud55c \uc5b4\ub311\ud130\ub97c \uc0ac\uc6a9\ud558\uc5ec RecyclerView \ubaa9\ub85d\uc5d0 \uc9c1\uc811 \ub85c\ub4dc \uc0c1\ud0dc\ub97c \ud45c\uc2dc\ud558\ub294 \uac83\uc774\ub2e4.<\/span><\/p>\n<h3>\ub85c\ub529 \uc0c1\ud0dc\ub97c \uac00\uc838\uc624\uae30 \uc704\ud574 \ub9ac\uc2a4\ub108 \uc0ac\uc6a9\ud558\uae30\u00a0<\/h3>\n<p>\uc77c\ubc18\uc801\uc778 \uc0c1\ud669\uc5d0\uc11c UI\uc5d0 \ub85c\ub529 \uc0c1\ud0dc\ub97c \uac00\uc838\uc624\ub294 \ubc29\ubc95\uc740 PagingDataAdapter\uac00 \ud3ec\ud568\ud55c addLoadStateListener() \uba54\uc11c\ub4dc\ub97c \uc0ac\uc6a9\ud558\ub294 \uac83\uc774\ub2e4.<\/p>\n<pre class=\"lang: decode:true\">\/\/ Activity\uc5d0\uc11c\ub294 lifecycleScope\ub97c \uc9c1\uc811\uc801\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\uc9c0\ub9cc \r\n\/\/ Fragment\ub294 viewLifecycleOwner.lifecycleScope\ub97c \uc0ac\uc6a9\ud574\uc57c \ud55c\ub2e4.\r\nlifecycleScope.launch {\r\n  pagingAdapter.loadStateFlow.collectLatest { loadStates -&gt;\r\n    progressBar.isVisible = loadStates.refresh is LoadState.Loading\r\n    retry.isVisible = loadState.refresh !is LoadState.Loading\r\n    errorMsg.isVisible = loadState.refresh is LoadState.Error\r\n  }\r\n}\r\n<\/pre>\n<h3>\ub85c\ub529 \uc0c1\ud0dc\ub97c \ub098\ud0c0\ub0b4\uae30 \uc704\ud574 \uc5b4\ub311\ud130\ub97c \uc0ac\uc6a9\ud558\uae30<\/h3>\n<p><span>\ud398\uc774\uc9d5 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \ud45c\uc2dc\ub41c \ud398\uc774\uc9d5 \ub370\uc774\ud130 \ubaa9\ub85d\uc5d0 \uc9c1\uc811\ub85c\ub4dc \uc0c1\ud0dc\ub97c \ud45c\uc2dc\ud558\uae30 \uc704\ud574 LoadStateAdapter\ub77c\ub294 \ub610 \ub2e4\ub978 \uc5b4\ub311\ud130\ub97c \uc81c\uacf5\ud55c\ub2e4. \uba3c\uc800 LoadStateAdapter\ub97c \uad6c\ud604\ud558\ub294 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4\uace0 onCreateViewHolder () \ubc0f onBindViewHolder () \uba54\uc11c\ub4dc\ub97c \uc7ac\uc815\uc758\ud558\uc790.<\/span><\/p>\n<pre class=\"lang: decode:true\">class LoadStateViewHolder(\r\n  parent: ViewGroup,\r\n  retry: () -&gt; Unit\r\n) : RecyclerView.ViewHolder(\r\n  LayoutInflater.from(parent.context)\r\n    .inflate(R.layout.load_state_item, parent, false)\r\n) {\r\n  private val binding = LoadStateItemBinding.bind(itemView)\r\n  private val progressBar: ProgressBar = binding.progressBar\r\n  private val errorMsg: TextView = binding.errorMsg\r\n  private val retry: Button = binding.retryButton\r\n    .also {\r\n      it.setOnClickListener { retry() }\r\n    }\r\n\r\n  fun bind(loadState: LoadState) {\r\n    if (loadState is LoadState.Error) {\r\n      errorMsg.text = loadState.error.localizedMessage\r\n    }\r\n\r\n    progressBar.isVisible = loadState is LoadState.Loading\r\n    retry.isVisible = loadState is LoadState.Error\r\n    errorMsg.isVisible = loadState is LoadState.Error\r\n  }\r\n}\r\n\r\n\/\/ \ub85c\ub529 \uc0c1\ud0dc\uac00 LoadState.Loading\uc77c \ub54c \ub85c\ub529 \uc2a4\ud53c\ub108\ub97c \ub098\ud0c0\ub0b8\ub2e4.\r\n\/\/ \uadf8\ub9ac\uace0 LoadState.Error\uc77c \ub54c\ub294 \uc5d0\ub7ec \uba54\uc2dc\uc9c0 \ubc0f \uc7ac\uc2dc\ub3c4 \ubc84\ud2bc\uc744 \ub098\ud0c0\ub0b8\ub2e4.\r\nclass ExampleLoadStateAdapter(\r\n  private val retry: () -&gt; Unit\r\n) : LoadStateAdapter&lt;LoadStateViewHolder&gt;() {\r\n\r\n  override fun onCreateViewHolder(\r\n    parent: ViewGroup,\r\n    loadState: LoadState\r\n  ) = LoadStateViewHolder(parent, retry)\r\n\r\n  override fun onBindViewHolder(\r\n    holder: LoadStateViewHolder,\r\n    loadState: LoadState\r\n  ) = holder.bind(loadState)\r\n}<\/pre>\n<p><span>\uadf8\ub7f0 \ub2e4\uc74c PagingDataAdapter \uac1c\uccb4\uc5d0\uc11c withLoadStateHeaderAndFooter () \uba54\uc11c\ub4dc\ub97c \ud638\ucd9c\ud55c\ub2e4.<\/span><\/p>\n<h1>\ub124\ud2b8\uc6cc\ud06c \ubc0f \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub85c\ubd80\ud130 \ud398\uc774\uc9d5\ud558\uae30<\/h1>\n<div id=\"translation\">\n<div class=\"gtx-body\">\ub124\ud2b8\uc6cc\ud06c \uc5f0\uacb0\uc774 \ubd88\uc548\uc815\ud558\uac70\ub098 \uc0ac\uc6a9\uc790\uac00 \uc624\ud504\ub77c\uc778 \uc77c \ub54c \uc571\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub3c4\ub85d \ud55c\ub2e4. \uc774\ub97c \uac00\ub2a5\ud558\uac8c \ud558\ub294 \ubc29\ubc95\uc740 \ub124\ud2b8\uc6cc\ud06c\uc640 \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \ub3d9\uc2dc\uc5d0 \ud398\uc774\uc9d5\ud558\ub294 \uac83\uc774\ub2e4. \uc774\ub807\uac8c \ud558\uba74 \uc571\uc774 \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uce90\uc2dc\uc5d0\uc11c \uc9c1\uc811 \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\uace0 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ub354 \uc774\uc0c1 \ub370\uc774\ud130\uac00 \uc5c6\uc744 \ub54c\ub9cc \ub124\ud2b8\uc6cc\ud06c\uc5d0 \uc694\uccad\ud55c\ub2e4.<\/div>\n<\/div>\n<div><\/div>\n<div id=\"translation\"><span>Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \uc774 \uc720\uc988\ucf00\uc774\uc2a4\ub97c \uc704\ud574 RemoteMediator \ucef4\ud3ec\ub10c\ud2b8\ub97c \uc81c\uacf5\ud558\uace0 \uc788\ub2e4. RemoteMediator\ub294 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub85c \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\ub294 \ud504\ub85c\uc138\uc2a4\ub97c \uad00\ub9ac\ud55c\ub2e4.<\/span><\/div>\n<div><\/div>\n<h2>\uae30\ubcf8\uc801\uc778 \uc0ac\uc6a9\ubc95<\/h2>\n<p><span>\uc571\uc774 User \uc544\uc774\ud15c\uc744 \ub370\uc774\ud130 \uc18c\uc2a4\ub85c\ubd80\ud130 \uc544\uc774\ud15c \ud0a4\ub97c \uae30\ubc18\uc73c\ub85c \ub85c\ub4dc\ud558\uc5ec Room \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud558\uace0 \ub85c\uceec \uce90\uc2dc\ub85c \uc774\ub97c \ub85c\ub4dc\ud558\ub3c4\ub85d \ud558\ub824\uace0 \ud55c\ub2e4. \ub2e4\uc74c \uadf8\ub9bc\uc740 RemoteMediator\uc640 PagingSource\uac00 \uc774 \uc720\uc988\ucf00\uc774\uc2a4\ub97c \ucda9\uc871\ud558\uae30 \uc704\ud574 \ud568\uaed8 \uc791\ub3d9\ud558\ub294 \ubc29\ubc95\uc744 \ubcf4\uc5ec\uc8fc\uace0 \uc788\ub2e4.<\/span><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/images\/paging3-layered-architecture.svg\" alt=\"The RemoteMediator loads data from the network into the database and\n    the PagingSource loads data from the database. A Pager uses both the\n    RemoteMediator and the PagingSource to load paged data.\" width=\"800\" \/><\/p>\n<div id=\"translation\">\n<p><span>RemoteMediator \uad6c\ud604\uc740 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub85c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\ub294 \uac83\uc744 \uad00\ub9ac\ud558\uc9c0\ub9cc \ub370\uc774\ud130\ub97c UI\ub85c \uc9c1\uc811\ub85c\ub4dc \ud558\uc9c0\ub294 \uc54a\ub294\ub2e4. \ub300\uc2e0 \uc571\uc740 \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \ub370\uc774\ud130 \uc18c\uc2a4\ub85c \uc0ac\uc6a9\ud55c\ub2e4. \uc989, \uc571\uc740 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uce90\uc2dc \ub41c \ub370\uc774\ud130\ub85c\ub9cc \ub098\ud0c0\ub0b8\ub2e4. PagingSource \uad6c\ud604 (\uc608 : Room\uc5d0\uc11c \uc0dd\uc131 \ub41c \uad6c\ud604)\uc740 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c UI\ub85c \uce90\uc2dc \ub41c \ub370\uc774\ud130\ub85c\ub4dc\ub97c \ucc98\ub9ac\ud55c\ub2e4.<\/span><\/p>\n<h3>Room \uc5d4\ud130\ud2f0 \uc0dd\uc131\ud558\uae30<\/h3>\n<p><span>\uccab \ubc88\uc9f8 \ub2e8\uacc4\ub294 Room \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub124\ud2b8\uc6cc\ud06c \ub370\uc774\ud130 \uc18c\uc2a4\uc5d0\uc11c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\uc758 \ub85c\uceec \uce90\uc2dc\ub97c \ubcf4\uc720\ud558\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \uc815\uc758\ud558\ub294 \uac83\uc774\ub2e4. <\/span><\/p>\n<p><span>\uadf8\ub7f0 \ub2e4\uc74c Room \uc5d4\ud130\ud2f0\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub370\uc774\ud130 \uc815\uc758\uc5d0 \uc124\uba85 \ub41c\ub300\ub85c \ubaa9\ub85d \ud56d\ubaa9 \ud14c\uc774\ube14\uc744 \ub098\ud0c0\ub0b4\ub294 Room \uc5d4\ud130\ud2f0\ub97c \uc815\uc758\ud55c\ub2e4. id \ud544\ub4dc\ub97c \uae30\ubcf8\ud0a4\ub85c \uc81c\uacf5\ud558\uace0 \ubaa9\ub85d \ud56d\ubaa9\uc5d0 \ud3ec\ud568 \ub41c \uae30\ud0c0 \uc815\ubcf4\uc5d0 \ub300\ud55c \ud544\ub4dc\ub97c \uc81c\uacf5\ud55c\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true \">@Entity(tableName = \"users\")\r\ndata class User(val id: String, val label: String)<\/pre>\n<p><span>\ub610\ud55c Room DAO\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub370\uc774\ud130 \uc561\uc138\uc2a4\uc5d0 \uc124\uba85 \ub41c\ub300\ub85c\uc774 Room \uc5d4\ud130\ud2f0\uc5d0 \ub300\ud55c \ub370\uc774\ud130 \uc561\uc138\uc2a4 \uac1c\uccb4 (DAO)\ub97c \uc815\uc758\ud574\uc57c\ud569\ub2c8\ub2e4. \ubaa9\ub85d \ud56d\ubaa9 \uc5d4\ud130\ud2f0\uc5d0 \ub300\ud55c DAO\uc5d0\ub294 \ub2e4\uc74c \uba54\uc11c\ub4dc\uac00 \ud3ec\ud568\ub418\uc5b4\uc57c \ud55c\ub2e4.<\/span><\/p>\n<ul>\n<li><span>insertAll ()\uc740 \uc544\uc774\ud15c \ubaa9\ub85d\uc744 \ud14c\uc774\ube14\uc5d0 \uc0bd\uc785\ud558\ub294 \uba54\uc11c\ub4dc\ub2e4<\/span><\/li>\n<li><span>pagingSource(query)\ub294 \ucffc\ub9ac \ubb38\uc790\uc5f4\uc744 \ub9e4\uac1c \ubcc0\uc218\ub85c \uc0ac\uc6a9\ud558\uc5ec \uacb0\uacfc \ubaa9\ub85d\uc5d0 \ub300\ud55c PagingSource \uac1c\uccb4\ub97c \ubc18\ud658\ud558\ub294 \uba54\uc11c\ub4dc\ub2e4. \uc774\ub7f0 \uc2dd\uc73c\ub85c Pager \uac1c\uccb4\ub294 \uc774 \ud14c\uc774\ube14\uc744 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\uc758 \uc18c\uc2a4\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4.<\/span><\/li>\n<li><span>clearAll () \uba54\uc11c\ub4dc\ub294 \ud14c\uc774\ube14\uc758 \ubaa8\ub4e0 \ub370\uc774\ud130\ub97c \uc0ad\uc81c\ud55c\ub2e4.<\/span><\/li>\n<\/ul>\n<pre class=\"lang: decode:true \">@Dao\r\ninterface UserDao {\r\n  @Insert(onConflict = OnConflictStrategy.REPLACE)\r\n  suspend fun insertAll(users: List&lt;User&gt;)\r\n\r\n  @Query(\"SELECT * FROM users WHERE label LIKE :query\")\r\n  fun pagingSource(query: String): PagingSource&lt;Int, User&gt;\r\n\r\n  @Query(\"DELETE FROM users\")\r\n  suspend fun clearAll()\r\n}<\/pre>\n<h3>RemoteMediator \uad6c\ud604\ud558\uae30<\/h3>\n<p><span>RemoteMediator\ub294 PagingSource\uc640 \uc720\uc0ac\ud558\ub2e4. \uc5ec\uae30\uc5d0\ub294 \ub85c\ub4dc \ub3d9\uc791\uc744 \uc815\uc758\ud558\uae30 \uc704\ud574 \uc7ac\uc815\uc758\ud574\uc57c\ud558\ub294 load() \uba54\uc11c\ub4dc\uac00 \ud3ec\ud568\ub418\uc5b4 \uc788\ub2e4. \ucc28\uc774\uc810\uc740 \ub370\uc774\ud130 \uc18c\uc2a4\uc5d0\uc11c RecyclerView \ubaa9\ub85d\uc73c\ub85c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c\ub85c\ub4dc\ud558\ub294 \ub300\uc2e0 RemoteMediator \uac1c\uccb4\ub294 \ub124\ud2b8\uc6cc\ud06c \ub370\uc774\ud130 \uc18c\uc2a4\uc5d0\uc11c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\uc5ec \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud55c\ub2e4\ub294 \uac83\uc774\ub2e4.<\/span><\/p>\n<p>\uc804\ud615\uc801\uc778 RemoteMediator \uad6c\ud604\uc740 \ub2e4\uc74c\uacfc \uac19\uc740 \ub9e4\uac1c\ubcc0\uc218\ub97c \ud3ec\ud568\ud55c\ub2e4.<\/p>\n<ul>\n<li>query: <span>\ubc31\uc5d4\ub4dc \uc11c\ube44\uc2a4\uc5d0\uc11c \uac80\uc0c9 \ud560 \ub370\uc774\ud130\ub97c \uc815\uc758\ud558\ub294 \ucffc\ub9ac \ubb38\uc790\uc5f4<\/span><\/li>\n<li>database: <span>\ub85c\uceec \uce90\uc2dc \uc5ed\ud560\uc744 \ud558\ub294 Room \ub370\uc774\ud130\ubca0\uc774\uc2a4<\/span><\/li>\n<li>networkService: \ubc31\uc5d4\ub4dc \uc11c\ube44\uc2a4 \uc6a9 API \uc778\uc2a4\ud134\uc2a4(\uc77c\ubc18\uc801\uc73c\ub85c Retrofit \uc0ac\uc6a9)\n<\/li>\n<\/ul>\n<p><span>RemoteMediator &lt;Key, Value&gt; \uad6c\ud604\uc744 \ub9cc\ub4e4\uace0, Key \ud0c0\uc785 \ubc0f Value \ud0c0\uc785\uc740 \ub3d9\uc77c\ud55c \ub124\ud2b8\uc6cc\ud06c \ub370\uc774\ud130 \uc18c\uc2a4\uc5d0 \ub300\ud574 PagingSource\ub97c \uc815\uc758\ud558\ub294 \uacbd\uc6b0\uc640 \ub3d9\uc77c\ud574\uc57c\ud55c\ub2e4.\u00a0<\/span><\/p>\n<pre class=\"lang: decode:true \">@OptIn(ExperimentalPagingApi::class)\r\nclass ExampleRemoteMediator(\r\n  private val query: String,\r\n  private val database: RoomDb,\r\n  private val networkService: ExampleBackendService\r\n) : RemoteMediator&lt;Int, User&gt;() {\r\n  val userDao = database.userDao()\r\n\r\n  override suspend fun load(\r\n    loadType: LoadType,\r\n    state: PagingState&lt;Int, User&gt;\r\n  ): MediatorResult {\r\n    \/\/ ...\r\n  }\r\n}<\/pre>\n<p><span>load () \uba54\uc11c\ub4dc\ub294 \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub97c \uc5c5\ub370\uc774\ud2b8\ud558\uace0 PagingSource\ub97c \ubb34\ud6a8\ud654\ud55c\ub2e4. \ud398\uc774\uc9d5\uc744 \uc9c0\uc6d0\ud558\ub294 \uc77c\ubd80 \ub77c\uc774\ube0c\ub7ec\ub9ac (\uc608 : Room)\ub294 \uad6c\ud604\ud558\ub294 PagingSource \uac1c\uccb4\ub97c \ubb34\ud6a8\ud654\ud558\ub294 \ucc98\ub9ac\ub97c \uc790\ub3d9\uc73c\ub85c \ud55c\ub2e4.<\/span><\/p>\n<p>load() \uba54\uc11c\ub4dc\ub294 \ub450\uac00\uc9c0 \ub9e4\uac1c\ubcc0\uc218\ub97c \ucde8\ud55c\ub2e4.<\/p>\n<ul>\n<li>PagingState : <span>\uc9c0\uae08\uae4c\uc9c0 \ub85c\ub4dc \ub41c \ud398\uc774\uc9c0, \uac00\uc7a5 \ucd5c\uadfc\uc5d0 \uc561\uc138\uc2a4 \ud55c \uc778\ub371\uc2a4 \ubc0f \ud398\uc774\uc9d5 \uc2a4\ud2b8\ub9bc\uc744 \ucd08\uae30\ud654\ud558\ub294 \ub370 \uc0ac\uc6a9\ud55c PagingConfig \uac1c\uccb4\uc5d0 \ub300\ud55c \uc815\ubcf4\uac00 \ud3ec\ud568 \ub41c\ub2e4.<\/span><\/li>\n<li>LoadType : \ub85c\ub4dc \uc720\ud615\uc744 \ub098\ud0c0\ub0b8\ub2e4,(REFRESH, APPEND, PREPEND)<\/li>\n<\/ul>\n<p><span>load () \uba54\uc11c\ub4dc\uc758 \ubc18\ud658 \uac12\uc740 MediatorResult \uac1d\uccb4\ub2e4. MediatorResult\ub294 MediatorResult.Error\u00a0 (\uc624\ub958 \uc124\uba85 \ud3ec\ud568) \ub610\ub294 MediatorResult.Success (\ub85c\ub4dc \ud560 \ub370\uc774\ud130\uac00 \ub354 \uc788\ub294\uc9c0 \uc5ec\ubd80\ub97c \ub098\ud0c0\ub0b4\ub294 \uc2e0\ud638 \ud3ec\ud568) \uac00 \ub420 \uc218 \uc788\ub2e4.<\/span><\/p>\n<p>load() \uba54\uc11c\ub4dc\ub294 \ubc18\ub4dc\uc2dc \ub2e4\uc74c \ub0b4\uc6a9\uc744 \uc218\ud589\ud574\uc57c\ud55c\ub2e4.<\/p>\n<ol>\n<li><span>\ub85c\ub4dc \uc720\ud615 \ubc0f \uc9c0\uae08\uae4c\uc9c0 \ub85c\ub4dc \ub41c \ub370\uc774\ud130\uc5d0 \ub530\ub77c \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub85c\ub4dc \ud560 \ud398\uc774\uc9c0 \uacb0\uc815<\/span><\/li>\n<li>\ub124\ud2b8\uc6cc\ud06c \uc694\uccad \ud2b8\ub9ac\uac70<\/li>\n<li>\n<div class=\"gtx-body\">\ub85c\ub4dc \uc791\uc5c5\uc758 \uacb0\uacfc\uc5d0 \ub530\ub77c \uc791\uc5c5 \uc218\ud589<\/div>\n<p>&#8211; <span>\ub85c\ub4dc\uc5d0 \uc131\uacf5\ud558\uace0 \ubc1b\uc740 \uc544\uc774\ud15c \ub9ac\uc2a4\ud2b8\uac00 \ube44\uc5b4 \uc788\uc9c0 \uc54a\uc73c\uba74 \ub9ac\uc2a4\ud2b8\ub97c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud558\uace0 MediatorResult.Success (endOfPaginationReached = false)\ub97c \ubc18\ud658\ud55c\ub2e4.<br \/>\n&#8211; \ub85c\ub4dc\uac00 \uc131\uacf5\ud558\uace0 \ubc1b\uc740 \uc544\uc774\ud15c \ub9ac\uc2a4\ud2b8\uac00 \ube44\uc5b4 \uc788\uc73c\uba74 MediatorResult.Success (endOfPaginationReached = true)\ub97c \ubc18\ud658\ud55c\ub2e4.<br \/>\n&#8211; \uc694\uccad \uc2dc \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud558\uba74 MediatorResult.Error\ub97c \ubc18\ud658\ud55c\ub2e4.<\/span><\/li>\n<\/ol>\n<pre class=\"lang: decode:true\">override suspend fun load(\r\n  loadType: LoadType,\r\n  state: PagingState&lt;Int, User&gt;\r\n): MediatorResult {\r\n  return try {\r\n    \/\/ \ub124\ud2b8\uc6cc\ud06c \ub85c\ub4dc \uba54\uc11c\ub4dc\ub294 \uc120\ud0dd\uc801\uc73c\ub85c after=&lt;user.id&gt; \ub9e4\uac1c\ubcc0\uc218\ub97c \ucde8\ud55c\ub2e4.\r\n    \/\/ \uccab\ubc88\uc9f8 \ud398\uc774\uc9c0 \uc774\ud6c4 \ubaa8\ub4e0 \ud398\uc774\uc9c0\uc5d0 \ub300\ud574 \ub9c8\uc9c0\ub9c9 User ID\ub97c \uc804\ub2ec\ud558\uc5ec\r\n    \/\/ \ub9c8\uc9c0\ub9c9 \uc694\uccad\ud55c \uc9c0\uc810\ubd80\ud130 \uc9c0\uc18d\uc801\uc73c\ub85c \ub370\uc774\ud130\ub97c \uac00\uc838\uc62c \uc218 \uc788\ub3c4\ub85d \ud55c\ub2e4\r\n    \/\/ REFRESH(\uc0c8\ub85c\uace0\uce68)\uc744 \ud558\uae30 \uc704\ud574\uc11c\ub294 null\uc744 \uc804\ub2ec\ud558\uc5ec \uccab\ubc88\uc9f8 \ud398\uc774\uc9c0\ub97c \ubd88\ub7ec\uc624\uc790.\r\n    val loadKey = when (loadType) {\r\n      LoadType.REFRESH -&gt; null\r\n      \/\/ \uc774 \uc608\uc81c\uc5d0\uc11c\ub294 REFRESH\uac00 \ud56d\uc0c1 \uccab\ubc88\uc9f8 \ud398\uc774\uc9c0\ub97c \ub85c\ub4dc\ud558\uae30 \ub54c\ubb38\uc5d0 \r\n      \/\/ \uc774\uc804 \ub370\uc774\ud130\ub97c \ubd88\ub7ec\uc62c \ud544\uc694\uac00 \uc5c6\ub2e4. \uc989\uc2dc \ud398\uc774\uc9d5\uc758 \ub05d\uc744 \uc54c\ub9ac\uc790\r\n      LoadType.PREPEND -&gt;\r\n        return MediatorResult.Success(endOfPaginationReached = true)\r\n      LoadType.APPEND -&gt; {\r\n        val lastItem = state.lastItemOrNull()\r\n\r\n        \/\/ null\uc744 \ub124\ud2b8\uc6cc\ud06c \uc11c\ube44\uc2a4\uc5d0 \uc804\ub2ec\ud558\ub294 \uac83\uc740 \ucd08\uae30 \ub85c\ub4dc\uc5d0\ub9cc \uc720\ud6a8\ud558\uae30 \ub54c\ubb38\uc5d0\r\n        \/\/ appending\uc2dc \ubd84\uba85\ud558\uac8c \ub9c8\uc9c0\ub9c9 \ud56d\ubaa9\uc774 null\uc778\uc9c0 \ud655\uc778\ud558\uc790.\r\n        \/\/ \ub9cc\uc57d \ub9c8\uc9c0\ub9c9 \ud56d\ubaa9\uc774 null\uc774\uba74 \ucd08\uae30 REFRESH \uc774\ud6c4 \uc544\uc774\ud15c\uc774 \ub85c\ub4dc\ub418\uc9c0 \uc54a\uc558\uace0,\r\n        \/\/ \ub354 \uc774\uc0c1 \ub85c\ub4dc\ud560 \uc544\uc774\ud15c\ub3c4 \uc5c6\uc74c\uc744 \uc758\ubbf8\ud55c\ub2e4.\r\n        if (lastItem == null) {\r\n          return MediatorResult.Success(\r\n            endOfPaginationReached = true\r\n          )\r\n        }\r\n\r\n        lastItem.id\r\n      }\r\n    }\r\n\r\n    \/\/ <span>Retrofit\uc744 \ud1b5\ud574 \ub124\ud2b8\uc6cc\ud06c \ub85c\ub4dc\uc2dc suspending \ub418\ub294 \uac83\uc5d0 \ub300\ud574 <\/span>\r\n    \/\/ <span>withContext(Dispatcher.ID)\ub85c \uac10\uc300 \ud544\uc694\uac00 \uc5c6\ub2e4.<\/span>\r\n    \/\/ Retrofit\uc758 \ucf54\ub8e8\ud2f4 CallAdapter\uac00 worker \uc2a4\ub808\ub4dc\ub85c \ucc98\ub9ac\ud558\uae30 \ub584\ubb38\uc774\ub2e4.\r\n    val response = networkService.searchUsers(\r\n      query = query, after = loadKey\r\n    )\r\n\r\n    database.withTransaction {\r\n      if (loadType == LoadType.REFRESH) {\r\n        userDao.deleteByQuery(query)\r\n      }\r\n\r\n      \/\/ \uc0c8\ub85c\uc6b4 users\ub97c \uc0bd\uc785\ud558\uace0, \ud604\uc7ac PagingData\ub97c \ubb34\ud6a8\ud654 \ud55c\ub2e4.\r\n      \/\/ \uadf8\ub7ec\uba74 \ud398\uc774\uc9d5 \ucef4\ud3ec\ub10c\ud2b8\ub294 \ub370\uc774\ud130 \ubca0\uc774\uc2a4 \ub0b4\uc6a9\uc744 \uc790\ub3d9\uc73c\ub85c \uac31\uc2e0\ud558\uc5ec \ubcf4\uc5ec\uc8fc\uac8c \ub41c\ub2e4.\r\n      userDao.insertAll(response.users)\r\n    }\r\n\r\n    MediatorResult.Success(\r\n      endOfPaginationReached = response.nextKey == null\r\n    )\r\n  } catch (e: IOException) {\r\n    MediatorResult.Error(e)\r\n  } catch (e: HttpException) {\r\n    MediatorResult.Error(e)\r\n  }\r\n}<\/pre>\n<h3>Pager \uc0dd\uc131\ud558\uae30<\/h3>\n<p><span>\ub9c8\uc9c0\ub9c9\uc73c\ub85c \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\uc758 \uc2a4\ud2b8\ub9bc\uc744 \uc124\uc815\ud558\ub824\uba74 Pager \uc778\uc2a4\ud134\uc2a4\ub97c \ub9cc\ub4e4\uc5b4\uc57c \ud55c\ub2e4. \uc774\uac83\uc740 \ub2e8\uc21c\ud55c \ub124\ud2b8\uc6cc\ud06c \ub370\uc774\ud130 \uc18c\uc2a4\uc5d0\uc11c Pager\ub97c \ub9cc\ub4dc\ub294 \uac83\uacfc \ube44\uc2b7\ud558\uc9c0\ub9cc \ub2e4\ub974\uac8c \uc218\ud589\ud574\uc57c\ud558\ub294 \ub450 \uac00\uc9c0 \uc791\uc5c5\uc774 \uc788\ub2e4.<\/span><\/p>\n<ul>\n<li>PagingSource\uc0dd\uc131\uc790\ub97c \uc9c1\uc811\uc801\uc73c\ub85c \ub118\uae30\ub294 \ub300\uc2e0 DAO\ub85c\ubd80\ud130 PagingSource\uac1d\uccb4\ub97c \ubc18\ud658\ud558\ub294 query\uba54\uc11c\ub4dc\ub97c \uc81c\uacf5\ud55c\ub2e4.<\/li>\n<li>RemoteMediator\uc758 \uad6c\ud604\uccb4\uc758 \uc778\uc2a4\ud134\uc2a4\ub97c \ub9e4\uac1c\ubcc0\uc218\ub85c \uc81c\uacf5\ud574\uc57c \ud55c\ub2e4.<\/li>\n<\/ul>\n<pre class=\"lang: decode:true \">val userDao = database.userDao()\r\nval pager = Pager(\r\n  config = PagingConfig(pageSize = 50)\r\n  remoteMediator = ExampleRemoteMediator(query, database, networkService)\r\n) {\r\n  userDao.pagingSource(query)\r\n}<\/pre>\n<h2>Remote key \uad00\ub9ac\ud558\uae30<\/h2>\n<p><span>\uc6d0\uaca9 \ud0a4(Remote key)\ub294 RemoteMediator \uad6c\ud604\uc5d0\uc11c \ub2e4\uc74c\uc5d0 \ub85c\ub4dc \ud560 \ub370\uc774\ud130\ub97c \ubc31\uc5d4\ub4dc \uc11c\ube44\uc2a4\uc5d0 \uc54c\ub9ac\ub294 \ub370 \uc0ac\uc6a9\ud558\ub294 \ud0a4\ub2e4. \uac00\uc7a5 \uac04\ub2e8\ud55c \ubc29\ubc95\uc740 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\uc758 \uac01 \ud56d\ubaa9\uc5d0 \uc27d\uac8c \ucc38\uc870 \ud560 \uc218\uc788\ub294 \uc6d0\uaca9 \ud0a4\ub97c \ud3ec\ud568\uc2dc\ucf1c \uc774\ub97c \ucc38\uc870\ud558\ub294 \uac83\uc774\ub2e4. \uadf8\ub7ec\ub098 \uc6d0\uaca9 \ud0a4\uac00 \uac1c\ubcc4 \ud56d\ubaa9\uc5d0 \ud574\ub2f9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 \ubcc4\ub3c4\ub85c \uc800\uc7a5\ud558\uace0 load () \uba54\uc11c\ub4dc\uc5d0\uc11c \uad00\ub9ac\ud574\uc57c \ud55c\ub2e4.\u00a0<\/span><\/p>\n<h3>\uc6d0\uaca9 \ud0a4 \ud14c\uc774\ube14 \ucd94\uac00\ud558\uae30<\/h3>\n<p><span>\uc6d0\uaca9 \ud0a4\uac00 \ubaa9\ub85d \ud56d\ubaa9\uacfc \uc9c1\uc811 \uc5f0\uacb0\ub418\uc9c0 \uc54a\uc740 \uacbd\uc6b0 \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc758 \ubcc4\ub3c4 \ud14c\uc774\ube14\uc5d0 \uc800\uc7a5\ud558\ub294 \uac83\uc774 \uac00\uc7a5 \uc88b\ub2e4. \uc6d0\uaca9 \ud0a4 \ud14c\uc774\ube14\uc744 \ub098\ud0c0\ub0b4\ub294 Room \uc5d4\ud130\ud2f0\ub97c \uc815\uc758\ud558\uc790.<\/span><\/p>\n<pre class=\"lang: decode:true \">@Entity(tableName = \"remote_keys\")\r\ndata class RemoteKey(val label: String, val nextKey: String?)<\/pre>\n<div class=\"gtx-body\">RemoteKey \uc5d4\ud130\ud2f0\uc5d0 \ub300\ud55c DAO\ub3c4 \uc815\uc758\ud574\uc57c\ud569\ub2c8\ub2e4.<\/div>\n<pre class=\"lang: decode:true \">@Dao\r\ninterface RemoteKeyDao {\r\n  @Insert(onConflict = OnConflictStrategy.REPLACE)\r\n  suspend fun insertOrReplace(remoteKey: RemoteKey)\r\n\r\n  @Query(\"SELECT * FROM remote_keys WHERE label = :query\")\r\n  suspend fun remoteKeyByQuery(query: String): RemoteKey\r\n\r\n  @Query(\"DELETE FROM remote_keys WHERE label = :query\")\r\n  suspend fun deleteByQuery(query: String)\r\n}<\/pre>\n<h3>\uc6d0\uaca9 \ud0a4\ub85c \ub85c\ub4dc\ud558\uae30<\/h3>\n<p><span>load () \uba54\uc11c\ub4dc\uac00 \uc6d0\uaca9 \ud0a4\ub97c \uad00\ub9ac\ud574\uc57c\ud558\ub294 \uacbd\uc6b0 RemoteMediator\uc758 \uae30\ubcf8 \uc0ac\uc6a9\ubc95\uacfc \ube44\uad50\ud558\uc5ec \ub2e4\uc74c\uacfc \uac19\uc740 \ubc29\uc2dd\uc73c\ub85c \ub2e4\ub974\uac8c \uc815\uc758\ud574\uc57c \ud55c\ub2e4.<\/span><\/p>\n<ul>\n<li><span>\uc6d0\uaca9 \ud0a4 \ud14c\uc774\ube14\uc5d0 \uc561\uc138\uc2a4 \ud560 \uc218 \uc788\ub294 DAO\ub97c\u00a0 \ucd94\uac00 \uc18d\uc131\uc73c\ub85c \ud3ec\ud568\ud55c\ub2e4.<\/span><\/li>\n<li><span>PagingState\ub97c \uc0ac\uc6a9\ud558\ub294 \ub300\uc2e0 \uc6d0\uaca9 \ud0a4 \ud14c\uc774\ube14\uc744 \ucffc\ub9ac\ud558\uc5ec \ub2e4\uc74c\uc5d0 \ub85c\ub4dc \ud560 \ud0a4\ub97c \uacb0\uc815\ud55c\ub2e4.<\/span><\/li>\n<li><span>\ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130 \uc790\uccb4 \uc678\uc5d0\ub3c4 \ub124\ud2b8\uc6cc\ud06c \ub370\uc774\ud130 \uc6d0\ubcf8\uc5d0\uc11c \ubc18\ud658 \ub41c \uc6d0\uaca9 \ud0a4\ub97c \uc0bd\uc785\ud558\uac70\ub098 \uc800\uc7a5\ud55c\ub2e4.<\/span><\/li>\n<\/ul>\n<pre class=\"lang: decode:true\">@OptIn(ExperimentalPagingApi::class)\r\nclass ExampleRemoteMediator(\r\n  private val query: String,\r\n  private val database: RoomDb,\r\n  private val networkService: ExampleBackendService\r\n) : RemoteMediator&lt;Int, User&gt;() {\r\n  val userDao = database.userDao()\r\n  val remoteKeyDao = database.remoteKeyDao()\r\n\r\n  override suspend fun load(\r\n    loadType: LoadType,\r\n    state: PagingState&lt;Int, User&gt;\r\n  ): MediatorResult {\r\n    return try {\r\n      val loadKey = when (loadType) {\r\n        LoadType.REFRESH -&gt; null\r\n        LoadType.PREPEND -&gt; return MediatorResult.Success(\r\n          endOfPaginationReached = true\r\n        )\r\n        \r\n        \/\/ remoteKeyDao\uc5d0 \ub2e4\uc74c \uc6d0\uaca9 \ud0a4\ub97c \uc694\uccad\ud55c\ub2e4.\r\n        LoadType.APPEND -&gt; {\r\n          val remoteKey = database.withTransaction {\r\n            remoteKeyDao.remoteKeyByQuery(query)\r\n          }\r\n\r\n          if (remoteKey.nextKey == null) {\r\n            return MediatorResult.Success(\r\n              endOfPaginationReached = true\r\n            )\r\n          }\r\n\r\n          remoteKey.nextKey\r\n        }\r\n      }\r\n\r\n      val response = networkService.searchUsers(query, loadKey)\r\n\r\n      \/\/ \ubd88\ub7ec\uc628 \ub370\uc774\ud130\uc640 next key\ub97c \uc800\uc7a5\ud558\uc5ec \uc601\uad6c\uc801\uc73c\ub85c \uad00\ub9ac\ud558\uc790.\r\n      database.withTransaction {\r\n        if (loadType == LoadType.REFRESH) {\r\n          remoteKeyDao.deleteByQuery(query)\r\n          userDao.deleteByQuery(query)\r\n        }\r\n\r\n        \/\/ \uc6d0\uaca9 \ud0a4\ub97c \uac31\uc2e0\ud55c\ub2e4.\r\n        remoteKeyDao.insertOrReplace(\r\n          RemoteKey(query, response.nextKey)\r\n        )\r\n\r\n        userDao.insertAll(response.users)\r\n      }\r\n\r\n      MediatorResult.Success(\r\n        endOfPaginationReached = response.nextKey == null\r\n      )\r\n    } catch (e: IOException) {\r\n      MediatorResult.Error(e)\r\n    } catch (e: HttpException) {\r\n      MediatorResult.Error(e)\r\n    }\r\n  }\r\n}<\/pre>\n<h2 class=\"gtx-body\">\uc81c\uc790\ub9ac\uc5d0\uc11c \uc0c8\ub85c \uace0\uce68<\/h2>\n<p><span>\uc571\uc774 \uc774\uc804 \uc608\uc81c\uc5d0\uc11c\uc640 \uac19\uc774 \ubaa9\ub85d \ub9e8 \uc704\uc5d0\uc11c \ub124\ud2b8\uc6cc\ud06c \uc0c8\ub85c \uace0\uce68\ub9cc \uc9c0\uc6d0\ud574\uc57c\ud558\ub294 \uacbd\uc6b0 RemoteMediator\ub294 \ub85c\ub4dc \ub3d9\uc791\uc744 \uc55e\uc5d0 \ucd94\uac00 \ud560 \ud544\uc694\uac00 \uc5c6\ub2e4.<\/span><\/p>\n<p><span>\uadf8\ub7ec\ub098 \uc571\uc774 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\ub85c\uc758 \uc810\uc9c4\uc801 \ub85c\ub4dc\ub97c \uc9c0\uc6d0\ud574\uc57c\ud558\ub294 \uacbd\uc6b0 \uc0ac\uc6a9\uc790\uc758 \uc2a4\ud06c\ub864 \uc704\uce58 \uc778 \uc575\ucee4\uc5d0\uc11c \uc2dc\uc791\ud558\ub294 \ud398\uc774\uc9c0 \uc7ac\uac1c \uc9c0\uc6d0\uc744 \uc81c\uacf5\ud574\uc57c \ud55c\ub2e4. Room\uc758 PagingSource \uad6c\ud604\uc774 \uc774\ub97c \ucc98\ub9ac\ud558\uc9c0\ub9cc Room\uc744 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uacbd\uc6b0 PagingSource.getRefreshKey ()\ub97c \uc7ac\uc815\uc758\ud558\uc5ec \uc774\ub97c \uc218\ud589 \ud560 \uc218 \uc788\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true\">\/\/ Item-keyed\uae30\ubc18\r\noverride fun getRefreshKey(state: PagingState): String? {\r\n  return state.anchorPosition?.let { anchorPosition -&gt;\r\n    state.getClosestItemToPosition(anchorPosition)?.id\r\n  }\r\n}\r\n\r\n\/\/ Positional\uae30\ubc18\r\noverride fun getRefreshKey(state: PagingState): Int? {\r\n  return state.anchorPosition\r\n}<\/pre>\n<p><span>\ub2e4\uc74c \uadf8\ub9bc\uc740 \uba3c\uc800 \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \ub370\uc774\ud130\ub97c \ub85c\ub4dc \ud55c \ub2e4\uc74c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ub370\uc774\ud130\uac00 \uc5c6\uc73c\uba74 \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub85c\ub4dc\ud558\ub294 \ud504\ub85c\uc138\uc2a4\ub97c \ubcf4\uc5ec\uc900\ub2e4.<\/span><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/images\/paging3-layered-load.svg\" alt=\"The PagingSource loads from the database into the UI until the database\n    is out of data. Then the RemoteMediator loads from the network into the\n    database, and afterward the PagingSource continues loading.\" width=\"800\" \/><\/p>\n<\/div>\n<div id=\"translation\">\n<h1>\ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc \ubcc0\ud658\ud558\uae30<\/h1>\n<p><span>\ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\ub85c \uc791\uc5c5 \ud558\uace0, \ub85c\ub4dc \ud560 \ub54c \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \ubcc0\ud658\ud574\uc57c \ud558\ub294 \uacbd\uc6b0\uac00 \ub9ce\ub2e4. \uc608\ub97c \ub4e4\uc5b4 \ud56d\ubaa9 \ubaa9\ub85d\uc744 \ud544\ud130\ub9c1\ud558\uac70\ub098 \ud56d\ubaa9\uc744 UI\uc5d0 \ud45c\uc2dc\ud558\uae30 \uc804\uc5d0 \ub2e4\ub978 \uc720\ud615\uc73c\ub85c \ubcc0\ud658\ud574\uc57c \ud560 \uc218 \uc788\ub2e4. \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc \ubcc0\ud658\uc758 \ub610 \ub2e4\ub978 \uc77c\ubc18\uc801\uc778 \uc720\uc988\ucf00\uc774\uc2a4\ub294 \ubaa9\ub85d \uad6c\ubd84 \uae30\ud638\ub97c \ucd94\uac00\ud558\ub294 \uac83\uc774\ub2e4. <\/span><\/p>\n<p><span>\uc77c\ubc18\uc801\uc73c\ub85c \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc5d0 \uc9c1\uc811 \ubcc0\ud658\uc744 \uc801\uc6a9\ud558\uba74 \ub9ac\ud3ec\uc9c0\ud1a0\ub9ac \uad6c\uc131\uacfc UI \uad6c\uc131\uc744 \ubcc4\ub3c4\ub85c \uc720\uc9c0\ud560 \uc218 \uc788\ub2e4.\u00a0<\/span><\/p>\n<h2>\uae30\ubcf8\uc801\uc778 \ubcc0\ud658 \uc801\uc6a9\ud558\uae30<\/h2>\n<p><span>PagingData\ub294 \ubc18\uc751 \uc2a4\ud2b8\ub9bc\uc5d0 \ucea1\uc290\ud654\ub418\uc5b4 \uc788\uae30 \ub54c\ubb38\uc5d0 \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\uace0 \ud45c\uc2dc\ud558\ub294 \uc0ac\uc774\uc5d0 \uc810\uc9c4\uc801\uc73c\ub85c \ub370\uc774\ud130\uc5d0 \ubcc0\ud658 \uc791\uc5c5\uc744 \uc801\uc6a9 \ud560 \uc218 \uc788\ub2e4. <\/span><\/p>\n<p><span>\uc2a4\ud2b8\ub9bc\uc758 \uac01 PagingData \uac1d\uccb4\uc5d0 \ubcc0\ud658\uc744 \uc801\uc6a9\ud558\ub824\uba74 \uc2a4\ud2b8\ub9bc\uc758 map() \uc5f0\uc0b0\uc790 \ub0b4\uc5d0 \ubcc0\ud658\uc744 \ubc30\uce58\ud55c\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true\">pager.flow \/\/ \ud0c0\uc785\uc740 Flow&lt;PagingData&lt;User&gt;&gt; \uc774\ub2e4.\r\n  \/\/ \ubc14\uae65\ucabd \uc2a4\ud2b8\ub9bc\uc744 \ub9e4\ud551\ud558\uc5ec \uc0c8\ub85c\uc6b4 PagingData\ubcc0\ud658\uc744 \uc801\uc6a9\ud560 \uc218 \uc788\ub2e4.\r\n  .map { pagingData -&gt;\r\n    \/\/ \uc774 \ube14\ub7ed\uc548 \ubcc0\ud658\uc740 \ud398\uc774\uc9d5 \ub41c \ub370\uc774\ud130\uc758 \ud56d\ubaa9\ub4e4\uc5d0 \uc801\uc6a9\ub41c\ub2e4.\r\n}<\/pre>\n<p>\uc544<span>\ub798 \uc608\uc81c\ub294 \ud398\uc774\uc9c0 \ub41c \ub370\uc774\ud130\uc5d0 \ub2e4\uc74c \ubcc0\ud658\uc744 \uc801\uc6a9\ud558\uc5ec\uc774 \ud328\ud134\uc744 \ubcf4\uc5ec\uc900\ub2e4.<\/span><\/p>\n<ol>\n<li><span>\ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc5d0 \ub300\ud55c \uc678\ubd80 map () \uc5f0\uc0b0\uc790\ub294 \ud574\ub2f9 \ube14\ub85d \ub0b4\ubd80\uc758 \ubaa8\ub4e0 \uc791\uc5c5\uc744 \uc2a4\ud2b8\ub9bc\uc5d0\uc11c PagingData \uac1d\uccb4\uc758 \uac01 \ud6c4\uc18d \uc0dd\uc131\uc5d0 \uc801\uc6a9\ud55c\ub2e4.<\/span><\/li>\n<li><span>filter \uc5f0\uc0b0\uc790\ub294 UI\uc5d0 \ud45c\uc2dc\ub418\uc9c0 \uc54a\ub294 \ubaa8\ub4e0 \ud56d\ubaa9\uc744 \uc0ad\uc81c\ud55c\ub2e4.<\/span><\/li>\n<li><span>\ub610 \ub2e4\ub978 \ub9f5 \uc791\uc5c5\uc740 \ubaa9\ub85d\uc758 \uac01 User \uac1d\uccb4\ub97c UiModel \ud0c0\uc785\uc73c\ub85c \ubcc0\ud658\ud55c\ub2e4.<\/span><\/li>\n<\/ol>\n<pre class=\"lang: decode:true\">pager.flow \/\/ \ud0c0\uc785\uc740 Flow&lt;PagingData&lt;User&gt;&gt; \uc774\ub2e4.\r\n    .map { pagingData -&gt;\r\n        pagingData.filter { user -&gt; !user.hiddenFromUi }\r\n            .map { user -&gt; UiModel(user) }\r\n    }\r\n    .cachedIn(viewModelScope)<\/pre>\n<p>c<span>achedIn() \uc5f0\uc0b0\uc790\ub294 \uc774\uc804\uc5d0 \ubc1c\uc0dd\ud55c \ubaa8\ub4e0 \ubcc0\ud658 \uacb0\uacfc\ub97c \uce90\uc2dc\ud55c\ub2e4. \ub530\ub77c\uc11c cachedIn ()\uc740 ViewModel\uc5d0\uc11c \ub9c8\uc9c0\ub9c9 \ud638\ucd9c\uc774\uc5b4\uc57c \ud55c\ub2e4.\u00a0<\/span><\/p>\n<h2>\ubaa9\ub85d \uad6c\ubd84\uc790 \ucd94\uac00\ud558\uae30<\/h2>\n<p><span>Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \ub3d9\uc801\uc778 \ubaa9\ub85d \uad6c\ubd84\uc790\ub97c \uc9c0\uc6d0\ud55c\ub2e4. RecyclerView \ubaa9\ub85d \ud56d\ubaa9\uc73c\ub85c \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc5d0 \uc9c1\uc811 \uad6c\ubd84 \uae30\ud638\ub97c \uc0bd\uc785\ud558\uc5ec \ubaa9\ub85d \uac00\ub3c5\uc131\uc744 \ud5a5\uc0c1\uc2dc\ud0ac \uc218 \uc788\ub2e4. \uacb0\uacfc\uc801\uc73c\ub85c \uad6c\ubd84\uc790\ub294 \ubaa8\ub4e0 \uae30\ub2a5\uc744 \uac16\ucd98 ViewHolder \uac1c\uccb4\uc774\uba70, \uc0c1\ud638 \uc791\uc6a9, \uc811\uadfc\uc131 \ud3ec\ucee4\uc2a4 \ubc0f View\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uae30\ud0c0 \ubaa8\ub4e0 \uae30\ub2a5\uc744 \ud65c\uc131\ud654\ud55c\ub2e4.<\/span><\/p>\n<p><span>\ud398\uc774\uc9c0 \ubaa9\ub85d\uc5d0 \uad6c\ubd84\uc790\ub97c \uc0bd\uc785\ud558\ub294 \ub370\ub294 \uc138 \ub2e8\uacc4\uac00 \uc788\ub2e4.<\/span><\/p>\n<ol>\n<li><span>\uad6c\ubd84\uc790\ub97c \uc218\uc6a9\ud558\ub3c4\ub85d UI \ubaa8\ub378\uc744 \ubcc0\ud658\ud55c\ub2e4.<\/span><\/li>\n<li><span>\ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \ubcc0\ud658\ud558\uc5ec \ub370\uc774\ud130 \ub85c\ub4dc\uc640 \ub370\uc774\ud130 \ud45c\uc2dc \uc0ac\uc774\uc5d0 \uad6c\ubd84\uc790\ub97c \ub3d9\uc801\uc73c\ub85c \ucd94\uac00\ud55c\ub2e4.<\/span><\/li>\n<li><span>\uad6c\ubd84 \ud56d\ubaa9\uc744 \ucc98\ub9ac\ud558\ub3c4\ub85d UI\ub97c \uc5c5\ub370\uc774\ud2b8 \ud55c\ub2e4.<\/span><\/li>\n<\/ol>\n<p><em>Note : \uc778\ud130\ub809\ud2f0\ube0c\ud558\uac70\ub098 \ud3ec\ucee4\uc2a4\ub97c \uac16\ub294 \uad6c\ubd84\uc790\uac00 \ud544\uc694\uc5c6\ub2e4\uba74 \uac04\ub2e8\ud788 RecyclerView.ItemDecoration\uc744 \uc0ac\uc6a9\ud558\uc5ec \uc815\uc801\uc778 \uad6c\ubd84\uc790\ub97c \ub9cc\ub4dc\ub294 \uac83\ub3c4 \ubc29\ubc95\uc774\ub2e4.<\/em><\/p>\n<\/div>\n<h3>UI \ubaa8\ub378 \ubcc0\ud658\ud558\uae30<\/h3>\n<p><span>Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \uad6c\ubd84\uc790\ub97c \uc2e4\uc81c \ubaa9\ub85d \ud56d\ubaa9\uc73c\ub85c RecyclerView\uc5d0 \uc0bd\uc785\ud558\uc9c0\ub9cc \uad6c\ubd84\uc790 UI\uac00 \ub2e4\ub978 \ubaa9\ub85d \ud56d\ubaa9\uc758 UI\uc640 \ub2e4\ub97c \uac00\ub2a5\uc131\uc774 \ub192\uae30 \ub54c\ubb38\uc5d0 \uad6c\ubd84\uc790 \uc544\uc774\ud15c\uc740 \ubaa9\ub85d\uc758 \ub2e4\ub978 \ud56d\ubaa9\uacfc \uad6c\ubd84\ud560 \uc218 \uc788\uc5b4\uc57c \ud55c\ub2e4. \ud574\uacb0\ucc45\uc740 \ub370\uc774\ud130\uc640 \uad6c\ubd84\uc790\ub97c \ub098\ud0c0\ub0b4\ub294 \ud558\uc704 \ud074\ub798\uc2a4\uac00\uc788\ub294 Kotlin \ubd09\uc778 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4dc\ub294 \uac83\uc774\ub2e4. \ub610\ub294 \ubaa9\ub85d \ud56d\ubaa9 \ud074\ub798\uc2a4\uc640 \uad6c\ubd84\uc790 \ud074\ub798\uc2a4\uc5d0 \uc758\ud574 \ud655\uc7a5\ub418\ub294 \uae30\ubcf8 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4e4 \uc218 \uc788\ub2e4.<\/span><\/p>\n<p><span>\uc0ac\uc6a9\uc790 \ud56d\ubaa9\uc758 \ud398\uc774\uc9c0 \ub41c \ubaa9\ub85d\uc5d0 \uad6c\ubd84 \uae30\ud638\ub97c \ucd94\uac00\ud55c\ub2e4\uace0 \uac00\uc815\ud574\ubcf4\uc790. \ub2e4\uc74c \uc2a4\ub2c8\ud3ab\uc740 \uc778\uc2a4\ud134\uc2a4\uac00 UserModel \ub610\ub294 SeparatorModel \uc77c \uc218\uc788\ub294 \uae30\ubcf8 \ud074\ub798\uc2a4\ub97c \ub9cc\ub4dc\ub294 \ubc29\ubc95\uc744 \ubcf4\uc5ec\uc900\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true \">sealed class UiModel {\r\n  class UserModel(val id: String, val label: String) : UiModel() {\r\n    constructor(user: User) : this(user.id, user.label)\r\n  }\r\n\r\n  class SeparatorModel(val description: String) : UiModel()\r\n}<\/pre>\n<h3>\ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc \ubcc0\ud658\ud558\uae30<\/h3>\n<p><span>\ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc744 \ub85c\ub4dc \ud55c \ud6c4 \ud45c\uc2dc\ud558\uae30 \uc804\uc5d0 \ubcc0\ud658\uc744 \ub370\uc774\ud130 \uc2a4\ud2b8\ub9bc\uc5d0 \uc801\uc6a9\ud574\uc57c \ud55c\ub2e4. \ubcc0\ud658\uc740 \ub2e4\uc74c\uc744 \uc218\ud589\ud574\uc57c \ud55c\ub2e4.<\/span><\/p>\n<div id=\"translation\">\n<ul>\n<li>\ub85c\ub4dc\ud55c \ubaa9\ub85d \uc544\uc774\ud15c\ub4e4\uc744 \uc0c8\ub85c\uc6b4 \uae30\ubcf8 \uc544\uc774\ud15c \ud0c0\uc785\uc744 \ubc18\uc601\ud558\ub3c4\ub85d \ubcc0\ud658\ud55c\ub2e4.<\/li>\n<li><span>PagingData.insertSeparators () \uba54\uc11c\ub4dc\ub97c \uc0ac\uc6a9\ud558\uc5ec \uad6c\ubd84 \uae30\ud638\ub97c \ucd94\uac00\ud55c\ub2e4.<\/span><\/li>\n<\/ul>\n<p><span>\ub2e4\uc74c \uc608\uc81c\ub294 PagingData &lt;User&gt; \uc2a4\ud2b8\ub9bc\uc744 \uad6c\ubd84\uc790\uac00 \ucd94\uac00 \ub41c PagingData &lt;UiModel&gt; \uc2a4\ud2b8\ub9bc\uc73c\ub85c \uc5c5\ub370\uc774\ud2b8\ud558\ub294 \ubcc0\ud658 \uc791\uc5c5\uc744 \ubcf4\uc5ec\uc900\ub2e4.<\/span><\/p>\n<pre class=\"lang: decode:true\">pager.flow.map { pagingData: PagingData&lt;User&gt; -&gt;\r\n  \/\/ \ubc14\uae65 \uc2a4\ud2b8\ub9bc\uc744 \ub9e4\ud551\ud558\uc5ec \uac01 \ud398\uc774\uc9d5 \ub370\uc774\ud130 \ub300\ud55c \ubcc0\ud658\uc744 \uc218\ud589\ud55c\ub2e4.\r\n  pagingData\r\n  .map { user -&gt;\r\n    \/\/ \uc2a4\ud2b8\ub9bc\uc5d0 \uc788\ub294 \uc544\uc774\ud15c\ub4e4\uc744 UiModel.UserModel\ub85c \ubcc0\ud658\ud55c\ub2e4.\r\n    UiModel.UserModel(user)\r\n  }\r\n  .insertSeparators&lt;UiModel.UserModel, UiModel&gt; { before, after -&gt;\r\n    when {\r\n      before == null -&gt; UiModel.SeparatorModel(\"HEADER\")\r\n      after == null -&gt; UiModel.SeparatorModel(\"FOOTER\")\r\n      shouldSeparate(before, after) -&gt; UiModel.SeparatorModel(\r\n        \"BETWEEN ITEMS $before AND $after\"\r\n      )\r\n      \/\/ Return null to avoid adding a separator between two items.\r\n      else -&gt; null\r\n    }\r\n  }\r\n}<\/pre>\n<h3>UI\uc5d0\uc11c \uad6c\ubd84\uc790 \ub2e4\ub8e8\uae30<\/h3>\n<div id=\"translation\">\n<div class=\"gtx-body\">\ub9c8\uc9c0\ub9c9 \ub2e8\uacc4\ub294 \uad6c\ubd84\uc790 \ud0c0\uc785\uc744 \uc218\uc6a9\ud558\ub3c4\ub85d UI\ub97c \ubcc0\uacbd\ud558\ub294 \uac83\uc774\ub2e4. \uad6c\ubd84\uc790 \ud56d\ubaa9\uc5d0 \ub300\ud55c \ub808\uc774\uc544\uc6c3 \ubc0f \ubdf0 \ud640\ub354\ub97c \ub9cc\ub4e4\uace0, \ub458 \uc774\uc0c1\uc758 \ubdf0 \ud640\ub354 \uc720\ud615\uc744 \ucc98\ub9ac \ud560 \uc218 \u200b\u200b\uc788\ub3c4\ub85d RecyclerView.ViewHolder\ub97c \ubdf0 \ud640\ub354 \uc720\ud615\uc73c\ub85c \uc0ac\uc6a9\ud558\ub3c4\ub85d \ubaa9\ub85d \uc5b4\ub311\ud130\ub97c \ubcc0\uacbd\ud55c\ub2e4. \ub610\ub294 \ud56d\ubaa9 \ubc0f \uad6c\ubd84\uc790\ubcf4\uae30 \ud640\ub354 \ud074\ub798\uc2a4\uac00 \ubaa8\ub450 \ud655\uc7a5\ud558\ub294 \uacf5\ud1b5 \uae30\ubcf8 \ud074\ub798\uc2a4\ub97c \uc815\uc758 \ud560 \uc218 \uc788\ub2e4.<\/div>\n<div class=\"gtx-body\">\ub610\ud55c \ubaa9\ub85d \uc5b4\ub311\ud130\ub97c \ub2e4\uc74c\uacfc \uac19\uc774 \ubcc0\uacbd\ud574\uc57c \ud55c\ub2e4.<\/div>\n<ul>\n<li><span>onCreateViewHolder () \ubc0f onBindViewHolder () \uba54\uc11c\ub4dc\uc5d0 \ucf00\uc774\uc2a4\ub97c \ucd94\uac00\ud558\uc5ec \uad6c\ubd84\uc790 \ubaa9\ub85d \ud56d\ubaa9\uc744 \uacc4\uc0b0\ud55c\ub2e4.<\/span><\/li>\n<li>\uc0c8\ub85c\uc6b4 comparator\ub97c \uad6c\ud604\ud55c\ub2e4.<\/li>\n<\/ul>\n<\/div>\n<pre class=\"lang: decode:true \">class UiModelAdapter :\r\n  PagingDataAdapter&lt;UiModel, RecyclerView.ViewHolder&gt;(UiModelComparator) {\r\n\r\n  override fun onCreateViewHolder(\r\n    parent: ViewGroup,\r\n    viewType: Int\r\n  ) = when (viewType) {\r\n    R.layout.item -&gt; UserModelViewHolder(parent)\r\n    else -&gt; SeparatorModelViewHolder(parent)\r\n  }\r\n\r\n  override fun getItemViewType(position: Int) = when (getItem(position)) {\r\n    is UiModel.UserModel -&gt; R.layout.item\r\n    is UiModel.SeparatorModel -&gt; R.layout.separator_item\r\n    null -&gt; throw IllegalStateException(\"Unknown view\")\r\n  }\r\n\r\n  override fun onBindViewHolder(\r\n    holder: RecyclerView.ViewHolder,\r\n    position: Int\r\n  ) {\r\n    val item = getItem(position)\r\n    if (holder is UserModelViewHolder) {\r\n      holder.bind(item as UserModel)\r\n    } else if (holder is SeparatorModelViewHolder) {\r\n      holder.bind(item as SeparatorModel)\r\n    }\r\n  }\r\n}\r\n\r\nobject UiModelComparator : DiffUtil.ItemCallback&lt;UiModel&gt;() {\r\n  override fun areItemsTheSame(\r\n    oldItem: UiModel,\r\n    newItem: UiModel\r\n  ): Boolean {\r\n    val isSameRepoItem = oldItem is UiModel.UserModel\r\n      &amp;&amp; newItem is UiModel.UserModel\r\n      &amp;&amp; oldItem.id == newItem.id\r\n\r\n    val isSameSeparatorItem = oldItem is UiModel.SeparatorModel\r\n      &amp;&amp; newItem is UiModel.SeparatorModel\r\n      &amp;&amp; oldItem.description == newItem.description\r\n\r\n    return isSameRepoItem || isSameSeparatorItem\r\n  }\r\n\r\n  override fun areContentsTheSame(\r\n    oldItem: UiModel,\r\n    newItem: UiModel\r\n  ) = oldItem == newItem\r\n}<\/pre>\n<h1>Paging3\uc744 \uc0ac\uc6a9\ud558\ub294 \uc608\uc81c<\/h1>\n<p><strong><a href=\"https:\/\/github.com\/Charlezz\/FinalArchitecture\">\ucc30\uc2a4\uc758 \uc548\ub4dc\ub85c\uc774\ub4dc \uc571<\/a><\/strong> : \uc774 \ube14\ub85c\uadf8\ub97c \uc548\ub4dc\ub85c\uc774\ub4dc \ub124\uc774\ud2f0\ube0c \uc571\uc73c\ub85c \ub9cc\ub4e0 \uc608\uc81c<\/p>\n<p><strong><a href=\"https:\/\/github.com\/charlezz\/pickle\">Pickle<\/a><\/strong> : \ube60\ub974\uac8c \ubaa9\ub85d\uc744 \ubd88\ub7ec\uc624\ub294 \uc548\ub4dc\ub85c\uc774\ub4dc \uc774\ubbf8\uc9c0 \ud53c\ucee4 \ub77c\uc774\ube0c\ub7ec\ub9ac\u00a0<\/p>\n<p><a href=\"https:\/\/github.com\/android\/architecture-components-samples\/tree\/main\/PagingSample\">PagingSample<\/a> : \uad6c\uae00\uc758 \uae30\ubcf8\uc801\uc778 \ud398\uc774\uc9d5 \uc608\uc81c<\/p>\n<p><a href=\"https:\/\/github.com\/android\/architecture-components-samples\/tree\/main\/PagingWithNetworkSample\">PagingWithNetworkSample<\/a> : RemoteMediator\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uce90\uc2dc\ud558\uace0, \uc624\ud504\ub77c\uc778 \ubaa8\ub4dc\ub97c \uc9c0\uc6d0\ud558\ub294 \ud398\uc774\uc9d5 \uc608\uc81c<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\uac1c\uc694 \ud398\uc774\uc9d5 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\uba74 \ub85c\uceec \uc800\uc7a5\uc18c \ub610\ub294 \ub124\ud2b8\uc6cc\ud06c\ub97c \ud1b5\ud574 \ud070 \ub370\uc774\ud130\ub97c \uc798\uac8c \ucabc\uac1c\uc5b4 \ub85c\ub4dc\ud558\uace0 \ud45c\uc2dc \ud560 \uc218 \uc788\ub2e4. \uc774 \ubc29\uc2dd\uc744 \uc0ac\uc6a9\ud558\uba74 \uc571\uc5d0\uc11c \ub124\ud2b8\uc6cc\ud06c \ub300\uc5ed\ud3ed\uacfc \uc2dc\uc2a4\ud15c \ub9ac\uc18c\uc2a4\ub97c \ub354 \ud6a8\uc728\uc801\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. Paging3 \ub77c\uc774\ube0c\ub7ec\ub9ac \ucef4\ud3ec\ub10c\ud2b8 Android \uc571 \uc544\ud0a4\ud14d\ucc98\uc5d0 \ub9de\uac8c \uc124\uacc4\ub418\uace0 \ub2e4\ub978 Jetpack \uad6c\uc131 \uc694\uc18c\uc640 \uae54\ub054\ud558\uac8c \ud1b5\ud569\ub418\uba70 Kotlin \uc6b0\uc120 \uc9c0\uc6d0\uc744 \uc81c\uacf5\ud55c\ub2e4. \uc7a5\uc810 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[16],"tags":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/charlezz.com\/index.php?rest_route=\/wp\/v2\/posts\/44684"}],"collection":[{"href":"https:\/\/charlezz.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/charlezz.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/charlezz.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/charlezz.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=44684"}],"version-history":[{"count":2,"href":"https:\/\/charlezz.com\/index.php?rest_route=\/wp\/v2\/posts\/44684\/revisions"}],"predecessor-version":[{"id":44691,"href":"https:\/\/charlezz.com\/index.php?rest_route=\/wp\/v2\/posts\/44684\/revisions\/44691"}],"wp:attachment":[{"href":"https:\/\/charlezz.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=44684"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/charlezz.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=44684"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/charlezz.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=44684"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}