{"id":11495,"date":"2024-11-09T15:50:15","date_gmt":"2024-11-09T06:50:15","guid":{"rendered":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=11495"},"modified":"2026-03-20T15:52:59","modified_gmt":"2026-03-20T06:52:59","slug":"11495","status":"publish","type":"post","link":"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=11495","title":{"rendered":""},"content":{"rendered":"<h1>Core Data &#8211; NSFetchedResultsController \uac00\uc774\ub4dc<\/h1>\n<p>Core Data\ub97c \uc0ac\uc6a9\ud574 \ub300\ub7c9\uc758 \ub370\uc774\ud130\ub97c <code>UITableView<\/code>\ub098 <code>UICollectionView<\/code>\uc5d0 \ubfcc\ub824\uc918\uc57c \ud560 \ub54c, \ud6a8\uc728\uc131\uacfc \uc131\ub2a5\uc744 \ubaa8\ub450 \uc7a1\uc744 \uc218 \uc788\ub294 \ucd5c\uace0\uc758 \ub3c4\uad6c\uac00 \ubc14\ub85c <strong><code>NSFetchedResultsController<\/code><\/strong>\uc785\ub2c8\ub2e4.<\/p>\n<h2>1. NSFetchedResultsController\ub780?<\/h2>\n<p><strong>NSFetchedResultsController(FRC)<\/strong>\ub294 Core Data \uac80\uc0c9 \uacb0\uacfc\uc640 \uc0ac\uc6a9\uc790 \uc778\ud130\ud398\uc774\uc2a4(UI)\ub97c \ub3d9\uae30\ud654\ud558\ub294 &#8216;\uc911\uac1c\uc790&#8217; \uc5ed\ud560\uc744 \ud569\ub2c8\ub2e4. \ub2e8\uc21c\ud788 \ub370\uc774\ud130\ub97c \uac00\uc838\uc624\ub294 \uac83\uc744 \ub118\uc5b4, \ub370\uc774\ud130\uc14b\uc758 \ubcc0\ud654\ub97c \uac10\uc9c0\ud558\uace0 \uc774\ub97c \ud14c\uc774\ube14\ubdf0\ub098 \uceec\ub809\uc158\ubdf0\uc5d0 \uc989\uac01\uc801\uc73c\ub85c \ubc18\uc601\ud558\ub294 \uae30\ub2a5\uc744 \uc81c\uacf5\ud569\ub2c8\ub2e4.<\/p>\n<h2>2. \uc65c \uc0ac\uc6a9\ud558\ub294\uac00? (\uc8fc\uc694 \uc7a5\uc810)<\/h2>\n<p>\ub2e8\uc21c\ud788 <code>fetchRequest<\/code>\ub97c \uc218\ud589\ud558\ub294 \uac83\ubcf4\ub2e4 FRC\ub97c \uc0ac\uc6a9\ud558\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc810\uc774 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n<ul>\n<li><strong>\uba54\ubaa8\ub9ac \ud6a8\uc728\uc131:<\/strong> \ubaa8\ub4e0 \ub370\uc774\ud130\ub97c \ud55c\uaebc\ubc88\uc5d0 \uba54\ubaa8\ub9ac\uc5d0 \uc62c\ub9ac\uc9c0 \uc54a\uace0, \ud544\uc694\ud55c \ubd80\ubd84\ub9cc \uac00\uc838\uc640\uc11c \ubcf4\uc5ec\uc90d\ub2c8\ub2e4.<\/li>\n<li><strong>\uc139\uc158 \uad00\ub9ac:<\/strong> \ub370\uc774\ud130\ub97c \ud2b9\uc815 \uae30\uc900\uc5d0 \ub530\ub77c \uc139\uc158(Section)\ubcc4\ub85c \uc544\uc8fc \uc27d\uac8c \ub098\ub20c \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/li>\n<li><strong>\uc790\ub3d9 \uc5c5\ub370\uc774\ud2b8:<\/strong> \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ubcc0\uacbd(\uc800\uc7a5, \uc0ad\uc81c, \uc218\uc815)\uc774 \uc0dd\uae30\uba74 Delegate \uba54\uc11c\ub4dc\ub97c \ud1b5\ud574 UI\ub97c \uc790\ub3d9\uc73c\ub85c \uac31\uc2e0\ud569\ub2c8\ub2e4.<\/li>\n<li><strong>\uce90\uc2f1:<\/strong> \ubcf5\uc7a1\ud55c \uc815\ub82c\uc774\ub098 \uc139\uc158 \uacc4\uc0b0 \uacb0\uacfc\ub97c \uce90\uc2f1\ud558\uc5ec \uc131\ub2a5\uc744 \ucd5c\uc801\ud654\ud569\ub2c8\ub2e4.<\/li>\n<\/ul>\n<h2>3. \ud575\uc2ec \uad6c\uc131 \uc694\uc18c<\/h2>\n<p>FRC\ub97c \uad6c\ud604\ud558\uae30 \uc704\ud574 \ud544\uc694\ud55c 4\uac00\uc9c0 \ud575\uc2ec \uc694\uc18c\uc785\ub2c8\ub2e4.<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">\uc694\uc18c<\/th>\n<th style=\"text-align: left;\">\uc124\uba85<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\"><strong>Fetch Request<\/strong><\/td>\n<td style=\"text-align: left;\">\uc5b4\ub5a4 \ub370\uc774\ud130\ub97c \uac00\uc838\uc62c\uc9c0 \uc815\uc758 (Entity, Sort Descriptors \ud544\uc218)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><strong>Managed Object Context<\/strong><\/td>\n<td style=\"text-align: left;\">\ub370\uc774\ud130\ub97c \uac00\uc838\uc62c \ud1b5\ub85c (MOC)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><strong>Section Name Key Path<\/strong><\/td>\n<td style=\"text-align: left;\">\ub370\uc774\ud130\ub97c \uc139\uc158\ubcc4\ub85c \ub098\ub20c \uae30\uc900\uc774 \ub418\ub294 \ud504\ub85c\ud37c\ud2f0 (\uc120\ud0dd \uc0ac\ud56d)<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: left;\"><strong>Cache Name<\/strong><\/td>\n<td style=\"text-align: left;\">\uc131\ub2a5 \ud5a5\uc0c1\uc744 \uc704\ud55c \uce90\uc2dc \ud30c\uc77c \uc774\ub984 (\uc120\ud0dd \uc0ac\ud56d)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<hr \/>\n<h2>4. \uad6c\ud604 \ub2e8\uacc4 (Code Snippet)<\/h2>\n<h3>Step 1: FRC \ucd08\uae30\ud654<\/h3>\n<pre><code class=\"language-swift\">lazy var fetchedResultsController: NSFetchedResultsController&lt;Item&gt; = {\n    let fetchRequest: NSFetchRequest&lt;Item&gt; = Item.fetchRequest()\n\n    \/\/ FRC\ub294 \ubc18\ub4dc\uc2dc \ucd5c\uc18c \ud558\ub098 \uc774\uc0c1\uc758 SortDescriptor\uac00 \ud544\uc694\ud569\ub2c8\ub2e4.\n    let sort = NSSortDescriptor(key: &quot;createdAt&quot;, ascending: false)\n    fetchRequest.sortDescriptors = [sort]\n\n    let controller = NSFetchedResultsController(\n        fetchRequest: fetchRequest,\n        managedObjectContext: self.container.viewContext,\n        sectionNameKeyPath: nil, \/\/ \uc139\uc158 \uad6c\ubd84\uc774 \ud544\uc694\ud558\uba74 \ud574\ub2f9 \ud0a4\uac12 \uc785\ub825\n        cacheName: &quot;itemsCache&quot;\n    )\n\n    \/\/ Delegate \uc124\uc815\n    controller.delegate = self\n    return controller\n}()<\/code><\/pre>\n<h3>Step 2: \ub370\uc774\ud130 \uac00\uc838\uc624\uae30 \uc2e4\ud589<\/h3>\n<pre><code class=\"language-swift\">do {\n    try fetchedResultsController.performFetch()\n} catch {\n    print(&quot;Fetch failed: \\(error)&quot;)\n}<\/code><\/pre>\n<h3>Step 3: NSFetchedResultsControllerDelegate \uad6c\ud604<\/h3>\n<p>\ub370\uc774\ud130\uac00 \ubcc0\uacbd\ub420 \ub54c UI\ub97c \uc5c5\ub370\uc774\ud2b8\ud558\ub294 \ub85c\uc9c1\uc785\ub2c8\ub2e4. (iOS 13+ \uae30\uc900 <code>DiffableDataSource<\/code>\ub97c \uc4f0\uc9c0 \uc54a\ub294 \uc804\ud1b5\uc801\uc778 \ubc29\uc2dd)<\/p>\n<pre><code class=\"language-swift\">extension ViewController: NSFetchedResultsControllerDelegate {\n    func controllerWillChangeContent(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;) {\n        tableView.beginUpdates()\n    }\n\n    func controller(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;, didChange anyObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {\n        switch type {\n        case .insert:\n            tableView.insertRows(at: [newIndexPath!], with: .fade)\n        case .delete:\n            tableView.deleteRows(at: [indexPath!], with: .fade)\n        case .update:\n            tableView.reloadRows(at: [indexPath!], with: .none)\n        case .move:\n            tableView.moveRow(at: indexPath!, to: newIndexPath!)\n        @unknown default:\n            break\n        }\n    }\n\n    func controllerDidChangeContent(_ controller: NSFetchedResultsController&lt;NSFetchRequestResult&gt;) {\n        tableView.endUpdates()\n    }\n}<\/code><\/pre>\n<h2>5. \uacb0\ub860: \uc5b8\uc81c \uc368\uc57c \ud560\uae4c?<\/h2>\n<ul>\n<li><strong>\uc0ac\uc6a9\ud574\uc57c \ud560 \ub54c:<\/strong> Core Data\uc758 \uc5d4\ud2f0\ud2f0\uac00 \ud14c\uc774\ube14\ubdf0\uc640 1:1\ub85c \ub9e4\uce6d\ub418\uba70, \ub370\uc774\ud130\uc758 \uc0bd\uc785\/\uc0ad\uc81c\uac00 \ube48\ubc88\ud558\uac8c \uc77c\uc5b4\ub0a0 \ub54c.<\/li>\n<li><strong>\uc0ac\uc6a9\ud558\uc9c0 \uc54a\uc544\ub3c4 \ub420 \ub54c:<\/strong> \ub370\uc774\ud130 \uc591\uc774 \ub9e4\uc6b0 \uc801\uac70\ub098, Core Data\uac00 \uc544\ub2cc \ub2e8\uc21c \ubc30\uc5f4 \ub370\uc774\ud130\ub97c \ubcf4\uc5ec\uc904 \ub54c.<\/li>\n<\/ul>\n<blockquote>\n<p><strong>\ud83d\udca1 Tip:<\/strong> iOS 13 \uc774\ud6c4\ubd80\ud130\ub294 <code>UICollectionViewDiffableDataSource<\/code>\uc640 \ud568\uaed8 FRC\ub97c \uc0ac\uc6a9\ud558\uc5ec \ub354\uc6b1 \ub9e4\ub044\ub7ec\uc6b4 \uc560\ub2c8\uba54\uc774\uc158\uacfc \uac04\uacb0\ud55c \ucf54\ub4dc\ub97c \uc791\uc131\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Core Data &#8211; NSFetchedResultsController \uac00\uc774\ub4dc Core Data\ub97c \uc0ac\uc6a9\ud574 \ub300\ub7c9\uc758 \ub370\uc774\ud130\ub97c UITableView\ub098 UICollectionView\uc5d0 \ubfcc\ub824\uc918\uc57c \ud560 \ub54c, \ud6a8\uc728\uc131\uacfc \uc131\ub2a5\uc744 \ubaa8\ub450 \uc7a1\uc744 \uc218 \uc788\ub294 \ucd5c\uace0\uc758 \ub3c4\uad6c\uac00 \ubc14\ub85c NSFetchedResultsController\uc785\ub2c8\ub2e4. 1. NSFetchedResultsController\ub780? NSFetchedResultsController(FRC)\ub294 Core Data \uac80\uc0c9 \uacb0\uacfc\uc640 \uc0ac\uc6a9\uc790 \uc778\ud130\ud398\uc774\uc2a4(UI)\ub97c \ub3d9\uae30\ud654\ud558\ub294 &#8216;\uc911\uac1c\uc790&#8217; \uc5ed\ud560\uc744 \ud569\ub2c8\ub2e4. \ub2e8\uc21c\ud788 \ub370\uc774\ud130\ub97c \uac00\uc838\uc624\ub294 \uac83\uc744 \ub118\uc5b4, \ub370\uc774\ud130\uc14b\uc758 \ubcc0\ud654\ub97c \uac10\uc9c0\ud558\uace0 \uc774\ub97c \ud14c\uc774\ube14\ubdf0\ub098 \uceec\ub809\uc158\ubdf0\uc5d0 \uc989\uac01\uc801\uc73c\ub85c \ubc18\uc601\ud558\ub294 \uae30\ub2a5\uc744 \uc81c\uacf5\ud569\ub2c8\ub2e4. 2. \uc65c\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.skyer9.pe.kr\/wordpress\/?p=11495\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[40],"tags":[],"class_list":["post-11495","post","type-post","status-publish","format-standard","hentry","category-language"],"_links":{"self":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/11495","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11495"}],"version-history":[{"count":1,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/11495\/revisions"}],"predecessor-version":[{"id":11496,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=\/wp\/v2\/posts\/11495\/revisions\/11496"}],"wp:attachment":[{"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11495"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11495"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.skyer9.pe.kr\/wordpress\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11495"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}