{"id":13052,"date":"2026-10-28T08:00:45","date_gmt":"2026-10-27T23:00:45","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=13052"},"modified":"2026-10-28T13:06:00","modified_gmt":"2026-10-28T04:06:00","slug":"nextjs-sns-server-51-11","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/10\/28\/nextjs-sns-server-51-11\/","title":{"rendered":"[nextjs]SNS Server-51(myapp61+)[\ud314\ub85c\uc6b0\ub9ac\uc2a4\ud2b8\/FollowList]\u00a0"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb \uc774 \ud3ec\uc2a4\ud2b8\uc5d0\uc11c\ub294 \ud314\ub85c\uc6b0\ub9ac\uc2a4\ud2b8 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \uc0b4\ud3b4 \ubd05\ub2c8\ub2e4.<br>In this post, we take a look at the &#8220;following&#8221; list section.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ud314\ub85c\uc6b0\ub9ac\uc2a4\ud2b8\ub294 \ub0b4\uac00 \ub2e4\ub978\uc11c\ubc84\uc758 \uc0ac\uc6a9\uc790\ub97c \ud314\ub85c\uc6b0\ud55c \uc0ac\ub78c\uc758 \ubaa9\ub85d\uc785\ub2c8\ub2e4.<br>The &#8220;following&#8221; list is a list of users from other servers whom you have chosen to follow.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb\uc774 \ubd80\ubd84\uc740 \ud14c\ub9c8\ub514\ub809\ud1a0\ub9ac\uc758 MainTheme.tsx\ud30c\uc77c\uc5d0\uc11c \ud638\ucd9c\ub418\ub294 \ubd80\ubd84\uc785\ub2c8\ub2e4.<br>This part is called from the MainTheme.tsx file in the theme directory.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ud398\uc774\uc9c0\ub124\uc774\uc158 \ubd80\ubd84\uc5d0\uc11c \uc124\uba85\ud588\ub358 TimelineList\ubd80\ubd84\uacfc \ub9e4\uc6b0 \uc720\uc0ac\ud569\ub2c8\ub2e4.<br>This is very similar to the <code>TimelineList<\/code> component discussed in the section on pagination.<\/p>\n\n\n\n<p>\ud83d\udcc1 UI\ubd80\ubd84 \/ UI section<\/p>\n\n\n\n<p>\u2714\ufe0f themeex\ub97c \ud14c\ub9c8\ub85c \uc0ac\uc6a9\ud558\ub294 \uacbd\uc6b0 \ud314\ub85c\uc6b0\ub9ac\uc2a4\ud2b8 UI\ubd80\ubd84\uc740 \ud504\ub85c\uc81d\ud2b8 \ub8e8\ud2b8\ub85c \ubd80\ud130 \uc544\ub798\uc758 \uacbd\ub85c\uc5d0 \uc788\uc2b5\ub2c8\ub2e4.<br>If you are using the &#8216;themeex&#8217; theme, the UI component for the follow list can be found at the following path, relative to the project root:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>https:\/\/aloy-horizon.duckdns.org\/<strong>@userid<\/strong>\/following\n(ex. https:\/\/aloy-horizon.duckdns.org\/<strong>@user1<\/strong>\/following)\n\n\ub610\ub294 \/ or\n\nhttps:\/\/aloy-horizon.duckdns.org\/usersui\/<strong>user1<\/strong>?view=following\n(ex. https:\/\/aloy-horizon.duckdns.org\/usersui\/<strong>userid<\/strong>?view=following)<\/code><\/pre>\n\n\n\n<p>1)\uc704\uc640 \uac19\uc740 \ub77c\uc6b0\ud2b8\ub85c \uc2e4\ud589\uc2dc \ud314\ub85c\uc6b0 \ub9ac\uc2a4\ud2b8\ub97c \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>You can view the follow list by accessing the route mentioned above.<\/p>\n\n\n\n<p>2)\ub77c\uc6b0\ud2b8 \uc2e4\ud589\uc2dc @userid \ub610\ub294 usersui\/userid \uc774\ub807\uac8c \uc2e4\ud589\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>When executing a route, you can use either <code>@userid<\/code> or <code>usersui\/userid<\/code>.<\/p>\n\n\n\n<p>3)\uc774 \ubd80\ubd84\uc740 next.config.ts\uc5d0 \ubcf4\uc2dc\uba74 \uc774 \uc124\uc815\uc774 \uc788\uc2b5\ub2c8\ub2e4.<br>You can find this configuration in <code>next.config.ts<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  async rewrites() {\n    return &#91;\n      {\n        source: '\/@:username',\n        destination: '\/usersui\/:username',\n      },\n      {\n        <strong>source: '\/@:username\/following',<\/strong>\n        <strong>destination: '\/usersui\/:username?view=following',<\/strong> \/\/ \u2705 myapp43 - initialView\n      },\n      {\n        source: '\/@:username\/followers',\n        destination: '\/usersui\/:username?view=followers',\n      },\n      { source: '\/@:username\/notifications', \/\/ \u2705 myapp44 - initialView for notifications\n        destination: '\/usersui\/:username?view=notifications'\n      },\n    ]\n  },<\/code><\/pre>\n\n\n\n<p>4)\ubcf8\uc778\uc758 \uacc4\uc815\uc73c\ub85c SNS\ud398\uc774\uc9c0\uc5d0 \uc811\uc18d\ud6c4 \ud504\ub85c\ud544 \ubd80\ubd84\uc758 &#8220;\ud314\ub85c\uc789\/Followng&#8221;\uc744 \ud074\ub9ad\ud558\uba74 \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>You can view this by accessing your social media page and clicking on &#8220;Following&#8221; in your profile section.<\/p>\n\n\n\n<p>\u2714\ufe0f \ud398\uc774\uc9c0\ub124\uc774\uc158 \/ Pagination<\/p>\n\n\n\n<p>\u2714\ufe0f app\/usersui\/[username]\/_components\/theme\/pinafore\/FollowingList.tsx<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  const {\n    flatData: users,\n    fetchNextPage,\n    hasNextPage,\n    isFetchingNextPage,\n    isLoading\n  } = useCursorPagination({\n    key: &#91;'following', username],\n    fetcher: async (cursor) => {\n      const params = new URLSearchParams();\n      params.set('username', username);\n      params.set('limit', '20');\n      if (cursor) params.set('max_id', cursor);\n\n      const r = await fetch(`\/api\/followinglist?${params.toString()}`, { credentials: 'include' });\n      const d = await r.json();\n      const accounts = Array.isArray(d)? d : d.accounts || d.data || &#91;];\n\n      return {\n        data: accounts,\n        nextCursor: accounts.length === 20? accounts&#91;accounts.length - 1]?.created_at || String(accounts.length) : null,\n        hasMore: accounts.length === 20,\n      };\n    },\n  });<\/code><\/pre>\n\n\n\n<p>1)UI \ubd80\ubd84 \ucf54\ub4dc\ub97c \ud655\uc778\ud558\uc2dc\uba74 \ud398\uc774\uc9c0\ub124\uc774\uc158 \uad6c\ud604\uc744 \uc704\ud574\uc11c useCursorPagination \ud6c5\uc744 \uc0ac\uc6a9\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4.<br>If you look at the UI-related code, you will see that the <code>useCursorPagination<\/code> hook is being used to implement pagination.<\/p>\n\n\n\n<p>2)\ucf5c\ubc31\ud568\uc218\ub85c useCursorPagination\uc5d0 \uc804\ub2ec\ud569\ub2c8\ub2e4<br>Pass it to <code>useCursorPagination<\/code> as a callback function.<\/p>\n\n\n\n<p>3)\uadf8\ub9ac\uace0 \ud6c5\ub0b4\ubd80\uc758 \ud568\uc218\uc640 \ubcc0\uc218 \uac12\uc744 \ub2e4\uc2dc \ub9ac\ud134\ud574\uc11c FollowingList.tsx\uc5d0\uc11c \uc0ac\uc6a9\ud569\ub2c8\ub2e4.<br>Then, the functions and variable values \u200b\u200bfrom within the hook are returned so that they can be used in FollowingList.tsx.<\/p>\n\n\n\n<p>4)\uc774\ub7f0 \ubc29\uc2dd\uc740 \uae30\uc874\uc758 \ud0c0\uc784\ub77c\uc778\uc5d0\uc11c \uad6c\ud604\ud558\ub294 \ubc29\uc2dd\uacfc \ub3d9\uc77c\ud569\ub2c8\ub2e4.<br>This approach is the same as the one implemented in the existing timeline.<\/p>\n\n\n\n<p>\u2714\ufe0f \ud398\uc774\uc9c0\ub124\uc774\uc158(\ubb34\ud55c\uc2a4\ud06c\ub864 \uc635\uc800\ubc84)<br>Pagination (Infinite Scroll Observer)<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  ... ...\n  const <strong>loaderRef<\/strong> = useRef&lt;HTMLDivElement>(null);\n  useEffect(() => {\n    const el = loaderRef.current;\n    if (!el ||!hasNextPage) return;\n    const observer = new IntersectionObserver(\n      (&#91;entry]) => {\n        if (entry.isIntersecting &amp;&amp;!isFetchingNextPage) fetchNextPage();\n      },\n      { threshold: 0.3 }\n    );\n    observer.observe(el);\n    return () => observer.disconnect();\n  }, &#91;hasNextPage, isFetchingNextPage, fetchNextPage]);\n... ...\n&lt;div ref={<strong>loaderRef<\/strong>} className=\"h-10 flex items-center justify-center text-sm text-&#91;#999]\">\n... ...\n&lt;\/div><\/code><\/pre>\n\n\n\n<p>1)\uc0ac\uc6a9\uc790\uac00 \ud654\uba74 \uc2a4\ud06c\ub864\uc744 \ub0b4\ub9ac\uba74 \ubb34\ud55c\uc2a4\ud06c\ub864 \uc635\uc800\ubc84\uac00 &lt;div ref={loaderRef}\ub97c \ud655\uc778\ud558\uba74 fetchNextPage()\ud568\uc218\ub97c \uc2e4\ud589\ud569\ub2c8\ub2e4.<br>When the user scrolls down the screen, the infinite scroll observer detects the <code>&lt;div ref={loaderRef}><\/code> element and triggers the <code>fetchNextPage()<\/code> function.<\/p>\n\n\n\n<p>2)fetchNextPage()\ud568\uc218\ub294 userCursorPagination\ud6c5\uc5d0\uc11c \ub9ac\ud134\ub418\ub294 \ud568\uc218\uc785\ub2c8\ub2e4.<br>The <code>fetchNextPage()<\/code> function is a function returned by the <code>userCursorPagination<\/code> hook.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>\/\/ app\/usersui\/&#91;username]\/_components\/theme\/pinafore\/FollowingList.tsx\n... ...\n  const {\n    flatData: users,\n   <strong> fetchNextPage,<\/strong>\n    hasNextPage,\n    isFetchingNextPage,\n    isLoading\n  } = useCursorPagination({\n    key: &#91;'following', username],\n    <strong>fetcher<\/strong>: async (cursor) => {\n... ....\n\n       const r = await<strong> fetch(`\/api\/followinglist?${params.toString()}`<\/strong>, { credentials: 'include' });\n... ...\n      };\n    },\n  });<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>\/\/ app\/hooks\/useCursorPagination.ts\n... ...\nconst <strong>fetchNextPage<\/strong> = useCallback(async () => {\n... ...\n const res: any = await <strong>fetcher<\/strong>(nextCursorRef.current || undefined);\n... ...\n      setPages(prev => {\n        \/\/ Set : \uc911\ubcf5 \uc5c6\ub294 \uc9d1\ud569 \/ Set with no duplicates\n        const seen = new Set(prev.flat().map((p:any) => p.id));\n        const filtered = data.filter((p:any) => p.id &amp;&amp;!seen.has(p.id));\n        if (filtered.length === 0) {\n          setHasNextPage(false);\n          return prev;\n        }\n        return &#91;...prev, filtered];\n      });\n... ...\n} \n... ...\n   return {\n    flatData,\n    pages: pages.map(data => ({ data })),\n    <strong>fetchNextPage<\/strong>, \/\/ \ub2e4\uc74c \ud398\uc774\uc9c0 \uac00\uc838\uc624\ub294 \ud568\uc218 \/ Function to fetch the next page\n    hasNextPage,   \/\/ \ub2e4\uc74c \ud398\uc774\uc9c0\uac00 \uc788\ub294\uc9c0 \/ Whether there is a next page\n    isFetchingNextPage, \/\/ \ub2e4\uc74c \ud398\uc774\uc9c0 \ub85c\ub529\uc911\uc778\uc9c0 \/ Checking if the next page is loading...\n    isLoading, \/\/ \uccab \ub85c\ub529\uc911\uc778\uc9c0 \/ Is it loading for the first time?\n  };<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ub370\uc774\ud130\ub97c UI\uc5d0 \ucd9c\ub825 \/ Display data in the UI<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>      {users.map((u: any, i: number) => (\n        &lt;div key={u.id || i} className=\"flex gap-3 px-4 py-3 border-b border-&#91;#eee] hover:bg-&#91;#fafafe]\">\n          &lt;div className=\"w-9 h-9 rounded-full bg-&#91;#6364ff] text-white flex items-center justify-center font-extrabold shrink-0\">\n            {(u.display_name || u.username || '?')&#91;0].toUpperCase()}\n          &lt;\/div>\n          &lt;div className=\"min-w-0 flex-1\">\n            &lt;div className=\"font-bold text- truncate\">{u.display_name || u.username}&lt;\/div>\n            &lt;div className=\"text- text-&#91;#657786] truncate\">{u.acct || u.actor}&lt;\/div>\n          &lt;\/div>\n          {\/* &lt;button className=\"ml-auto px-3 py-1 bg-&#91;#6364ff] text-white rounded-full text-xs font-bold hover:bg-&#91;#5556ee]\">\n            Following\n          &lt;\/button> *\/}\n        &lt;\/div>\n      ))}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1API<\/p>\n\n\n\n<p>\u2714\ufe0f app\/api\/followerslist\/route.ts<\/p>\n\n\n\n<p>\u2714\ufe0f \ud30c\ub77c\uba54\ud130 \ubcc0\uc218\uc5d0 username\uc774 \uc5c6\uc73c\uba74 \uc5d0\ub7ec\ub97c \ub9ac\ud134\ud569\ub2c8\ub2e4.<br>If &#8216;username&#8217; is missing from the parameter variables, an error is returned.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>if (!username) return NextResponse.json({ error: 'username required' }, { status: 400 });<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f\ub370\uc774\ud130\ubca0\uc774\uc2a4 following \ud14c\uc774\ube14\uc5d0\uc11c username\ubcc0\uc218\uc758 \uac12\uacfc \uc77c\uce58\ud558\ub294 \ud544\ub4dc\ub97c \uac80\uc0c9\ud569\ub2c8\ub2e4. <br>Search for a field in the following database table that matches the value of the <code>username<\/code> variable.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>const rows = db.prepare(`SELECT actor FROM following WHERE username=? ORDER BY created_at DESC`).all(username) as any&#91;];\n<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f map\ud568\uc218\ub85c \ub370\uc774\ud130\ub97c \ubf51\uc544\ub0c5\ub2c8\ub2e4.<br>Extract data using the map function.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>const data = rows.map(r => { ... }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f URL\uc5d0\uc11c id\ubd80\ubd84\ub9cc \ubd84\ub9ac\ud569\ub2c8\ub2e4.<br>Extract only the &#8216;id&#8217; portion from the URL.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>      const url = new URL(r.actor);\n      const hostname = url.hostname; \/\/ ex) freelifemakers.com\n\n      const parts = url.pathname.split('\/').filter(Boolean);\n      let remoteName: string | null = null;\n      const idx = parts.indexOf('users');\n      if (idx!== -1) remoteName = parts&#91;idx+1];\n      else if (parts&#91;0]?.startsWith('@')) remoteName = parts&#91;0].substring(1);\n      else remoteName = parts&#91;0];<\/code><\/pre>\n\n\n\n<p>1)URL\uc5d0\uc11c &#8216;\/&#8217;\ub97c \uae30\uc900\uc73c\ub85c \ubd84\ub9ac\ud558\uace0 \uac12\uc774 null,false\uba74 \ubc84\ub9bd\ub2c8\ub2e4.<br>Split the URL by &#8216;\/&#8217; and discard any values \u200b\u200bthat are null or false.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>const parts = url.pathname.split('\/').filter(Boolean);<\/code><\/pre>\n\n\n\n<p>A.URL\uac1d\uccb4\ub294 &#8216;\/&#8217;\ub97c \uae30\uc900\uc73c\ub85c \ubd84\ub9ac\ud558\uba74 &#8216;https:\/\/&#8217;\uc774 \ubd80\ubd84\uc740 \uc774\ubbf8 \ubd84\ub9ac\ub418\uc5b4 \ubc30\uc5f4\uc5d0 \ud3ec\ud568\ub418\uc5b4 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.<br>When a URL object is split based on the &#8216;\/&#8217; character, the &#8216;https:\/\/&#8217; portion is already separated out and is not included in the resulting array.<\/p>\n\n\n\n<p>B.\uc989 0\ubc88 \ubc30\uc5f4\uc740 &#8216;https:\/\/&#8217; \ub2e4\uc74c \uac12 \uc785\ub2c8\ub2e4.<br>In other words, the element at index 0 is the value that follows &#8216;https:\/\/&#8217;.<\/p>\n\n\n\n<p>2)URL\uc5d0 users\uac12\uc774 \ud3ec\ud568\ub418\uc5b4 \uc788\uc73c\uba74 user\ub2e4\uc74c \uac12\uc774 0\ubc88 \ubc30\uc5f4\uc774 \ub429\ub2c8\ub2e4. \uc989 users\/user2\uc758 \uacbd\uc6b0 user2\uac00remoteName\uc5d0 \uc800\uc7a5\ub429\ub2c8\ub2e4.<br>If the URL contains the value &#8216;users&#8217;, the segment following &#8216;user&#8217; becomes the first element (index 0) of the array. For example, in the case of &#8216;users\/user2&#8217;, &#8216;user2&#8217; is stored in &#8216;remoteName&#8217;.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>let remoteName: string | null = null;\nconst idx = parts.indexOf('users');\nif (idx !== -1) remoteName = parts&#91;idx+1];\nelse if (parts&#91;0]?.startsWith('@')) remoteName = parts&#91;0].substring(1);\nelse remoteName = parts&#91;0];<\/code><\/pre>\n\n\n\n<p>A.URL\uc5d0 &#8216;@&#8217;\uac12\uc774 \ud3ec\ud568\ub418\uc5b4 \uc788\uc73c\uba74 1\uc778\ub371\uc2a4 \ubd80\ud130 \ub05d\uae4c\uc9c0 \uc798\ub77c\uc11c remoteName\uc5d0 \uc800\uc7a5\ud569\ub2c8\ub2e4.<br>If the URL contains an &#8216;@&#8217; character, the portion from index 1 to the end is extracted and stored in <code>remoteName<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>(parts&#91;0]?.startsWith('@')) remoteName = parts&#91;0].substring(1);<\/code><\/pre>\n\n\n\n<p>B.substring(1)\uc740 &#8216;@&#8217;\ub97c \uc790\ub974\uace0 \ub2e4\uc74c \ubb38\uc790\uc5f4\ubd80\ud130 \ubb38\uc790\uc5f4 \ub9c8\uc9c0\ub9c9\uae4c\uc9c0 \uc120\ud0dd\ud558\ub294 \uc5ed\ud560\uc744 \ud569\ub2c8\ub2e4.<br><code>substring(1)<\/code> serves to remove the &#8216;@&#8217; character and select the remainder of the string, from the next character to the very end.<\/p>\n\n\n\n<p>\u2714\ufe0f \ub85c\uceec \ub3c4\uba54\uc778\uc778\uc9c0 \ud655\uc778\ud574\uc11c \ub85c\uceec\ub3c4\uba54\uc778\uc774\uba74 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c username\uacfc display_name\uc744 \uac80\uc0c9\ud569\ub2c8\ub2e4.<br>Check whether it is a local domain; if it is, retrieve the <code>username<\/code> and <code>display_name<\/code> from the database.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>      const isLocalDomain = hostname === DOMAIN || hostname === 'localhost' || hostname.endsWith(`.${DOMAIN}`);\n\n      let info = null;\n      if (isLocalDomain &amp;&amp; remoteName) {\n        info = db.prepare('SELECT username, display_name FROM users WHERE username=?').get(remoteName) as any;\n      }\n\n      const isLocalUser = isLocalDomain &amp;&amp;!!info;<\/code><\/pre>\n\n\n\n<p>A.isLocalUser\uc5d0\ub294 true\/false \uac12\uc774 \uc800\uc7a5\ub429\ub2c8\ub2e4.<br>isLocalUser stores a true\/false value.<\/p>\n\n\n\n<p>B.isLocalDomain\uc774 \uc874\uc7ac\ud558\uace0 info\uac12\ub3c4 \uc874\uc7ac\ud574\uc57c isLocalUser\uc5d0 true\uac12\uc774 \uc800\uc7a5\ub429\ub2c8\ub2e4.<br><code>isLocalUser<\/code> is set to <code>true<\/code> only if both <code>isLocalDomain<\/code> and <code>info<\/code> are present.<\/p>\n\n\n\n<p>C.\uc989 URL\ub85c\uc811\uc18d\ud55c ID\uc640 \ub85c\uceec \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\uc11c \uc870\ud68c\ud55c \uacb0\uacfc\uac00 \ubaa8\ub450 \uc874\uc7ac\ud574\uc57c \ub85c\uceec\ub3c4\uba54\uc778\uc73c\ub85c \uc778\uc815\ud569\ub2c8\ub2e4.<br>In other words, a domain is recognized as a local domain only if both the ID accessed via the URL and the result retrieved from the local database exist.<\/p>\n\n\n\n<p>\u2714\ufe0f UI\uc5d0\uc11c \uc77d\uc744 \uac12\uc744 \uc544\ub798 \ucc98\ub7fc \ub9ac\ud134\ud569\ub2c8\ub2e4<br>Return the values \u200b\u200bto be read by the UI as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>      return {\n        username: remoteName || r.actor,\n        display_name: info?.display_name || remoteName || 'remote',\n        acct: isLocalUser\n         ? `${remoteName}@${DOMAIN}` \/\/ \u2705 \uc9c4\uc9dc \ub85c\uceec\uc77c \ub54c\ub9cc aloy \/ only when it's truly local\n          : `${remoteName}@${hostname}`, \/\/ \u2705 freelifemakers.com\uc774\uba74 freelifemakers@freelifemakers.com\n        actor: r.actor,\n        domain: hostname,\n      };<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<p>\u2714\ufe0f UI<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"800\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/followlist-jpg.jpg\" alt=\"\" class=\"wp-image-13101\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/followlist-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/followlist-jpg-300x300.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/followlist-jpg-150x150.jpg 150w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/followlist-jpg-768x768.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/followlist-jpg-400x400.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n\n\n\n<p>\u2714\ufe0f API<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"520\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/API-jpg.jpg\" alt=\"\" class=\"wp-image-13102\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/API-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/API-jpg-300x195.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/API-jpg-768x499.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/API-jpg-400x260.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">API<\/figcaption><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\ub9c8\ud0dc\ubcf5\uc74c 21\uc7a5 22\uc808 \/ Matthew 21: 22<\/p>\n\n\n\n<p>&#8220;\ub108\ud76c\uac00 \uae30\ub3c4\ud560 \ub54c\uc5d0 \ubb34\uc5c7\uc774\ub4e0\uc9c0 \ubbff\uace0 \uad6c\ud558\ub294 \uac83\uc740 \ub2e4 \ubc1b\uc73c\ub9ac\ub77c \ud558\uc2dc\ub2c8\ub77c&#8221;<\/p>\n\n\n\n<p>&#8220;And everything you ask in prayer, believing, you will receive.&#8221;<\/p>\n<\/blockquote>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb \uc774 \ud3ec\uc2a4\ud2b8\uc5d0\uc11c\ub294 \ud314\ub85c\uc6b0\ub9ac\uc2a4\ud2b8 \ubd80\ubd84\uc5d0 \ub300\ud574\uc11c \uc0b4\ud3b4 \ubd05\ub2c8\ub2e4.In this post, we take a look at the &#8220;following&#8221; list section. \ud83d\udc49\ud83c\udffb \ud314\ub85c\uc6b0\ub9ac\uc2a4\ud2b8\ub294 \ub0b4\uac00 \ub2e4\ub978\uc11c\ubc84\uc758 \uc0ac\uc6a9\uc790\ub97c \ud314\ub85c\uc6b0\ud55c \uc0ac\ub78c\uc758 \ubaa9\ub85d\uc785\ub2c8\ub2e4.The &#8220;following&#8221; list is a list of users from other servers whom you have chosen to follow. \ud83d\udc49\ud83c\udffb\uc774 \ubd80\ubd84\uc740 \ud14c\ub9c8\ub514\ub809\ud1a0\ub9ac\uc758 MainTheme.tsx\ud30c\uc77c\uc5d0\uc11c \ud638\ucd9c\ub418\ub294 \ubd80\ubd84\uc785\ub2c8\ub2e4.This part is called from the MainTheme.tsx [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,27,1,7],"tags":[],"class_list":["post-13052","post","type-post","status-publish","format-standard","hentry","category-news","category-nextjs","category-uncategorized","category-website","missing-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/13052","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/comments?post=13052"}],"version-history":[{"count":49,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/13052\/revisions"}],"predecessor-version":[{"id":13103,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/13052\/revisions\/13103"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=13052"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=13052"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=13052"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}