{"id":1012,"date":"2021-07-10T17:23:21","date_gmt":"2021-07-10T08:23:21","guid":{"rendered":"http:\/\/batmask.dothome.co.kr\/?p=1012"},"modified":"2025-09-12T18:29:54","modified_gmt":"2025-09-12T09:29:54","slug":"android-paging-library","status":"publish","type":"post","link":"http:\/\/batmask.net\/index.php\/2021\/07\/10\/1012\/","title":{"rendered":"Android : Paging Library"},"content":{"rendered":"\n<p> \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\ub4e0\uc9c0, DB\uc5d0 \uc800\uc7a5\ub41c\uac78 \uac00\uc838\uc624\ub4e0\uc9c0 \ub370\uc774\ud130\uac00 \ub9e4\uc6b0 \ub9ce\uc544\uc9c0\uba74 \ud55c\ubc88\uc5d0 \uc77d\uc5b4\uc62c \ub54c \ubb38\uc81c\uac00 \uc0dd\uae30\uac8c\ub41c\ub2e4. \uadf8\ub807\ub2e4\uace0 \ud654\uba74\uc5d0 \ubcf4\uc774\ub294 \ub9cc\ud07c\ub9cc \ub9e4\ubc88 \uc77d\uc5b4\uc624\uac8c\ub418\uba74, \uc2a4\ud06c\ub864\ub4f1\uc744 \uc774\uc6a9\ud558\uc5ec \ub370\uc774\ud130\ub97c \uc0b4\ud3b4\ubcfc \ub54c, \ub809\uc774 \uc0dd\uae38 \uac83\uc774\ub2e4. \uc774\ub97c \ud574\uacb0\ud558\uae30\uc704\ud574 \uc0ac\uc6a9\uc790 \uc785\ub825\uc5d0 \ucda9\ubd84\ud788 \ubc18\uc751\ud560\ub9cc\ud07c \uc5ec\uc720\uc788\ub294 \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\uace0, \uc0ac\uc6a9\uc790\uac00 \ucd94\uac00\uc801\uc73c\ub85c \ub370\uc774\ud130\ub97c \uc0b4\ud3b4\ubcfc \ub54c, \ud544\uc694\ud55c \ub9cc\ud07c\uc529 \ubbf8\ub9ac \uc77d\uc5b4\uc624\ub294 \uae30\ubc95\uc774 \uc624\ub798\uc804\ubd80\ud130 \uc0ac\uc6a9\ub418\uc5b4 \uc654\ub2e4. \uc774\uac78 paging \uae30\ubc95\uc774\ub77c \ud558\ub294\ub370,  \uaf2d \uc774\ub7f0\uacbd\uc6b0\uac00 \uc544\ub2c8\ub77c\ub3c4 \uc5ec\ub7ec\uacf3\uc5d0\uc11c \ucc3e\uc544\ubcfc \uc218 \uc788\ub2e4.<br> \uac00\uc7a5 \uac00\uae4c\uc774\uc5d0\uc11c \ucc3e\uc544\ubcfc \uc218 \uc788\ub294 \uacf3\uc740 Operating System\uc774\ub2e4. OS\uc5d0\uc11c paging \uae30\ubc95\uc740 \uba54\ubaa8\ub9ac\ub97c \ud655\uc7a5\ud558\ub294\ub370 \uc0ac\uc6a9\ub41c\ub2e4. \uace0\uc815\ub41c \ub370\uc774\ud130 \uc0ac\uc774\uc988\ub97c page\ub77c\uace0 \ud558\uba70, HDD\uac19\uc740\uacf3\uc5d0 \uc2e4\uc81c\ubcf4\ub2e4 \ud6e8\uc52c \ud070 \uac00\uc0c1\uba54\ubaa8\ub9ac\ub97c \ub9cc\ub4e4\uc5b4\ub193\uace0 \uc0ac\uc6a9\ud558\ub294 page\ub4e4\ub9cc \uc2e4\uc81c \uba54\ubaa8\ub9ac\uc5d0 \uc62c\ub824\uc11c \uc4f0\ub294 \ubc29\uc2dd\uc774\ub2e4. <br> Database\uc5d0\uc11c\ub3c4 \ucc3e\uc544\ubcfc \uc218 \uc788\ub2e4. \ub370\uc774\ud130\uac00 \ub9ce\uc740 \uacbd\uc6b0, \ucffc\ub9ac \uacb0\uacfc\ub97c \ubaa8\ub450 \uba54\ubaa8\ub9ac\uc5d0 \uc62c\ub9b4 \uc218 \uc5c6\uac70\ub098 \ubd80\ub2f4\uc2a4\ub7ec\uc6b8 \uc218 \uc788\ub2e4. \uc774 \uacbd\uc6b0, Database \uc790\uccb4\uc5d0\uc11c \ud544\uc694\ud55c \ub9cc\ud07c\uc529\ub9cc page\ub2e8\uc704\ub85c \uba54\ubaa8\ub9ac\ub85c \uc77d\uc5b4\uc640 \uc0ac\uc6a9\ud558\ub294 \ubc29\uc2dd\uc774 \uad6c\ud604\ub418\uae30\ub3c4 \ud55c\ub2e4. <br><\/p>\n\n\n\n<p> Android\uc5d0\uc11c\ub294 SQLite\uc5d0\uc11c \ucffc\ub9ac\ub97c \uc0ac\uc6a9\ud560 \ub54c &#8216;limit&#8217;\ubb38\uc744 \uc774\uc6a9\ud558\uac70\ub098 \uc9c1\uc811 \uad6c\ud604\ud574\uc11c \uc368\uc624\ub2e4\uac00, Android Jetpack\uc758 \uc77c\ubd80\ub85c \uc774\ub97c \uc9c0\uc6d0\ud558\ub294<a href=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/paging\/v3-overview\"> Paging library<\/a>\uac00 \ub098\uc624\uac8c \ub418\uc5c8\ub2e4. \ud604\uc7ac\ub294 Paging 3 \uae4c\uc9c0 \ub098\uc640\uc788\uace0, Room\uc5d0\uc11c\ub3c4 \uc774\ub97c \uc9c0\uc6d0\ud55c\ub2e4. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">SQLite\uc758 Paging<\/h3>\n\n\n\n<p>\uc548\ub4dc\ub85c\uc774\ub4dc\uc5d0\uc11c SQLite\uc758 SQLiteCursor\ub97c \uc774\uc6a9\ud55c paging\uc758 \uacbd\uc6b0 \ubc1c\uc0dd\uac00\ub2a5\ud55c \uc5ec\ub7ec \ubb38\uc81c\uc810\ub4e4\uc744 <a href=\"https:\/\/medium.com\/androiddevelopers\/large-database-queries-on-android-cb043ae626e8\">\uc548\ub4dc\ub85c\uc774\ub4dc \uac1c\ubc1c \uacf5\uc2dd \ube14\ub85c\uadf8 \uae00<\/a> \uc5d0\uc11c \uc9c0\uc801\ud558\uace0\uc788\ub2e4. 2017\ub144\ub3c4 \uae00\uc774\uace0 \ud604\uc7ac\ub294 \uac1c\uc120\uc774 \ub9ce\uc774\ub418\uc11c \uc774\uc640 \uac19\uc9c0\ub294 \uc54a\ub2e4\uace0 \ud558\uc9c0\ub9cc \uc5ec\uc804\ud788 \ub3c4\uc6c0\uc774 \ub418\ub294 \ub0b4\uc6a9\uc73c\ub85c \ubcf4\uc778\ub2e4. \uc774 \uae00\uc758 \ub0b4\uc6a9\uc744 \uc870\uae08 \uc0b4\ud3b4\ubcf4\uaca0\ub2e4.<\/p>\n\n\n\n<p> SQLite\ub97c \uc9c1\uc811 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0\ub97c \uc0b4\ud3b4\ubcf4\uba74, \ucffc\ub9ac\ub97c \ub0a0\ub838\uc744 \ub54c \uacb0\uacfc\uac12\uc758 \ub370\uc774\ud130\uac00 \ub9ce\uc740\uacbd\uc6b0 \uc804\uccb4\ub97c \uba54\ubaa8\ub9ac\uc5d0 \uc62c\ub9b4 \uc218 \uc5c6\uae30 \ub54c\ubb38\uc5d0 CursorWindow\ub77c\ub294\uac78 \uc774\uc6a9\ud55c\ub2e4. \ud604\uc7ac \uc0ac\uc6a9\uc911\uc778 CursorWindow\ub97c \uba54\ubaa8\ub9ac\uc5d0 \uc62c\ub9ac\uace0 \uc0ac\uc6a9\ud558\ub2e4\uac00 \ucc38\uc870\ud558\ub294 \ub370\uc774\ud130\uac00 CursorWindow\uc5d0 \uc5c6\uc744 \ub54c, \uc0c8\ub85c refresh\ub418\ub294 \uc2dd\uc774\ub2e4. \uc6b0\ub9ac\uac00 \ucffc\ub9ac\uc5d0\uc11c \ub9ac\ud134\ubc1b\ub294 SQLiteCursor\ub294 \uc774 \uba54\ubaa8\ub9ac\uc0c1\uc758 CursorWindow\uc758 \ud55c \ubd80\ubd84\uc744 \uac00\ub9ac\ud0a4\uac8c \ub41c\ub2e4. \uc774\uac83\uc740 \uc55e\uc11c \uc598\uae30\ud55c Paging \uae30\ubc95\uc5d0 \ud574\ub2f9\ud558\ub294\ub370, CursorWindow\uac00 \uace0\uc815\ub41c \ud06c\uae30\uc758 page\uc5d0 \ud574\ub2f9\ud55c\ub2e4.<\/p>\n\n\n\n<p> \uc774\uc5d0\ub300\ud574 \uba87\uac00\uc9c0 \ubb38\uc81c\uc810\ub4e4\uc774 \ub098\uc5f4\ub418\uc5b4 \uc788\ub294\ub370, \ub300\ucda9 \uc0b4\ud3b4\ubcf4\uba74 CursorWindow\uac00 refresh\ub420 \ub54c, \ub9c8\uce58 \ub9c1\ud06c\ub4dc \ub9ac\uc2a4\ud2b8\uc758 \ub370\uc774\ud130\ub97c \uac00\uc838\uc624\ub294 \uac83\ucc98\ub7fc, \ub9e4\ubc88 \ucc98\uc74c\ubd80\ud130 \uc77d\uc73c\uba74\uc11c \uc6d0\ud558\ub294 CursorWindow\uc758 \uc704\uce58\uae4c\uc9c0 DB\uc758 row\ub4e4\uc744 \uc2a4\ud0b5\ud558\uac8c \ub418\uc5b4\uc788\ub2e4\uace0 \ud55c\ub2e4. SQLiteCursor\uac00 query\ub97c resume\ud558\ub294 \uae30\ub2a5\uc774 \uc5c6\uc5b4\uc11c\ub77c\uace0 \ud558\ub294\ub370, \ub370\uc774\ud130\uac00 \ub9ce\uc744\uc218\ub85d \uc2dc\uac04\uc774 \ub9ce\uc774 \uc18c\ubaa8\ub420 \uac83\uc774\ub2e4. \uc774\ub294 SQLiteCursor.getCount()\ub97c \ubd80\ub97c \ub54c\uc5d0\ub3c4 \uc720\uc0ac\ud558\uac8c \uc804\uccb4 \ub9ac\uc2a4\ud2b8\uac00 \uc2a4\uce94\ub41c\ub2e4\uace0 \ud55c\ub2e4. <\/p>\n\n\n\n<p> \ub610\ub2e4\ub978 \ubb38\uc81c\ub85c, \uc0ac\uc6a9\ud55c Cursor\ub294 close()\uac00 \ub418\uc5b4\uc57c \ud55c\ub2e4. \uc774\ub294 \uc804\uc801\uc73c\ub85c \uac1c\ubc1c\uc790\uc5d0\uac8c \ub2ec\ub824\uc788\ub294 \ucc45\uc784\uc774\ub2e4. \ub610\ud55c, SQLiteCursor\ub294 \uc0ac\uc6a9\uc911 \ub370\uc774\ud130\uc758 \ubcc0\uacbd\uc5d0 \ub300\ud574 \uc54c\uc9c0 \ubabb\ud55c\ub2e4. \ub2e4\ub978\uac74 \ubb34\uc2dc\ud558\uace0 \uc0ac\uc6a9\ud55c\ub2e4\uace0 \ud574\ub3c4, \uc774\uac74 \uc880 \ubb38\uc81c\uac00 \ub420 \uc218 \uc788\ub2e4. <\/p>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-red-color\">\uc774\ub7ec\ud55c \ubb38\uc81c\ub4e4\uc758 workarround \ud574\uacb0\ucc45\uc73c\ub85c \uc81c\uc2dc\ub418\ub294\uac8c small queries\uc774\ub2e4. CursorWindow\ud558\ub098\ub85c \ucc98\ub9ac\ub420\ub9cc\ud55c \uc591\ub9cc\ud07c\uc529 \ucffc\ub9ac\ub97c \ud558\uac8c\ub418\uba74 \ubb38\uc81c\uac00 \uc5c6\ub2e4\ub294 \uac83. Room\uc744 \ub9e4\uc6b0 \uc120\ud638\ud558\ub294 \uc774\uc720\uac00 \uc774\uac83\uc774\ub77c\uace0 \ud558\ub2c8, Room\uc758 \uc790\uc138\ud55c \uad6c\ud604\ub0b4\uc6a9\uc740 \ubaa8\ub974\uaca0\uc9c0\ub9cc, \uc774\ub7f0 \ubd80\ubd84\ub4e4\uc774 \uace0\ub824\uac00\ub418\uc5b4 \uc788\ub2e4\uace0 \ubcf4\uc778\ub2e4. small query\uc758 \ub2e8\uc810\uc740 \uba85\ud655\ud55c\ub370, CursorWindow\ud558\ub098\ub9cc \uc0ac\uc6a9\ud558\uae30 \ub54c\ubb38\uc5d0 paging\uc758 \uae30\ub2a5\uc744 \uc81c\ub300\ub85c \ubc1c\ud718\ud558\uc9c0 \ubabb\ud55c\ub2e4\ub294 \uac83\uc774\ub2e4. \ub370\uc774\ud130\uac00 \ub9ce\uc544\uc9c0\uba74, query\uc758 \uc218\uac00 \ub9ce\uc544\uc9c0\ub294 \uac70\ub2c8\uae4c. \uadf8\ub798\uc11c \uad8c\uc7a5\ud558\ub294\uac8c \uc9c1\uc811 \ucee8\ud2b8\ub864 \uac00\ub2a5\ud55c Paging Library + Room \uc758 \uc870\ud569\uc774\ub2e4.<\/span><\/strong> <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Android Jetpack: Paging Library<\/h3>\n\n\n\n<p>\uc548\ub4dc\ub85c\uc774\ub4dc paging library\ub294 version 2\ub97c \uc9c0\ub098 3\uae4c\uc9c0 \uc654\ub2e4. <a href=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/paging\/v3-overview\">\uacf5\uc2dd \uac00\uc774\ub4dc\ubb38\uc11c<\/a>\ub97c \ub2f9\uc5f0\ud788 \ubcf4\ub294\uac8c \uc88b\ub2e4. \uc5ec\uae30\uc11c\ub294 \uc774 \uacf5\uc2dd \uac00\uc774\ub4dc \ubb38\uc11c\ub97c \uc911\uc2ec\uc73c\ub85c \uc815\ub9ac\ud574\ubcfc \uc0dd\uac01\uc774\ub2e4. \ub9ce\uc740 \ub370\uc774\ud130\ub97c \ud654\uba74\uc5d0 \ubfcc\ub9ac\ub294 \uacbd\uc6b0, RecyclerView\ub97c \uc0ac\uc6a9\ud560\ud150\ub370, \uc774 \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\uba74 \uc774\ub97c \uc704\ud55c Adapter\ub3c4 \uc81c\uacf5\ud55c\ub2e4. <\/p>\n\n\n\n<p> \uba3c\uc800, \ub77c\uc774\ube0c\ub7ec\ub9ac \uc0ac\uc6a9\uc744 \uc704\ud574 gradle\ud30c\uc77c\uc5d0 \ucd94\uac00\uac00 \ud544\uc694\ud558\ub2e4. \ub77c\uc774\ube0c\ub7ec\ub9ac \uc815\ubcf4\ub294 Jetpack\uc758 <a href=\"https:\/\/developer.android.com\/jetpack\/androidx\/releases\/paging?hl=ko\">\ud574\ub2f9 \ub77c\uc774\ube0c\ub7ec\ub9ac \ud398\uc774\uc9c0\ub97c \ucc38\uc870<\/a>\ud558\uc790.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>dependencies {\n\u00a0 def paging_version = \"3.0.0\"\n\n\u00a0 implementation \"androidx.paging:paging-runtime:$paging_version\"\n\n\u00a0 \/\/ alternatively - without Android dependencies for tests\n\u00a0 testImplementation \"androidx.paging:paging-common:$paging_version\"\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #DCBDFB\">dependencies<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">\u00a0 def paging_version <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #96D0FF\">&quot;3.0.0&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">\u00a0 implementation <\/span><span style=\"color: #96D0FF\">&quot;androidx.paging:paging-runtime:<\/span><span style=\"color: #6CB6FF\">$paging_version<\/span><span style=\"color: #96D0FF\">&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">\u00a0 <\/span><span style=\"color: #768390\">\/\/ alternatively - without Android dependencies for tests<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">\u00a0 testImplementation <\/span><span style=\"color: #96D0FF\">&quot;androidx.paging:paging-common:<\/span><span style=\"color: #6CB6FF\">$paging_version<\/span><span style=\"color: #96D0FF\">&quot;<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">\ub77c\uc774\ube0c\ub7ec\ub9ac \uae30\ubcf8\uad6c\uc870<\/h4>\n\n\n\n<p>paging \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 android app architecture\uc5d0 \ub9de\ucdb0\uc838\uc788\ub2e4. \uc774\uc5d0 \ub530\ub77c, Repository, ViewModel, UI \uc758 \uc138\uac1c \ub808\uc774\uc5b4\ubcc4\ub85c \ub4e4\uc5b4\uac00\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac \ucef4\ud3ec\ub10c\ud2b8\uac00 \uc874\uc7ac\ud55c\ub2e4.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"678\" height=\"165\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2021\/07\/paging3-library-architecture.png\" alt=\"\" class=\"wp-image-1023\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2021\/07\/paging3-library-architecture.png 678w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2021\/07\/paging3-library-architecture-300x73.png 300w\" sizes=\"auto, (max-width: 678px) 100vw, 678px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">1. Repository Layer<\/h4>\n\n\n\n<p> Repository\uc5d0\uc11c\ub294 \ub2e4\uc591\ud55c \ub370\uc774\ud130\uc18c\uc2a4\uc5d0 \ub530\ub77c \ub370\uc774\ud130\ub97c \uc81c\uacf5\ud55c\ub2e4. Paging \ub77c\uc774\ube0c\ub7ec\ub9ac\uc5d0\ub294 \uc774\uc5d0 \ud574\ub2f9\ud558\ub294 PagingSource \ud074\ub798\uc2a4\ub97c \uc81c\uacf5\ud558\ub2c8 \ubc14\ub85c \uc0ac\uc6a9\ud558\uac70\ub098 \uc0c1\uc18d\ubc1b\uc544 \uc0ac\uc6a9\uc774 \uac00\ub2a5\ud558\ub2e4. Room\uc744 \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0, @Query \uc5b4\ub178\ud14c\uc774\uc158\uc5d0 \ub9ac\ud134\uac12\uc73c\ub85c PagingSource\ub97c \uc9c0\uc6d0\ud558\uace0 \uc788\uc5b4 Room\uacfc \uc5f0\ub3d9\ub418\ub294 \uacbd\uc6b0\uc5d0 \uac04\ub2e8\ud788 \uc0ac\uc6a9\uac00\ub2a5\ud574\uc9c4\ub2e4. <\/p>\n\n\n\n<p> RemoteMediator\ub294 \ub124\ud2b8\uc6cd\uc758 \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\ub294 \ubaa8\ub4c8\uc774\ub2e4. \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\ub294 \uacbd\uc6b0, DB\ub97c Cache\ub85c \uc0dd\uac01\ud574\uc11c DB\uc5d0 \uc800\uc7a5\ud558\uace0 PagingSource\ub97c \ud1b5\ud574 DB\uc5d0 \uc800\uc7a5\ud55c \ub370\uc774\ud130\ub97c \uc81c\uacf5\ud558\ub3c4\ub85d \uac00\uc774\ub4dc\ud558\uace0 \uc788\ub2e4. \uc774\uac83\uc740 <a href=\"https:\/\/developer.android.com\/jetpack\/guide#truth\">Single source of truth\ub77c\uace0 \ud558\ub294 \uac00\uc774\ub4dc<\/a>\uc778\ub370, \ub370\uc774\ud130 \uc18c\uc2a4\uac00 \ub124\ud2b8\uc6cc\ud06c\uc640 DB \ub450\uac00\uc9c0\uac00 \ub418\ub294\uacbd\uc6b0, \uc11c\ub85c \ub2e4\ub978 \ub370\uc774\ud130\ub97c \uac00\uc9c0\uace0 \uc788\uc744 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0 \ub2e8\uc77c\ud55c \ub370\uc774\ud130\ub97c \uc81c\uacf5\ud558\uae30 \uc704\ud568\uc774\ub2e4. RemoteMediator\ub294 \uc544\uc9c1 experiment api\ub85c \ubcc0\uacbd \uac00\ub2a5\uc131\uc774 \uc788\ub2e4.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">2. ViewModel Layer<\/h4>\n\n\n\n<p> ViewModel Layer\uc5d0\uc11c\ub294 \uc2e4\uc81c\ub85c UI\uc5d0 \ubcf4\uc5ec\uc9c8 \ub370\uc774\ud130\ub97c \uc81c\uacf5\ud574\uc57c\ud55c\ub2e4. Repository\ub85c\ubd80\ud130 \ub370\uc774\ud130\ub97c \uac00\uc838\uc640 \uc81c\uacf5\ud558\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac \ubaa8\ub4c8\ub85c Pager \ud074\ub798\uc2a4\ub97c \uc81c\uacf5\ud558\uace0 \uc788\ub2e4. Pager \ud074\ub798\uc2a4\ub294 PagingConfig\ub97c \uc778\uc790\ub85c \ubc1b\ub294\ub370, PagingConfig\ub294 page size\ub098 \uae30\ud0c0 \uc124\uc815\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uac8c\ub41c\ub2e4. <br> Pager\ub294 \ucd5c\uc885\uc801\uc73c\ub85c Flow&lt;PagingData&gt;\ub97c \uc81c\uacf5\ud55c\ub2e4. PagingData\ub294 \uc5ed\uc2dc \ub77c\uc774\ube0c\ub7ec\ub9ac\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \ud074\ub798\uc2a4\ub85c \uc2e4\uc81c \ud55c page\uc5d0 \ud574\ub2f9\ud558\ub294 \ub370\uc774\ud130\ub97c \ub2f4\uace0 \uc788\ub2e4. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">3. UI Layer<\/h4>\n\n\n\n<p> UI \ub808\uc774\uc5b4\uc5d0\uc11c\ub294 ViewModel\uc5d0 \uc788\ub294 Flow&lt;PagingData&gt;\ub97c \uc9c1\uc811 \ud65c\uc6a9\ud560 \uc218\ub3c4 \uc788\uc73c\uba70, RecyclerView\ub97c \uc704\ud55c PagingDataAdapter\ub97c \uc81c\uacf5\ud558\ub2c8 \uc774\ub97c \uc0c1\uc18d\ubc1b\uc544 \uad6c\ud604\ud560 \uc218 \uc788\ub2e4. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"771\" height=\"57\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2020\/04\/seperator.png\" alt=\"seperator\" class=\"wp-image-381\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/04\/seperator.png 771w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/04\/seperator-300x22.png 300w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/04\/seperator-768x57.png 768w\" sizes=\"auto, (max-width: 771px) 100vw, 771px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Paging Library \uad6c\ud604 \uc608<\/h4>\n\n\n\n<p>\uc774\uc81c \uc2e4\uc81c\ub85c \uad6c\ud604\uc744 \ud574\ubcf4\uc790. RemoteMediator\ub294 \uc77c\uc744 \ubcf5\uc7a1\ud558\uac8c \ub9cc\ub4e4\uae30 \ub54c\ubb38\uc5d0 \uc77c\ub2e8 \ubc30\uc81c\ud558\uace0 \uc2dc\uc791\ud558\uc790. \uc55e\uc11c \uae30\ubcf8\uad6c\uc870\uc5d0\uc11c \ubd24\uaca0\uc9c0\ub9cc, PagingSource, Pager, PagingDataAdapter\uc758 \uad6c\ud604\uc774 \ud575\uc2ec\uc774\ub2e4. <\/p>\n\n\n\n<p>\uc5b4\ub5bb\uac8c \uad6c\ud604\ud558\ub294\uc9c0 \uae30\ubcf8\uc801\uc778 \uc124\uba85\uc740 <a href=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/paging\/v3-paged-data\">\uacf5\uc2dd \uac00\uc774\ub4dc\ubb38\uc11c<\/a>\uc5d0 \uc788\ub2e4. \ud558\uc9c0\ub9cc, \uc9c1\uc811 \uad6c\ud604\ud558\ub294\ub370 \ucc98\uc74c \ubcf4\uace0 \ub530\ub77c\ud558\uae30\ub294 \uc27d\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uc81c\uacf5\ub418\ub294 \uc0d8\ud50c\ucf54\ub4dc \ucc38\uc870\ud558\uac70\ub098, <a href=\"https:\/\/developer.android.com\/codelabs\/android-paging#0\">\ucf54\ub4dc\ub7a9\uc758 Android Paging<\/a>\uc744 \ubcf4\ub294\uac8c \ub3c4\uc6c0\uc774\ub41c\ub2e4. \uc0d8\ud50c\ucf54\ub4dc\ub294 <a href=\"https:\/\/github.com\/android\/architecture-components-samples\/tree\/main\/PagingSample\">database\ub9cc \uc774\uc6a9\ud558\ub294 \uac83<\/a>\uacfc <a href=\"https:\/\/github.com\/android\/architecture-components-samples\/tree\/main\/PagingWithNetworkSample\">\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\ub294 \uacbd\uc6b0<\/a> \ub450\uac00\uc9c0\uac00 \uc788\ub2e4.<\/p>\n\n\n\n<p> \uac00\uc7a5 \uc27d\uac8c \uc77d\ud788\ub294 <a href=\"https:\/\/github.com\/android\/architecture-components-samples\/tree\/main\/PagingSample\">database\ub97c \uc774\uc6a9\ud558\ub294 \uc0d8\ud50c\ucf54\ub4dc<\/a>\ub97c \uc0b4\ud3b4\ubcf4\ub3c4\ub85d \ud558\uc790. \uc55e\uc5d0\uc11c \ub9d0\ud588\ub4ef\uc774, PagingSource, Pager, PagingDataAdapter\uc758 \uad6c\ud604\uc744 \ubd10\uc57c\ud55c\ub2e4. \uadf8\ub7f0\ub370, \uc0d8\ud50c\ucf54\ub4dc\uc640 \uac19\uc774 Room\uc744 \uc774\uc6a9\ud558\ub294 \uacbd\uc6b0, Paging\uc744 \uc9c1\uc811 \uc9c0\uc6d0\ud558\uc5ec \ub9ac\ud134\uac12\uc73c\ub85c PagingSource\ub97c \uc9c0\uc6d0\ud55c\ub2e4. CheeseDao\ub97c \uc0b4\ud3b4\ubcf4\uba74, \uc544\ub798\uc640 \uac19\uc740 \ucf54\ub4dc\ub97c \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>@Dao\ninterface CheeseDao {\n    \/**\n     * Room knows how to return a LivePagedListProvider, from which we can get a LiveData and serve\n     * it back to UI via ViewModel.\n     *\/\n    @Query(\"SELECT * FROM Cheese ORDER BY name COLLATE NOCASE ASC\")\n    fun allCheesesByName(): PagingSource&lt;Int, Cheese>\n...<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F69D50\">@Dao<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">interface<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">CheeseDao<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #768390\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * Room knows how to return a LivePagedListProvider, from which we can get a LiveData and serve<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * it back to UI via ViewModel.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F69D50\">@Query<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #96D0FF\">&quot;SELECT * FROM Cheese ORDER BY name COLLATE NOCASE ASC&quot;<\/span><span style=\"color: #ADBAC7\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">allCheesesByName<\/span><span style=\"color: #ADBAC7\">(): <\/span><span style=\"color: #F69D50\">PagingSource<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Int<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Cheese<\/span><span style=\"color: #ADBAC7\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">..<\/span><span style=\"color: #ADBAC7\">.<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>\ucffc\ub9ac\uc758 \ub9ac\ud134\uac12\uc73c\ub85c PagingSource\ub97c \uc0ac\uc6a9\ud558\uace0 \uc788\ub294\ub370, PagingSource\ub97c \uc9c1\uc811 \uad6c\ud604\ud558\uc9c0 \uc54a\uace0, Room\uc5d0\uc11c \ucc98\ub9ac\ud574\uc8fc\uac8c \ub41c\ub2e4.<\/p>\n\n\n\n<p> PagingSource&lt;Key, Value&gt;\uc640 \uac19\uc774, Key, Value\uc758 \ub450 generic \ud0c0\uc785\uc744 \ubc1b\ub294\ub2e4. Key\uac00 page number\uc5d0 \ud574\ub2f9\ud558\ub294 \uac12\uc774\uace0, Value\ub294 \ub2e4\ub8e8\ub294 \ub370\uc774\ud130\uc5d0 \ud574\ub2f9\ud55c\ub2e4. \uc55e\uc758 \ucf54\ub4dc\uc5d0\uc11c\ub294 Key\ub85c Int, Value\ub85c Cheese\uac00 \uc0ac\uc6a9\ub418\ub294\uac78 \ubcfc \uc218 \uc788\ub2e4.<\/p>\n\n\n\n<p>PagingSource\uac00 \uc900\ube44\ub410\uc73c\uba74, \ud544\uc694\ud560 \ub54c PagingSource\ub85c\ubd80\ud130 \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud558\ub294 Pager\ub97c \uad6c\ud604\ud574\uc57c \ud55c\ub2e4. Pager\ub294 ViewModel\uc548\uc5d0\uc11c \uad6c\ud604\ub41c\ub2e4. \uc608\uc81c\uc758 CheeseViewModel\uc744 \uc0b4\ud3b4\ubcf4\uc790. <\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>val allCheeses: Flow&lt;PagingData&lt;CheeseListItem>> = Pager(\n    config = PagingConfig(\n        \/**\n         * A good page size is a value that fills at least a few screens worth of content on a\n         * large device so the User is unlikely to see a null item.\n         * You can play with this constant to observe the paging behavior.\n         *\n         * It's possible to vary this with list device size, but often unnecessary, unless a\n         * user scrolling on a large device is expected to scroll through items more quickly\n         * than a small device, such as when the large device uses a grid layout of items.\n         *\/\n        pageSize = 60,\n\n        \/**\n         * If placeholders are enabled, PagedList will report the full size but some items might\n         * be null in onBind method (PagedListAdapter triggers a rebind when data is loaded).\n         *\n         * If placeholders are disabled, onBind will never receive null but as more pages are\n         * loaded, the scrollbars will jitter as new pages are loaded. You should probably\n         * disable scrollbars if you disable placeholders.\n         *\/\n        enablePlaceholders = true,\n\n        \/**\n         * Maximum number of items a PagedList should hold in memory at once.\n         *\n         * This number triggers the PagedList to start dropping distant pages as more are loaded.\n         *\/\n        maxSize = 200\n    )\n) {\n    dao.allCheesesByName()\n}.flow\n    .map { pagingData ->\n        pagingData\n            \/\/ Map cheeses to common UI model.\n            .map { cheese -> CheeseListItem.Item(cheese) }\n            .insertSeparators { before: CheeseListItem?, after: CheeseListItem? ->\n                if (before == null &amp;&amp; after == null) {\n                    \/\/ List is empty after fully loaded; return null to skip adding separator.\n                    null\n                } else if (after == null) {\n                    \/\/ Footer; return null here to skip adding a footer.\n                    null\n                } else if (before == null) {\n                    \/\/ Header\n                    CheeseListItem.Separator(after.name.first())\n                } else if (!before.name.first().equals(after.name.first(), ignoreCase = true)){\n                    \/\/ Between two items that start with different letters.\n                    CheeseListItem.Separator(after.name.first())\n                } else {\n                    \/\/ Between two items that start with the same letter.\n                    null\n                }\n            }\n    }\n    .cachedIn(viewModelScope)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> allCheeses: <\/span><span style=\"color: #F69D50\">Flow<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">PagingData<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">&gt;&gt; <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">Pager<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    config <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">PagingConfig<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * A good page size is a value that fills at least a few screens worth of content on a<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * large device so the User is unlikely to see a null item.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * You can play with this constant to observe the paging behavior.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * It&#39;s possible to vary this with list device size, but often unnecessary, unless a<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * user scrolling on a large device is expected to scroll through items more quickly<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * than a small device, such as when the large device uses a grid layout of items.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        pageSize <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">60<\/span><span style=\"color: #ADBAC7\">,<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * If placeholders are enabled, PagedList will report the full size but some items might<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * be null in onBind method (PagedListAdapter triggers a rebind when data is loaded).<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * If placeholders are disabled, onBind will never receive null but as more pages are<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * loaded, the scrollbars will jitter as new pages are loaded. You should probably<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * disable scrollbars if you disable placeholders.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        enablePlaceholders <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">true<\/span><span style=\"color: #ADBAC7\">,<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * Maximum number of items a PagedList should hold in memory at once.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * This number triggers the PagedList to start dropping distant pages as more are loaded.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        maxSize <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">200<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    )<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    dao.<\/span><span style=\"color: #DCBDFB\">allCheesesByName<\/span><span style=\"color: #ADBAC7\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}.flow<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    .<\/span><span style=\"color: #DCBDFB\">map<\/span><span style=\"color: #ADBAC7\"> { pagingData <\/span><span style=\"color: #F47067\">-&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        pagingData<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #768390\">\/\/ Map cheeses to common UI model.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            .<\/span><span style=\"color: #DCBDFB\">map<\/span><span style=\"color: #ADBAC7\"> { cheese <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> CheeseListItem.<\/span><span style=\"color: #DCBDFB\">Item<\/span><span style=\"color: #ADBAC7\">(cheese) }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            .<\/span><span style=\"color: #DCBDFB\">insertSeparators<\/span><span style=\"color: #ADBAC7\"> { before: <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">?, after: <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">? -&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (before <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">null<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">&amp;&amp;<\/span><span style=\"color: #ADBAC7\"> after <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">null<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #768390\">\/\/ List is empty after fully loaded; return null to skip adding separator.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #6CB6FF\">null<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (after <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">null<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #768390\">\/\/ Footer; return null here to skip adding a footer.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #6CB6FF\">null<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (before <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">null<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #768390\">\/\/ Header<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    CheeseListItem.<\/span><span style=\"color: #DCBDFB\">Separator<\/span><span style=\"color: #ADBAC7\">(after.name.<\/span><span style=\"color: #DCBDFB\">first<\/span><span style=\"color: #ADBAC7\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (<\/span><span style=\"color: #F47067\">!<\/span><span style=\"color: #ADBAC7\">before.name.<\/span><span style=\"color: #DCBDFB\">first<\/span><span style=\"color: #ADBAC7\">().<\/span><span style=\"color: #DCBDFB\">equals<\/span><span style=\"color: #ADBAC7\">(after.name.<\/span><span style=\"color: #DCBDFB\">first<\/span><span style=\"color: #ADBAC7\">(), ignoreCase <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">true<\/span><span style=\"color: #ADBAC7\">)){<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #768390\">\/\/ Between two items that start with different letters.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    CheeseListItem.<\/span><span style=\"color: #DCBDFB\">Separator<\/span><span style=\"color: #ADBAC7\">(after.name.<\/span><span style=\"color: #DCBDFB\">first<\/span><span style=\"color: #ADBAC7\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #768390\">\/\/ Between two items that start with the same letter.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                    <\/span><span style=\"color: #6CB6FF\">null<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    .<\/span><span style=\"color: #DCBDFB\">cachedIn<\/span><span style=\"color: #ADBAC7\">(viewModelScope)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Pager\uc758 \uc0dd\uc131\uacfc \ub3d9\uc2dc\uc5d0 flow \uc18d\uc131\uc744 \ucc38\uc870\ud558\uace0 \uc788\ub2e4. \uc6b0\uc120, \uc0dd\uc131\uc790\uc5d0 PagingConfig\uac00 \ubcf4\uc778\ub2e4. PagingConfig\ub294 \uba87\uac00\uc9c0 Paging \uac12\ub4e4\uc744 \uc9c0\uc815\ud574\uc8fc\ub294\ub370, pageSize\ub294 \ud55c\ubc88\uc5d0 \uc77d\uc5b4\uc62c \uc544\uc774\ud15c \uac1c\uc218\ub97c \ub9d0\ud55c\ub2e4. enablePlaceHolder\ub294 \ub85c\ub529\uc2dc, \ub370\uc774\ud130\uac00 \uc544\uc9c1 \uc5c6\uc744 \ub54c null\uc0c1\ud0dc\uc758 placeholder\ub97c \uc0ac\uc6a9\ud558\ub294\uc9c0 \uc5ec\ubd80\uc774\ub2e4.  maxSize\ub294 PagingData\uc758 \ucd5c\ub300 \ud06c\uae30\ub97c \ub9d0\ud55c\ub2e4. \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc62c \ub54c pageSize\ub9cc\ud07c\uc529 \uc77d\uc5b4\uc640\uc11c PagingData\uc5d0 \uc313\uc774\uac8c\ub418\ub294\ub370, \uc9c0\uc815\ud55c \ucd5c\ub300\ud06c\uae30\ubcf4\ub2e4 \ucee4\uc9c0\uba74 \ub370\uc774\ud130\ub97c \uba54\ubaa8\ub9ac\uc5d0\uc11c \ub4dc\ub86d\uc2dc\ud0a8\ub2e4. \uc608\uc81c\uc5d0\uc11c 200\uc73c\ub85c \uc9c0\uc815\ub418\uc5b4 \uc788\uc73c\ubbc0\ub85c, 60\uc529 4\ubc88\uc758 \ub85c\ub529\uc774 \uc77c\uc5b4\ub098\uba74 \uc544\ub9c8\ub3c4 \ub370\uc774\ud130\uc758 drop\uc774 \ubc1c\uc0dd\ud560 \uac83\uc774\ub2e4.  \uc774\uac12\uc744 MAX_SIZE_UNBOUNDED\ub85c \uc124\uc815\ud558\uba74 drop\uc5c6\uc774 \uacc4\uc18d \uc313\uc774\uac8c\ub41c\ub2e4. maxSize\uc5d0 \ub300\ud55c \ubcf4\ub2e4 \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/paging\/PagingConfig#maxSize()\">\uacf5\uc2dd \ub808\ud37c\ub7f0\uc2a4<\/a>\ub97c \ucc38\uace0\ud558\uc790.<\/p>\n\n\n\n<p><a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/paging\/Pager#summary\">Pager\uc758 \ub808\ud37c\ub7f0\uc2a4<\/a>\ub97c \ubcf4\uba74 \uc0dd\uc131\uc790\uc758 \uc815\uc758\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>&lt;Key : Any, Value : Any> Pager(\n    config: PagingConfig,\n    initialKey: Key?,\n    pagingSourceFactory: () -> PagingSource&lt;Key, Value>\n)\n&lt;Key : Any, Value : Any> Pager(\n    config: PagingConfig,\n    initialKey: Key?,\n    remoteMediator: RemoteMediator&lt;Key, Value>?,\n    pagingSourceFactory: () -> PagingSource&lt;Key, Value>\n)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">&lt;<\/span><span style=\"color: #ADBAC7\">Key : <\/span><span style=\"color: #F69D50\">Any<\/span><span style=\"color: #ADBAC7\">, Value : <\/span><span style=\"color: #F69D50\">Any<\/span><span style=\"color: #F47067\">&gt;<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">Pager<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    config: <\/span><span style=\"color: #F69D50\">PagingConfig<\/span><span style=\"color: #ADBAC7\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    initialKey: <\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">?,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    pagingSourceFactory: () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> PagingSource<\/span><span style=\"color: #F47067\">&lt;<\/span><span style=\"color: #ADBAC7\">Key, Value<\/span><span style=\"color: #F47067\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">&lt;<\/span><span style=\"color: #ADBAC7\">Key : <\/span><span style=\"color: #F69D50\">Any<\/span><span style=\"color: #ADBAC7\">, Value : <\/span><span style=\"color: #F69D50\">Any<\/span><span style=\"color: #F47067\">&gt;<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">Pager<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    config: <\/span><span style=\"color: #F69D50\">PagingConfig<\/span><span style=\"color: #ADBAC7\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    initialKey: <\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">?,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    remoteMediator: <\/span><span style=\"color: #F69D50\">RemoteMediator<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Value<\/span><span style=\"color: #ADBAC7\">&gt;?,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    pagingSourceFactory: () <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> PagingSource<\/span><span style=\"color: #F47067\">&lt;<\/span><span style=\"color: #ADBAC7\">Key, Value<\/span><span style=\"color: #F47067\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>\ub450\uac00\uc9c0 \uc0dd\uc131\uc790\uac00 \uc788\ub294\ub370, RemoteMediator\uc758 \uc0ac\uc6a9 \uc720\ubb34 \ucc28\uc774\uac00 \uc788\ub2e4. \uc0dd\uc131\uc790\ub97c \ubcf4\uba74, pagingSouceFactory\ub85c lambda \ud45c\ud604\uc2dd\uc774 \ub4e4\uc5b4\uac00\uac8c \ub418\uc5b4\uc788\ub294\ub370, lambda\uc758 \ub9ac\ud134\uac12\uc740 PagingSouce\uc774\ub2e4. \uc608\uc81c\uc5d0\uc120 lambda\ud45c\ud604\uc2dd\uc5d0 dao.allCheeseByName()\uc744 \uc0ac\uc6a9\ud558\uc5ec, Room\uc5d0\uc11c \ub9ac\ud134\ub418\ub294 PagingSouce\ub97c \ub9ac\ud134\ud558\uac8c \uad6c\ud604\ub418\uc5c8\ub2e4. <\/p>\n\n\n\n<p>Pager\ub97c \uc0dd\uc131\ud55c \ud6c4\uc5d0\ub294 \ubc14\ub85c \uc5f0\uacb0\ud574\uc11c flow \uc18d\uc131\uc744 \uc0ac\uc6a9\ud558\uc5ec \uac12\uc744 \ubcc0\ud658\ud574\uc8fc\uace0 \uc788\ub2e4. \uac12\uc758 \ubcc0\ud658\uacfc\uc815\uc744 \uc0b4\ud3b4\ubcf4\uba74,<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>.map{ cheese -> CheeseListItem.Item(cheese) }<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #ADBAC7\">.<\/span><span style=\"color: #DCBDFB\">map<\/span><span style=\"color: #ADBAC7\">{ cheese <\/span><span style=\"color: #F47067\">-&gt;<\/span><span style=\"color: #ADBAC7\"> CheeseListItem.<\/span><span style=\"color: #DCBDFB\">Item<\/span><span style=\"color: #ADBAC7\">(cheese) }<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>map\uc744 \uc774\uc6a9\ud574\uc11c PagingData&lt;cheese&gt; \ub97c PagingData&lt;CheeseListItem.Item(cheese)&gt;\ub85c \ubc14\uafd4\uc900\ub2e4. CheeseListItem\uc744 \uc0b4\ud3b4\ubcf4\uba74 \ub2e4\uc74c\uacfc \uac19\uc740\ub370,<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>sealed class CheeseListItem(val name: String) {\n    data class Item(val cheese: Cheese) : CheeseListItem(cheese.name)\n    data class Separator(private val letter: Char) : CheeseListItem(letter.toUpperCase().toString())\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">sealed<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> name: <\/span><span style=\"color: #F69D50\">String<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">data<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">Item<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> cheese: <\/span><span style=\"color: #F69D50\">Cheese<\/span><span style=\"color: #ADBAC7\">) : <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F69D50\">cheese<\/span><span style=\"color: #ADBAC7\">.<\/span><span style=\"color: #F69D50\">name<\/span><span style=\"color: #ADBAC7\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">data<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">Separator<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #F47067\">private<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> letter: <\/span><span style=\"color: #F69D50\">Char<\/span><span style=\"color: #ADBAC7\">) : <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">(letter.<\/span><span style=\"color: #DCBDFB\">toUpperCase<\/span><span style=\"color: #ADBAC7\">().<\/span><span style=\"color: #DCBDFB\">toString<\/span><span style=\"color: #ADBAC7\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>sealed class\ub97c \uc0c1\uc18d\ubc1b\uc544 data class\ub97c \ub9cc\ub4e4\uba74, enum\uc744 \ub300\uccb4\ud574\uc11c \uc0ac\uc6a9\ud560 \uc218 \uc788\ub2e4. \uc774\uc5d0 \uad00\ud574\uc11c\ub294 <a href=\"http:\/\/batmask.dothome.co.kr\/index.php\/2021\/06\/29\/869\/\">\uc774\uc804 \ud3ec\uc2a4\ud305, Sealed class vs Enum\uc744 \ucc38\uc870<\/a>. \uc774\uac78 \uc0ac\uc6a9\ud558\ub294 \uc774\uc720\uac00 \ubc14\ub85c \ub098\uc624\ub294\ub370, \ub2e4\uc74c\uc904\uc744 \ubcf4\uba74, insertSeparators()\ub97c \uc0ac\uc6a9\ud574\uc11c \uc54c\ud30c\ubcb3 \ud5e4\ub354\ubaa8\uc591\uc758 separator\ub97c \ucd94\uac00\ud558\uace0 \uc788\ub2e4. \ubc14\ub85c recyclerview\uc5d0 \uc77c\ubc18 \uc544\uc774\ud15c\uacfc separator\ub97c \uac19\uc774 \ud45c\uc2dc\ud558\uae30 \uc704\ud568\uc774\ub2e4. <\/p>\n\n\n\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c,<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>...\n.cachedIn(viewModelScope)<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">..<\/span><span style=\"color: #ADBAC7\">.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">.<\/span><span style=\"color: #DCBDFB\">cachedIn<\/span><span style=\"color: #ADBAC7\">(viewModelScope)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>\uc704\uc640 \uac19\uc774 cachedIn() \uc744 \ubd88\ub7ec\uc900\ub2e4. \uc774 \ud568\uc218\uc758 \uc5ed\ud560\uc740 PagingData\ub97c \uc778\uc790\ub85c \ubc1b\uc740 scope\ub0b4\uc5d0\uc11c Flow \ub370\uc774\ud130\ub97c cach\uc5d0 \uc800\uc7a5\ud55c\ub2e4. \uadf8\ub7ec\ub2c8\uae4c \uc704\uc640\uac19\uc774 viewModelScope\ub85c \uc9c0\uc815\ud558\uba74, Flow\ub97c \ucc38\uc870\ud558\ub294 Activity\uac00 \ud654\uba74 \ub85c\ud14c\uc774\uc158\uc5d0 \uc758\ud574 destroy \ub410\ub2e4\uac00 \ub2e4\uc2dc create\ub418\ub3c4 ViewModel\uc5d0 \ub370\uc774\ud130\uac00 \uc720\uc9c0\ub41c\ub2e4\ub294 \uc598\uae30\ub2e4. \ub610 \ud55c\uac00\uc9c0 \uc5ed\ud560\uc740 \uc5ec\ub7ec\uacf3\uc5d0\uc11c \ub370\uc774\ud130\ub97c \ud544\uc694\ub85c \ud560 \ub54c, \uc250\uc5b4\uac00 \uac00\ub2a5\ud558\ub3c4\ub85d \ub9cc\ub4e4\uc5b4\uc900\ub2e4. <\/p>\n\n\n\n<p>\uc774\ub807\uac8c\ud574\uc11c ViewModel\uc5d0 Flow&lt;PagingData&lt;CheeseListItem&gt;&gt;\uc774 \uc900\ube44\ub418\uc5c8\ub2e4. \ub2e4\ub9cc, Flow\ub294 Channel\uacfc \ub300\ube44\ud574\uc11c cold stream\uc774\ub77c\uace0 \ub9d0\ud55c\ub2e4. \uc774 \uc758\ubbf8\ub294 sequence\uc640 \ub9c8\ucc2c\uac00\uc9c0\ub85c \uc0ac\uc6a9\uc790\uac00 \uc9c1\uc811 collect()\ub97c \ud638\ucd9c\ud560 \ub54c, \ub370\uc774\ud130\ub97c \uac00\uc838\uc628\ub2e4. Flow\ub97c \uc0dd\uc131\ud588\ub2e4\uace0 \ubd88\ud544\uc694\ud55c \uc77c\uc774 \uc77c\uc5b4\ub098\uc9c0 \uc54a\ub294 \uac83\uc774\ub2e4. \uadf8\ub7ec\ubbc0\ub85c \uc774\ub97c \uc791\ub3d9\uc2dc\ud0a4\uae30 \uc704\ud574\uc11c UI \ub808\uc774\uc5b4\uc5d0\uc11c collectLatest()\ub97c \ubd88\ub7ec\uc900\ub2e4. collectLatest()\uac00 collect()\uc640 \ub2e4\ub978\uc810\uc740, \uc0c8\ub85c\uc6b4 \uac12\uc774 emit() \ub418\uba74 \uc9c4\ud589\ud558\ub358 collectLatest() \ube14\ub7ed\uc744 cancel\ud558\uace0 \ub2e4\uc2dc \ucc98\uc74c\ubd80\ud130 \uc2e4\ud589\ub41c\ub2e4. <a href=\"https:\/\/kotlin.github.io\/kotlinx.coroutines\/kotlinx-coroutines-core\/kotlinx.coroutines.flow\/collect-latest.html\">Kotlin\uc758 collectLatest() \ubb38\uc11c<\/a>\ub97c \ubcf4\uba74 \uac04\ub2e8\ud55c \uc608\uc81c\uac00 \uc788\uc73c\ub2c8  \uc774\ud574\uac00 \ub420 \uac83\uc774\ub2e4.<\/p>\n\n\n\n<p>UI \ub808\uc774\uc5b4\uc5d0\uc11c \uba3c\uc800 CheeseAdapter \ud074\ub798\uc2a4\ub97c \uc0b4\ud3b4\ubcf4\uc790. <\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>class CheeseAdapter : PagingDataAdapter&lt;CheeseListItem, CheeseViewHolder>(diffCallback) {\n...<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">CheeseAdapter<\/span><span style=\"color: #ADBAC7\"> : <\/span><span style=\"color: #F69D50\">PagingDataAdapter<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">CheeseViewHolder<\/span><span style=\"color: #ADBAC7\">&gt;(<\/span><span style=\"color: #F69D50\">diffCallback<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">..<\/span><span style=\"color: #ADBAC7\">.<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Paging Library\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 RecyclerView\uc6a9 adapter\uc778 PagingDataAdapter\ub97c \uc0c1\uc18d\ubc1b\uc544 \uad6c\ud604\ud558\uace0 \uc788\ub2e4. PagingDataAdapter\ub294 generic \uc778\uc790\ub85c recyclerview\uc5d0\uc11c \uc0ac\uc6a9\ud560 \uc544\uc774\ud15c\uacfc viewholer\ub97c \ubc1b\ub294\ub2e4. \ucd94\uac00\ub85c diffCallback\uc744 \uc778\uc790\ub85c \ubc1b\uace0 \uc788\ub294\ub370, \ucd94\uac00\ub418\ub294 \uc544\uc774\ud15c\uc744 \uae30\uc874 \uc544\uc774\ud15c\uacfc \ube44\uad50\ud574\uc11c \uc2e4\uc81c\ub85c UI\uc5d0 \ucd94\uac00\ud560 \ub370\uc774\ud130\ub97c \uad6c\ubd84\ud558\ub294 \uc5ed\ud560\uc744 \ud55c\ub2e4. diffCallback\uc774 \uc5b4\ub5bb\uac8c \uad6c\ud604\ub418\uc5b4 \uc788\ub294\uc9c0 \uc0b4\ud3b4\ubcf4\uc790. <\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>companion object {\n    \/**\n     * This diff callback informs the PagedListAdapter how to compute list differences when new\n     * PagedLists arrive.\n     *\n     * When you add a Cheese with the 'Add' button, the PagedListAdapter uses diffCallback to\n     * detect there's only a single item difference from before, so it only needs to animate and\n     * rebind a single view.\n     *\n     * @see DiffUtil\n     *\/\n    val diffCallback = object : DiffUtil.ItemCallback&lt;CheeseListItem>() {\n        override fun areItemsTheSame(oldItem: CheeseListItem, newItem: CheeseListItem): Boolean {\n            return if (oldItem is CheeseListItem.Item &amp;&amp; newItem is CheeseListItem.Item) {\n                oldItem.cheese.id == newItem.cheese.id\n            } else if (oldItem is CheeseListItem.Separator &amp;&amp; newItem is CheeseListItem.Separator) {\n                oldItem.name == newItem.name\n            } else {\n                oldItem == newItem\n            }\n        }\n\n        \/**\n         * Note that in kotlin, == checking on data classes compares all contents, but in Java,\n         * typically you'll implement Object#equals, and use it to compare object contents.\n         *\/\n        override fun areContentsTheSame(oldItem: CheeseListItem, newItem: CheeseListItem): Boolean {\n            return oldItem == newItem\n        }\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">companion<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">object<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #768390\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * This diff callback informs the PagedListAdapter how to compute list differences when new<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * PagedLists arrive.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     *<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * When you add a Cheese with the &#39;Add&#39; button, the PagedListAdapter uses diffCallback to<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * detect there&#39;s only a single item difference from before, so it only needs to animate and<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * rebind a single view.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     *<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     * <\/span><span style=\"color: #F47067\">@see<\/span><span style=\"color: #768390\"> DiffUtil<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">     *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> diffCallback <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> object : <\/span><span style=\"color: #F69D50\">DiffUtil<\/span><span style=\"color: #ADBAC7\">.<\/span><span style=\"color: #DCBDFB\">ItemCallback<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">&gt;() {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">override<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">areItemsTheSame<\/span><span style=\"color: #ADBAC7\">(oldItem: <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">, newItem: <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">): <\/span><span style=\"color: #F69D50\">Boolean<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (oldItem <\/span><span style=\"color: #F47067\">is<\/span><span style=\"color: #ADBAC7\"> CheeseListItem.Item <\/span><span style=\"color: #F47067\">&amp;&amp;<\/span><span style=\"color: #ADBAC7\"> newItem <\/span><span style=\"color: #F47067\">is<\/span><span style=\"color: #ADBAC7\"> CheeseListItem.Item) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                oldItem.cheese.id <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> newItem.cheese.id<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (oldItem <\/span><span style=\"color: #F47067\">is<\/span><span style=\"color: #ADBAC7\"> CheeseListItem.Separator <\/span><span style=\"color: #F47067\">&amp;&amp;<\/span><span style=\"color: #ADBAC7\"> newItem <\/span><span style=\"color: #F47067\">is<\/span><span style=\"color: #ADBAC7\"> CheeseListItem.Separator) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                oldItem.name <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> newItem.name<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                oldItem <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> newItem<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/**<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * Note that in kotlin, == checking on data classes compares all contents, but in Java,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         * typically you&#39;ll implement Object#equals, and use it to compare object contents.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">         *\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">override<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">areContentsTheSame<\/span><span style=\"color: #ADBAC7\">(oldItem: <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">, newItem: <\/span><span style=\"color: #F69D50\">CheeseListItem<\/span><span style=\"color: #ADBAC7\">): <\/span><span style=\"color: #F69D50\">Boolean<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> oldItem <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> newItem<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>DiffUtil.ItemCallback\uc744 \uc774\uc6a9\ud558\uc5ec areItemsTheSame(), areContentsTheSame() \ub450 \uba54\uc18c\ub4dc\ub97c \uc624\ubc84\ub77c\uc774\ub4dc\ud574\uc11c \uad6c\ud604\ud574\uc8fc\uace0 \uc788\ub2e4. \ub0b4\uc6a9\uc740 \ud06c\uac8c \uc5b4\ub824\uc6b8\uac8c \uc5c6\uc744\uac70\ub77c \uc0dd\uac01\ub41c\ub2e4. <\/p>\n\n\n\n<p>\uc900\ube44\ub294 \ubaa8\ub450 \ub05d\ub0ac\uace0, \uc774\uc81c adapter\uc640 viewmodel\uc5d0 Pager\ub97c \uc774\uc6a9\ud574 \uc900\ube44\ub41c Flow\ub97c \uc5f0\uacb0\ud558\uae30\ub9cc \ud558\uba74 \ub41c\ub2e4. MainActivity\ub97c \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/\/ Subscribe the adapter to the ViewModel, so the items in the adapter are refreshed\n\/\/ when the list changes\nlifecycleScope.launch {\n    viewModel.allCheeses.collectLatest { adapter.submitData(it) }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #768390\">\/\/ Subscribe the adapter to the ViewModel, so the items in the adapter are refreshed<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\">\/\/ when the list changes<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">lifecycleScope.<\/span><span style=\"color: #DCBDFB\">launch<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    viewModel.allCheeses.<\/span><span style=\"color: #DCBDFB\">collectLatest<\/span><span style=\"color: #ADBAC7\"> { adapter.<\/span><span style=\"color: #DCBDFB\">submitData<\/span><span style=\"color: #ADBAC7\">(it) }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>\ucf54\ub8e8\ud2f4\uc744 \uc0dd\uc131\ud574\uc11c \uc2e4\ud589\ud558\ub294\ub370, viewmodel\uc5d0 \uc900\ube44\ub41c flow\uc5d0 collectLatest\ub85c \uac12\uc744 \uac00\uc838\uc628\ub2e4. \uc774\ub807\uac8c \uac00\uc838\uc628 PagingData&lt;CheeseListItem&gt;\uc744 adapter.submitData()\ub97c \uc774\uc6a9\ud574 adapter\ub85c \ubcf4\ub0b4\uc900\ub2e4. <strong><span class=\"has-inline-color has-vivid-red-color\">\uc8fc\uc758\ud560\uc810\uc740 PagingDataAdapter.submitData\uac00 suspend fun \uc774\ub77c\ub294 \uc810\uc774\ub2e4. \uc77c\ud68c\uc131\uc73c\ub85c \uc885\ub8cc\ub418\uc9c0 \uc54a\uace0, \ub300\uae30\uc0c1\ud0dc\uac00 \ub418\uae30 \ub54c\ubb38\uc5d0, \uac12\uc774 \ubcc0\uacbd\ub418\ub294 \uacbd\uc6b0 collectLatest\uc758 \ud2b9\uc131\uc5d0 \ub530\ub77c \ub2e4\uc2dc \uc2e4\ud589\uc774 \ub41c\ub2e4. \uadf8\ub7ec\ubbc0\ub85c, \uc774 \ud55c\uc904\ub85c \ub370\uc774\ud130\uc5d0 \ub300\ud55c observing \uc0c1\ud0dc\uc5d0 \ub4e4\uc5b4\uac00\uc11c \uc9c0\uc18d\uc801\uc778 \ubcc0\uacbd\uc5d0 \ub300\ud574 \uac12\uc744 \uc77d\uc5b4\uc624\uac8c\ub41c\ub2e4<\/span><\/strong>. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Custom PagingSource \uc758 \uad6c\ud604<\/h4>\n\n\n\n<p> \uc55e\uc758 \uc608\uc5d0\uc120 PagingSource\ub97c Room\uc5d0\uc11c \uc81c\uacf5\ud558\ub294\uac78 \uc0ac\uc6a9\ud588\ub2e4. DB\ub9cc \uc0ac\uc6a9\uc2dc \uc774\uac83\uc73c\ub85c \ucda9\ubd84\ud558\uaca0\uc9c0\ub9cc, \uc0ac\uc6a9\uc790\uac00 \uc815\uc758\ud558\ub294 \uacbd\uc6b0\ub3c4 \uc54c\uc544\ubcf4\uc790. \uc608\uc81c\ub85c\ub294 <a href=\"https:\/\/developer.android.com\/codelabs\/android-paging?hl=en&amp;continue=https%3A%2F%2Fcodelabs.developers.google.com%2F#0\">\uad6c\uae00 \ucf54\ub4dc\ub7a9 Android Paging<\/a> \uc758 \uc608\ub97c \uc0b4\ud3b4\ubcf4\uc790. <br><a href=\"https:\/\/developer.android.com\/codelabs\/android-paging?hl=en&amp;continue=https%3A%2F%2Fcodelabs.developers.google.com%2F#4\">\uad6c\uae00 \ucf54\ub4dc\ub7a9 Android Paging \uc744 5\ub2e8\uacc4<\/a>\uae4c\uc9c0 \uc9c4\ud589\ud558\uba74 GithubPagingSource\ub97c \ub9cc\ub4dc\ub294 \ubd80\ubd84\uc774 \uc788\ub2e4. \ud574\ub2f9 \uc18c\uc2a4\ub97c \uc5bb\ub294 \ubc29\ubc95\uc740 \uadf8 <a href=\"https:\/\/developer.android.com\/codelabs\/android-paging?hl=en&amp;continue=https%3A%2F%2Fcodelabs.developers.google.com%2F#1\">\ucf54\ub4dc\ub7a9 2\ub2e8\uacc4<\/a>\uc5d0 \uc124\uba85\uc774 \ub098\uc640\uc788\ub2e4. \uadf8\ub7fc GithubPagingSource\uc758 \ucf54\ub4dc\ub97c \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>private const val GITHUB_STARTING_PAGE_INDEX = 1\n\nclass GithubPagingSource(\n    private val service: GithubService,\n    private val query: String\n) : PagingSource&lt;Int, Repo>() {\n    override suspend fun load(params: LoadParams&lt;Int>): LoadResult&lt;Int, Repo> {\n        val position = params.key ?: GITHUB_STARTING_PAGE_INDEX\n        val apiQuery = query + IN_QUALIFIER\n        return try {\n            val response = service.searchRepos(apiQuery, position, params.loadSize)\n            val repos = response.items\n            val nextKey = if (repos.isEmpty()) {\n                null\n            } else {\n                \/\/ initial load size = 3 * NETWORK_PAGE_SIZE\n                \/\/ ensure we're not requesting duplicating items, at the 2nd request\n                position + (params.loadSize \/ NETWORK_PAGE_SIZE)\n            }\n            val prevKey = if (position == GITHUB_STARTING_PAGE_INDEX) null else position - 1\n            LoadResult.Page(\n                data = repos,\n                prevKey = prevKey,\n                nextKey = nextKey\n            )\n        } catch (exception: IOException) {\n            LoadResult.Error(exception)\n        } catch (exception: HttpException) {\n            LoadResult.Error(exception)\n        }\n    }\n\n    \/\/ The refresh key is used for the initial load of the next PagingSource, after invalidation\n    override fun getRefreshKey(state: PagingState&lt;Int, Repo>): Int? {\n\n        \/\/ We need to get the previous key (or next key if previous is null) of the page\n        \/\/ that was closest to the most recently accessed index.\n        \/\/ Anchor position is the most recently accessed index\n        return state.anchorPosition?.let { anchorPosition ->\n            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)\n                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)\n        }\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">private<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">const<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> GITHUB_STARTING_PAGE_INDEX <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">1<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">GithubPagingSource<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">private<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> service: <\/span><span style=\"color: #F69D50\">GithubService<\/span><span style=\"color: #ADBAC7\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">private<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> query: <\/span><span style=\"color: #F69D50\">String<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">) : <\/span><span style=\"color: #F69D50\">PagingSource<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Int<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Repo<\/span><span style=\"color: #ADBAC7\">&gt;() {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">override<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">suspend<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">load<\/span><span style=\"color: #ADBAC7\">(params: <\/span><span style=\"color: #F69D50\">LoadParams<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Int<\/span><span style=\"color: #ADBAC7\">&gt;): <\/span><span style=\"color: #F69D50\">LoadResult<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Int<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Repo<\/span><span style=\"color: #ADBAC7\">&gt; {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> position <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> params.key ?: GITHUB_STARTING_PAGE_INDEX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> apiQuery <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> query <\/span><span style=\"color: #F47067\">+<\/span><span style=\"color: #ADBAC7\"> IN_QUALIFIER<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">try<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> response <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> service.<\/span><span style=\"color: #DCBDFB\">searchRepos<\/span><span style=\"color: #ADBAC7\">(apiQuery, position, params.loadSize)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> repos <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> response.items<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> nextKey <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (repos.<\/span><span style=\"color: #DCBDFB\">isEmpty<\/span><span style=\"color: #ADBAC7\">()) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                <\/span><span style=\"color: #6CB6FF\">null<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            } <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                <\/span><span style=\"color: #768390\">\/\/ initial load size = 3 * NETWORK_PAGE_SIZE<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                <\/span><span style=\"color: #768390\">\/\/ ensure we&#39;re not requesting duplicating items, at the 2nd request<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                position <\/span><span style=\"color: #F47067\">+<\/span><span style=\"color: #ADBAC7\"> (params.loadSize <\/span><span style=\"color: #F47067\">\/<\/span><span style=\"color: #ADBAC7\"> NETWORK_PAGE_SIZE)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            <\/span><span style=\"color: #F47067\">val<\/span><span style=\"color: #ADBAC7\"> prevKey <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">if<\/span><span style=\"color: #ADBAC7\"> (position <\/span><span style=\"color: #F47067\">==<\/span><span style=\"color: #ADBAC7\"> GITHUB_STARTING_PAGE_INDEX) <\/span><span style=\"color: #6CB6FF\">null<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">else<\/span><span style=\"color: #ADBAC7\"> position <\/span><span style=\"color: #F47067\">-<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #6CB6FF\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            LoadResult.<\/span><span style=\"color: #DCBDFB\">Page<\/span><span style=\"color: #ADBAC7\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                <\/span><span style=\"color: #F47067\">data<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> repos,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                prevKey <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> prevKey,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                nextKey <\/span><span style=\"color: #F47067\">=<\/span><span style=\"color: #ADBAC7\"> nextKey<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            )<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        } <\/span><span style=\"color: #F47067\">catch<\/span><span style=\"color: #ADBAC7\"> (exception: <\/span><span style=\"color: #F69D50\">IOException<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            LoadResult.<\/span><span style=\"color: #DCBDFB\">Error<\/span><span style=\"color: #ADBAC7\">(exception)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        } <\/span><span style=\"color: #F47067\">catch<\/span><span style=\"color: #ADBAC7\"> (exception: <\/span><span style=\"color: #F69D50\">HttpException<\/span><span style=\"color: #ADBAC7\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            LoadResult.<\/span><span style=\"color: #DCBDFB\">Error<\/span><span style=\"color: #ADBAC7\">(exception)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #768390\">\/\/ The refresh key is used for the initial load of the next PagingSource, after invalidation<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #F47067\">override<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">getRefreshKey<\/span><span style=\"color: #ADBAC7\">(state: <\/span><span style=\"color: #F69D50\">PagingState<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Int<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Repo<\/span><span style=\"color: #ADBAC7\">&gt;): <\/span><span style=\"color: #F69D50\">Int<\/span><span style=\"color: #ADBAC7\">? {<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/\/ We need to get the previous key (or next key if previous is null) of the page<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/\/ that was closest to the most recently accessed index.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #768390\">\/\/ Anchor position is the most recently accessed index<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #F47067\">return<\/span><span style=\"color: #ADBAC7\"> state.anchorPosition?.<\/span><span style=\"color: #DCBDFB\">let<\/span><span style=\"color: #ADBAC7\"> { anchorPosition <\/span><span style=\"color: #F47067\">-&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">            state.<\/span><span style=\"color: #DCBDFB\">closestPageToPosition<\/span><span style=\"color: #ADBAC7\">(anchorPosition)?.prevKey?.<\/span><span style=\"color: #DCBDFB\">plus<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">1<\/span><span style=\"color: #ADBAC7\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">                ?: state.<\/span><span style=\"color: #DCBDFB\">closestPageToPosition<\/span><span style=\"color: #ADBAC7\">(anchorPosition)?.nextKey?.<\/span><span style=\"color: #DCBDFB\">minus<\/span><span style=\"color: #ADBAC7\">(<\/span><span style=\"color: #6CB6FF\">1<\/span><span style=\"color: #ADBAC7\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>\uc9c1\uc811 \uad6c\ud604\ud558\ub294 PagingSource\ub294 \ub77c\uc774\ube0c\ub7ec\ub9ac\uc5d0\uc11c \uc81c\uacf5\ub418\ub294 \ucd94\uc0c1\ud074\ub798\uc2a4\uc778 PagingSource\ub97c \uc0c1\uc18d\ubc1b\uc544, load(), getRefreshKey()\ub97c \uad6c\ud604\ud574\uc57c \ud55c\ub2e4. \ucf54\ub4dc \uc815\uc758\ub97c \ub530\ub77c\uac00 \uc0b4\ud3b4\ubcf4\uba74 \uac01\uac01\uc740 \ub2e4\uc74c\uacfc \uac19\uc774 \uc120\uc5b8\ub418\uc5b4 \uc788\ub2e4.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-Roboto-Mono.ttf\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Roboto-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#22272e\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>public abstract class PagingSource&lt;Key : Any, Value : Any>\npublic abstract suspend fun load(params: LoadParams&lt;Key>): LoadResult&lt;Key, Value>\npublic abstract fun getRefreshKey(state: PagingState&lt;Key, Value>): Key?<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">abstract<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">class<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F69D50\">PagingSource<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\"> : <\/span><span style=\"color: #F69D50\">Any<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Value<\/span><span style=\"color: #ADBAC7\"> : <\/span><span style=\"color: #F69D50\">Any<\/span><span style=\"color: #ADBAC7\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">abstract<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">suspend<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">load<\/span><span style=\"color: #ADBAC7\">(params: <\/span><span style=\"color: #F69D50\">LoadParams<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">&gt;): <\/span><span style=\"color: #F69D50\">LoadResult<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Value<\/span><span style=\"color: #ADBAC7\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F47067\">public<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">abstract<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #F47067\">fun<\/span><span style=\"color: #ADBAC7\"> <\/span><span style=\"color: #DCBDFB\">getRefreshKey<\/span><span style=\"color: #ADBAC7\">(state: <\/span><span style=\"color: #F69D50\">PagingState<\/span><span style=\"color: #ADBAC7\">&lt;<\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #F69D50\">Value<\/span><span style=\"color: #ADBAC7\">&gt;): <\/span><span style=\"color: #F69D50\">Key<\/span><span style=\"color: #ADBAC7\">?<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>PagingSource\ub294 Pager\uc5d0 \uc758\ud574 \ucee8\ud2b8\ub864 \ub418\ub294\ub370, \ub370\uc774\ud130\ub97c \ub85c\ub4dc\ud574\uc57c\ud558\ub294 \uacbd\uc6b0 load()\uac00 \ubd88\ub9ac\uace0, adapter.refresh()\ub4f1\uc73c\ub85c \ub370\uc774\ud130\ub97c \ub2e4\uc2dc \ub85c\ub4dc\ud574\uc57c\ud560 \ub54c, \ud604\uc7ac \ubcf4\uace0 \uc788\ub294 \uc704\uce58\uc758 \ud398\uc774\uc9c0\ub97c \ub85c\ub4dc\ud558\uae30\uc704\ud574 getRefreshKey()\uac00 \ubd88\ub9b0\ub2e4. <\/p>\n\n\n\n<p>\uba3c\uc800, load()\ub97c \uc0b4\ud3b4\ubcf4\uba74 LoadParam&lt;Key&gt; \uc640 LoadResult&lt;Key, Value&gt;\uac00 \uc0dd\uc18c\ud560 \uac83\uc774\ub2e4. LoadParam\uc740 \uad6c\ud604\uc744 \ub530\ub77c\uac00\ubcf4\uba74, sealed class\ub85c \uc815\uc758\ub418\uc5b4 \uc788\uace0, \uc774\ub97c \uc0c1\uc18d\ud55c \uc774\ub108 \ud074\ub798\uc2a4\ub85c LoadParam.Refresh, LoadParam.Append, LoadParam.Prepend \uac00 \uc815\uc758\ub418\uc5b4 \uc788\ub2e4. \ubc14\ub85c enum \ud615\ud0dc\uc758 sealed class \uc0ac\uc6a9\ud328\ud134\uc774\ub2e4. \ud544\uc694\uc2dc load() \ub0b4\uc5d0\uc11c LoadParam\uc774 \uc14b\uc911\uc5d0 \uc5b4\ub5a4 \ud074\ub798\uc2a4\uc778\uc9c0 \uad6c\ubcc4\ud558\uba74 \ub85c\ub529\uc0c1\ud0dc\uc5d0 \ub530\ub77c \ucc98\ub9ac\uac00 \uac00\ub2a5\ud558\ub2e4. <br> LoadParams\uc758 \uc18d\uc131\uc73c\ub85c loadsize\uac00 \uc0ac\uc6a9\ub418\uace0 \uc788\ub294\ub370, \uc774 \uac12\uc740 PagingConfig\uc5d0 \ub118\uaca8\uc8fc\ub294 pageSize\uc758 \uc601\ud5a5\uc744 \ubc1b\ub294\ub2e4. PagingConfig\uc5d0\ub294 initialLoadingSize\uc18d\uc131\uc774 \uc788\ub294\ub370, pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER \ub85c \uae30\ubcf8\uac12\uc774 \uc124\uc815\ub418\uc5b4 \uc788\ub2e4. DEFAULT_INITIAL_PAGE_MULTIPLIER = 3\uc73c\ub85c \uc815\uc758\ub418\uc5b4 \uc788\uc5b4\uc11c \ucd5c\ucd08 \ub85c\ub529\uc2dc, \uadf8\ub7ec\ub2c8\uae4c Prepend\uc5d0\uc11c\ub294 pageSize*3 \uc774 LoadingParams.loadsize\ub85c \ub118\uc5b4\uc628\ub2e4. \ucc98\uc74c \uc774 \ud6c4, Append\uc2dc\uc5d0\ub294 pageSize\uc640 \ub3d9\uc77c\ud558\uc5ec \ucc98\uc74c\uc5d0\ub9cc 3\ud398\uc774\uc9c0\ub97c \uc77d\uc5b4\uc624\uac8c \ub41c\ub2e4.<br> LoadResult&lt;Key, Value&gt;\ub294 load()\uc758 \ub9ac\ud134\uac12\uc778\ub370, \uad6c\ud604\uc744 \uc0b4\ud3b4\ubcf4\uba74, \uc5ed\uc2dc sealed class\ub85c LoadResult.Page \uc640 LoadResult.Error \uc758 \ub450 \ud074\ub798\uc2a4\uac00 \uc0c1\uc18d\ubc1b\ub294\ub2e4. \uac01\uac01, load()\uc758 \uc131\uacf5\uc2dc \ub9ac\ud134\uac12\uacfc \uc2e4\ud328\uc2dc \ub9ac\ud134\ub418\ub294 \uc5d0\ub7ec\uac12\uc774\ub2e4. LoadResult.Page\uc758 \uc0dd\uc131\uc790\ub97c \ubcf4\uba74, \uac00\uc838\uc628 \ub370\uc774\ud130\uc758 \ub9ac\uc2a4\ud2b8\uc640 \uc774\uc804 \ud398\uc774\uc9c0\uac12, \ub2e4\uc74c\ud398\uc774\uc9c0\uac12\uc774 \ub4e4\uc5b4\uac04\ub2e4. <\/p>\n\n\n\n<p>\uc774\uc81c \uc608\uc81c \ucf54\ub4dc\uc778 GithubPagingSource\ub85c \ub3cc\uc544\uac00\ubcf4\uba74, \uc0dd\uc131\uc790\ub85c \ubc1b\ub294 service\ub294 github\uc5d0\uac8c \uc694\uccad\uc744 \ub0a0\ub9ac\ub294 retrofit \uc11c\ube44\uc2a4\uc774\uace0 query\ub294 UI\ub85c \ubc1b\uc740 \uac80\uc0c9\uc5b4\uc774\ub2e4. retrofit \uc11c\ube44\uc2a4\ub97c \uc774\uc6a9\ud558\uc5ec \ub370\uc774\ud130\ub97c \ubc1b\uc544\uc624\uace0 \uc5d0\ub7ec\uac00 \ubc1c\uc0dd\ud558\uba74 LoadResult.Error\ub97c \ub9ac\ud134\ud55c\ub2e4. \uc5d0\ub7ec\uc5c6\uc774 \uc131\uacf5\ud558\uba74, prevKey\uac12\uacfc nextKey\uac12\uc744 \uacc4\uc0b0\ud574\uc11c \ub370\uc774\ud130\uc640 \ud568\uaed8 LoadResult.Page\ub97c \ucc44\uc6cc \uc774\uac83\uc744 \ub9ac\ud134\ud574\uc900\ub2e4. <\/p>\n\n\n\n<p>\ub2e4\uc74c\uc73c\ub85c \uc0b4\ud3b4\ubcfc \uac83\uc740 getRefreshKey()\uc778\ub370, \uc5ec\ub7ec \uc870\uac74\ub4e4\uc5d0\uc758\ud574 \ud604\uc7ac \ubcf4\uace0\uc788\ub294 \ub9ac\uc2a4\ud2b8\ub97c \uc0c8\ub85c \uc77d\uc5b4\ub4e4\uc774\ub294 \uacbd\uc6b0\uc5d0 \uc0ac\uc6a9\ub41c\ub2e4. \uc778\uc790\ub85c PagingState&lt;Key: Any, Value: Any&gt;\uac00 \ub118\uc5b4\uc624\ub294\ub370, \uc5ec\uae30\uc5d0\ub294 \ud604\uc7ac \uc0ac\uc6a9\uc911\uc778 Paging\uc758 \uc5ec\ub7ec \uc815\ubcf4\ub4e4\uc774 \ub4e4\uc5b4\uc788\ub2e4. \uad6c\ud604\uc744 \ub530\ub77c\uac00\ubcf4\uba74, \uc0ac\uc6a9\uc911\uc778 page\ub4e4\uc758 \ub9ac\uc2a4\ud2b8\uac00 \ub4e4\uc5b4\uc788\uc73c\uba70, \uac00\uc7a5 \ucd5c\uadfc\uc5d0 \uc0ac\uc6a9\ud55c \uc544\uc774\ud15c\uc758 \uc704\uce58\uac00 anchorPosition\uc5d0 \ub4e4\uc5b4\uc788\ub2e4. \uc774 \uc678\uc5d0\ub3c4 PagingConfig\uac12\uc774 \uadf8\ub300\ub85c \ub4e4\uc5b4\uac00 \uc788\uae30\ub3c4 \ud558\ub2e4. <br> \ub9ac\ud134\uac12\uc73c\ub85c\ub294 Key\uac12\uc744 \ub3cc\ub824\uc8fc\uac8c \ub418\uc5b4 \uc788\ub294\ub370, \uc608\uc81c\ucf54\ub4dc\uc5d0\uc120 anchorPosition\uc744 \uae30\uc900\uc73c\ub85c \ud604\uc7ac \ud398\uc774\uc9c0\uac12\uc744 \ub3cc\ub824\uc8fc\uace0 \uc788\ub2e4. \ud604\uc7ac \ud398\uc774\uc9c0\ub97c \uacc4\uc0b0\ud558\uae30 \uc704\ud574, closestPageToPosition()\uba54\uc18c\ub4dc\ub97c \uc774\uc6a9\ud558\uace0 \uc788\uc73c\uba70, \uc774 \uba54\uc18c\ub4dc\uc758 \ub9ac\ud134\uac12\uc774 LoadResult.Page\uc774\uae30 \ub54c\ubb38\uc5d0, prevKey\uc640 nextKey\uc911\uc5d0 \uc874\uc7ac\ud558\ub294 \uac12\uc744 \uac00\uc9c0\uace0 1\uc744 \ub354\ud558\uac70\ub098 \ube7c\uc11c \ud604\uc7ac\ud398\uc774\uc9c0 \uac12\uc744 \ub3cc\ub824\uc8fc\uace0 \uc788\ub2e4. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">RemoteMediator \uc758 \uad6c\ud604<\/h3>\n\n\n\n<p>\ucd08\ubc18\uc758 \ub77c\uc774\ube0c\ub7ec\ub9ac \uad6c\uc870\uc5d0\uc11c \uc5b8\uae09\ud588\ub358 \ubd80\ubd84\uc778\ub370, \ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc640\uc11c DB\uc5d0 \uce90\uc2f1\ud558\uace0 \uc571\uc740 DB\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uac00\ub294 \uad6c\uc870\ub97c \uad8c\uc7a5\ud558\uace0 \uc788\ub2e4. \uc5ec\uae30\uc5d0\uc11c \ub124\ud2b8\uc6cc\ud06c\uc640 DB\uc758 \uc5f0\uacb0\uc9c0\uc810\uc5d0  RemoteMediator \uac00 \uc874\uc7ac\ud55c\ub2e4. <\/p>\n\n\n\n<p>\uc5b4&#8230; \uc774 \ubd80\ubd84\ub3c4 \uc815\ub9ac\ub97c \ud558\uae34 \ud574\uc57c\ud558\ub294\ub370, \ub4a4\ub85c \ubbf8\ub8e8\ub3c4\ub85d \ud558\uaca0\ub2e4. \uadf8\ub807\uac8c \uc5b4\ub824\uc6b4 \ubd80\ubd84\uc740 \uc544\ub2c8\uace0, PagingSource\uc758 \uad6c\ud604\uacfc \uc720\uc0ac\ud55c \ubd80\ubd84\uc774 \ub9ce\ub2e4. \uc81c\ub300\ub85c \uc54c\uace0\uc2f6\uc73c\uba74, \uc55e\uc758 \uc608\uc81c\uc5d0\uc11c \uc0ac\uc6a9\ud55c <a href=\"https:\/\/developer.android.com\/codelabs\/android-paging?hl=en&amp;continue=https%3A%2F%2Fcodelabs.developers.google.com%2F#12\">\uad6c\uae00 \ucf54\ub4dc\ub7a9 AndroidPaging\uc758 13\ub2e8\uacc4<\/a>\ubd80\ud130 \ubcf4\uba74 \ub420 \uac83\uc774\ub2e4. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"771\" height=\"57\" src=\"http:\/\/batmask.dothome.co.kr\/wordpress\/wp-content\/uploads\/2020\/04\/seperator.png\" alt=\"seperator\" class=\"wp-image-381\" srcset=\"http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/04\/seperator.png 771w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/04\/seperator-300x22.png 300w, http:\/\/batmask.net\/wordpress\/wp-content\/uploads\/2020\/04\/seperator-768x57.png 768w\" sizes=\"auto, (max-width: 771px) 100vw, 771px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\uc0ac\uc871&#8230;<\/h3>\n\n\n\n<p> &#8216;\uac04\ub2e8\ud558\uac8c&#8217; \uc815\ub9ac\ud558\uace0 \ub118\uc5b4\uac00\uc790\uace0 \uc2dc\uc791\ud55c\uac74\ub370, \uc0dd\uac01\ubcf4\ub2e4 \uc624\ub798\uac78\ub838\ub2e4. \uadf8\ub798\uc11c \ub098\uc911\uc5d4 \uc9c0\uccd0\ubc84\ub824\uc11c RemoteMediator \ubd80\ubd84\uc740 \ub204\ub77d\uc2dc\ud0a4\uae30\ub3c4 \ud588\ub2e4. \uc774\ud574\ud588\ub2e4\uace0 \uc0dd\uac01\ud558\uace0 \uae00\uc744 \uc2dc\uc791\ud588\ub294\ub370, \ub9c9\uc0c1 \uc2dc\uc791\ud558\uace0\ubcf4\ub2c8 \ubaa8\ub974\ub294\uac8c \ub9ce\uc558\uace0, \uc548\ub4dc\ub85c\uc774\ub4dc \uc2a4\ud29c\ub514\uc624\ub97c \uc774\uc6a9\ud558\uba74 \uac01 \ubd80\ubd84\ub4e4\uc758 \uad6c\ud604\uc744 \ub2e4 \ub4e4\uc5ec\ub2e4 \ubcfc \uc218 \uc788\uc5c8\ub294\ub370, \ucc98\uc74c\uc5d0\ub294 \ubcfc \uc0dd\uac01\uc744 \uc548\ud588\ub2e4. \ucc98\uc74c\ubd80\ud130 \ub4e4\uc5ec\ub2e4\ubcf4\uace0 \ub0b4\ubd80\uad6c\uc870\ub97c \uc880 \ubd84\uc11d\ud574\uc11c \uadf8 \ub0b4\uc6a9\uc744 \uc62c\ub838\uc73c\uba74 \uc5b4\ub560\uc744\uae4c\ud558\ub294 \uc544\uc26c\uc6c0\ub3c4 \uc0dd\uae34\ub2e4. <\/p>\n\n\n\n<p><strong><span class=\"has-inline-color has-vivid-cyan-blue-color\">\ub098\ub85c\uc11c\ub294 \uc774\uac70 \uc4f0\uba74\uc11c \ub9ce\uc774 \uacf5\ubd80\uac00 \ub410\ub294\ub370, \ub2e4\ub978\ubd84\ub4e4\uc5d0\uac90 \uadf8\ub2e5 \ub3c4\uc6c0\uc774 \uc548\ub420\uac70 \uac19\uae30\ub3c4 \ud558\ub2e4. \uc774 \ube14\ub85c\uadf8 \uc2dc\uc791\uc774 \ub0b4\uac00 \ub098\uc911\uc5d0 \ucc3e\uc544\ubcfc \uc218 \uc788\ub294\uac78 \uc4f0\uc790, \ub098\ub9cc \uc54c\uc544\ubcf4\ub294\uac78 \ucd5c\uc18c\ud55c\uc758 \uc870\uac74\uc73c\ub85c \uac78\uc5c8\ub294\ub370 \uc54c\ub78c\uad00\ub828 \uae00\uc774 \ub2e4\ub978\ubd84\ub4e4\uc5d0\uac8c \ub3c4\uc6c0\uc774 \ub418\uace0 \uc788\ub294\uac78 \ubcf4\ub2c8 \uc695\uc2ec\uc744 \uc880 \ubd80\ub838\ub358\uac70 \uac19\ub2e4. \uc544\uc27d\uc9c0\ub9cc \uc774\uc815\ub3c4 \uc218\uc900\uc774 \ub0b4\uac00 \uc6d0\ud558\ub294 \ub808\ubca8\uc774\uace0, \ub204\uad70\uac00\uc5d0\uac8c \ub3c4\uc6c0\uae4c\uc9c0 \ub418\ub294\uae00\uc740 \ub098\uc911\uc744 \uae30\uc57d\ud574\uc57c\uaca0\ub2e4.<\/span><\/strong> <\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\ub4e0\uc9c0, DB\uc5d0 \uc800\uc7a5\ub41c\uac78 \uac00\uc838\uc624\ub4e0\uc9c0 \ub370\uc774\ud130\uac00 \ub9e4\uc6b0 \ub9ce\uc544\uc9c0\uba74 \ud55c\ubc88\uc5d0 \uc77d\uc5b4\uc62c \ub54c \ubb38\uc81c\uac00 \uc0dd\uae30\uac8c\ub41c\ub2e4. \uadf8\ub807\ub2e4\uace0 \ud654\uba74\uc5d0 \ubcf4\uc774\ub294 \ub9cc\ud07c\ub9cc \ub9e4\ubc88 \uc77d\uc5b4\uc624\uac8c\ub418\uba74, \uc2a4\ud06c\ub864\ub4f1\uc744 \uc774\uc6a9\ud558\uc5ec \ub370\uc774\ud130\ub97c \uc0b4\ud3b4\ubcfc \ub54c, \ub809\uc774 \uc0dd\uae38 \uac83\uc774\ub2e4. \uc774\ub97c \ud574\uacb0\ud558\uae30\uc704\ud574 \uc0ac\uc6a9\uc790 \uc785\ub825\uc5d0 \ucda9\ubd84\ud788 \ubc18\uc751\ud560\ub9cc\ud07c \uc5ec\uc720\uc788\ub294 \ub370\uc774\ud130\ub97c \uc77d\uc5b4\uc624\uace0, \uc0ac\uc6a9\uc790\uac00 \ucd94\uac00\uc801\uc73c\ub85c \ub370\uc774\ud130\ub97c \uc0b4\ud3b4\ubcfc \ub54c, \ud544\uc694\ud55c \ub9cc\ud07c\uc529 \ubbf8\ub9ac \uc77d\uc5b4\uc624\ub294 \uae30\ubc95\uc774 \uc624\ub798\uc804\ubd80\ud130 \uc0ac\uc6a9\ub418\uc5b4 \uc654\ub2e4. \uc774\uac78 paging \uae30\ubc95\uc774\ub77c \ud558\ub294\ub370, \uaf2d [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,34],"tags":[186,38,225,227,226],"class_list":["post-1012","post","type-post","status-publish","format-standard","hentry","category-android","category-kotlin","tag-android-2","tag-kotlin","tag-paging","tag-paging-3","tag-paging-library"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/1012","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/comments?post=1012"}],"version-history":[{"count":46,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/1012\/revisions"}],"predecessor-version":[{"id":3563,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/posts\/1012\/revisions\/3563"}],"wp:attachment":[{"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/media?parent=1012"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/categories?post=1012"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/batmask.net\/index.php\/wp-json\/wp\/v2\/tags?post=1012"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}