{"id":9105,"date":"2026-08-31T09:35:58","date_gmt":"2026-08-31T00:35:58","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=9105"},"modified":"2026-08-31T14:46:27","modified_gmt":"2026-08-31T05:46:27","slug":"nextjs-sns-server-18","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/31\/nextjs-sns-server-18\/","title":{"rendered":"[nextjs]SNS Server-18(myapp28)"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb myapp28\uc5d0\ub294 \ud14c\ub9c8\ud30c\uc77c\uc5d0 \ubd80\uc2a4\ud2b8\ubc84\ud2bc\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4.<br>In myapp28, a boost button is added to the theme file.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub0b4\uac00 \uc4f4 \uae00\uc5d0\ub294 \ubd80\uc2a4\ud2b8 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.<br>You cannot boost your own posts.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub0b4\uac00 \ubd80\uc2a4\ud2b8 \ubc84\ud2bc\uc744 \ub204\ub974\uace0 \ub2e4\uc2dc \ud55c\ubc88 \ub354 \ub204\ub974\uba74 \ubd80\uc2a4\ud2b8 \ucde8\uc18c\uac00 \ub429\ub2c8\ub2e4.<br>If I press the boost button and then press it again, the boost is cancelled.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub0b4\uac00 \ubd80\uc2a4\ud2b8 \ud55c \uae00\uc744 \ub2e4\ub978 \uc0ac\ub78c\uc774 \ubd80\uc2a4\ud2b8\ud558\uba74 \uce74\uc6b4\ud130\uac00 \uc99d\uac00\ud569\ub2c8\ub2e4.<br>When someone else boosts a post I have boosted, the counter increases.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc804\uccb4 \ucf54\ub4dc\ub294 \uae43\ud5c8\ube0c\uc5d0\uc11c \ud655\uc778 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>You can find the full code on GitHub.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/gideonslife01\/flm-nextjs\">https:\/\/github.com\/gideonslife01\/flm-nextjs<\/a><\/p>\n\n\n\n<p>\ud83d\udcc1 \uc804\uccb4 \ud504\ub85c\uc81d\ud2b8 \uad6c\uc870 \/ Overall Project Structure<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>myapp28\/  \n\u251c\u2500\u2500 app\/  (Next.js App Router)\n\u2502   \u251c\u2500\u2500 .well-known\/webfinger\/route.ts  -&gt; webfinger\n\u2502   \u251c\u2500\u2500 api\/follow\/route.ts    -&gt; Follow API(temporary)\n\u2502   \u251c\u2500\u2500 api\/announce\/route.ts  -&gt; Boost(Announcement)\n\u2502   \u251c\u2500\u2500 api\/like\/route.ts.     -&gt; Like API\n\u2502   \u251c\u2500\u2500 api\/posts\/route.ts     -&gt; Writing API\n\u2502   \u251c\u2500\u2500 api\/timeline\/route.ts  -&gt; Timeline API\n\u2502   \u251c\u2500\u2500 users\/&#91;username]\/\n\u2502   \u2502   \u251c\u2500\u2500 statuses\/&#91;id]\/route.ts -&gt; Indivisual Post\n\u2502   \u2502   \u251c\u2500\u2500 route.ts               -&gt; Acotr Information\n\u2502   \u2502   \u251c\u2500\u2500 followers\/route.ts     -&gt; Followers List\n\u2502   \u2502   \u251c\u2500\u2500 following\/route.ts     -&gt; Following List\n\u2502   \u2502   \u251c\u2500\u2500 inbox\/route.ts         -&gt; Inbox\n\u2502   \u2502   \u2514\u2500\u2500 outbox\/route.ts        -&gt; outbox\n\u2502   \u251c\u2500\u2500 usersui\/&#91;username]\/\n\u2502   \u2502   \u251c\u2500\u2500 page.tsx               -&gt; Timeline UI\n\u2502   \u2502   \u2514\u2500\u2500 _components\/themes\/\n\u2502   \u2502       \u251c\u2500\u2500 themeex\/ThemeexTheme.tsx   -&gt; Example Theme\n\u2502   \u2502       \u251c\u2500\u2500 pinafore\/PinaforeTheme.tsx -&gt; Theme 1\n\u2502   \u2502       \u251c\u2500\u2500 mastodon\/MastodonTheme.tsx -&gt; Theme 2\n\u2502   \u2502       \u2514\u2500\u2500 minimal\/MinimaltTheme.tsx  -&gt; Theme 3\n\u2502   \u251c\u2500\u2500 layout.tsx, page.tsx, globals.css\n\u2502   \u2514\u2500\u2500 favicon.ico\n\u251c\u2500\u2500 lib\/\n\u2502   \u251c\u2500\u2500 theme.tsx              -&gt; Theme Provider\n\u2502   \u251c\u2500\u2500<strong> <\/strong>watchThemes.ts         -&gt; Check real-time theme changes\n\u2502   \u251c\u2500\u2500 ap.ts                  -&gt; Follow Accept\n\u2502   \u2514\u2500\u2500 db.ts                  -&gt; DB connection\n\u251c\u2500\u2500 data\/\n\u2502   \u2514\u2500\u2500 keys\/                  -&gt; private.pem, public.pem\n\u251c\u2500\u2500 data.sqlite                -&gt; Database\n\u251c\u2500\u2500 Caddyfile                  -&gt; https \n\u251c\u2500\u2500 instrumentation.ts         -&gt; Background Server\n\u2514\u2500\u2500 package.json<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud504\ub85c\uc81d\ud2b8 \uc2dc\uc791(myapp28)<br>Project Start (myapp28)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx create-next-app@latest<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 SQLite\uc124\uce58 \/ Installing SQLite<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd ~\/myapp28\nnpm install better-sqlite3\nnpm install -D @types\/better-sqlite3<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 chokidar \uc124\uce58 \/ Installing chokidar<\/p>\n\n\n\n<p>&#8212; \uc2e4\uc2dc\uac04 \ud30c\uc77c \ubc0f \ud3f4\ub354\ubcc0\uacbd \uac10\uc2dc\uae30\ub2a5<br>Real-time file and folder change monitoring function<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm install -D chokidar<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 DDNS,https\uc124\uc815 \/ DDNS,https settings<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-wp-embed is-provider-freelifemakers-org wp-block-embed-freelifemakers-org\"><div class=\"wp-block-embed__wrapper\">\n<blockquote class=\"wp-embedded-content\" data-secret=\"NjebkXu6N6\"><a href=\"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/10\/nextjs-dns-caddy\/\">[nextjs]nextjs+domain(ddns)+https(caddy)<\/a><\/blockquote><iframe loading=\"lazy\" class=\"wp-embedded-content\" sandbox=\"allow-scripts\" security=\"restricted\" style=\"position: absolute; visibility: hidden;\" title=\"&#8220;[nextjs]nextjs+domain(ddns)+https(caddy)&#8221; &#8212; freelifemakers.org\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/10\/nextjs-dns-caddy\/embed\/#?secret=5s1wK75nqR#?secret=NjebkXu6N6\" data-secret=\"NjebkXu6N6\" width=\"560\" height=\"315\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>\ud83d\udcc1 \ub3c4\uba54\uc778\ud5c8\uc6a9, @userid \uc0ac\uc6a9\ud558\uae30<br>Allowing domains, using @userid<\/p>\n\n\n\n<p>&#8212; next.config.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n  \/* allow domain*\/\n  allowedDevOrigins: &#91;'aloy-horizon.duckdns.org', '*.duckdns.org'],\n\n  \/* host.domain.org\/@userid *\/\n<strong>  async rewrites() {\n    return &#91;\n      {\n        source: '\/@:username',\n        destination: '\/usersui\/:username',\n      },\n    ]\n  }<\/strong>\n};\n\n\nexport default nextConfig;\n<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \uc774\uc804 \ud504\ub85c\uc81d\ud2b8\uc5d0\uc11c \uc2e0\uaddc \ud504\ub85c\uc81d\ud2b8\ub85c \ubcf5\uc0ac\ud560 \ud30c\uc77c<br>Files to copy from the previous project to the new project<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>myapp project\/  \n\u251c\u2500\u2500 app\/  (Next.js App Router)\n\u2502   \u251c\u2500\u2500 .well-known\/webfinger\/route.ts  -&gt; webfinger\n\u2502   \u251c\u2500\u2500 api\/                  -&gt; API\n\u2502   \u251c\u2500\u2500 users\/                -&gt; Inbox,Outbox,Post,Actor\n\u2502   \u251c\u2500\u2500 usersui\/              -&gt; Timeline UI,Theme\n\u2502   \u251c\u2500\u2500 layout.tsx            -&gt; Privoder\n\u251c\u2500\u2500 lib\/\n\u251c\u2500\u2500 data\/                     -&gt; Key\n\u251c\u2500\u2500 data.sqlite               -&gt; Database\n\u251c\u2500\u2500 Caddyfile                 -&gt; https \n\u251c\u2500\u2500 next.config.ts            -&gt; DDNS,@\n\u2514\u2500\u2500 instrumentation.ts        -&gt; Background Server<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \ucf54\ub4dc \uc218\uc815 \/ Code Modification<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udca1 app\/api\/announce\/route.ts<\/p>\n\n\n\n<p>&#8212; \uc790\uae30\uc790\uc2e0\uc758 \uae00\uc740 \ubd80\uc2a4\ud2b8\ud558\uc9c0 \ubabb\ud574\uac8c \uae30\ubcf8\uc801\uc73c\ub85c \ub9c9\ud600 \uc788\uc2b5\ub2c8\ub2e4.<br>By default, you are prevented from boosting your own posts.<\/p>\n\n\n\n<p>&#8212; target\uc740 \ub370\uc774\ud130\ubca0\uc774\uc2a4\uac00 \uac00\ub9ac\ud0a4\ub294 \uc8fc\uc18c\uc774\uace0 DOMAIN\uc740 \ub0b4\uc11c\ubc84\uc758 \uc8fc\uc18c\uc785\ub2c8\ub2e4.<br>&#8216;target&#8217; is the address pointed to by the database, while &#8216;DOMAIN&#8217; is the address of my server.<\/p>\n\n\n\n<p>&#8212; \ub450\uac1c\uc758 \uac12\uc774 \uc77c\uce58\ud558\uba74 \uc624\ub958\ub97c \ubc1c\uc0dd\uc2dc\ud0b5\ub2c8\ub2e4.<br>An error is raised if the two values \u200b\u200bmatch.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    if (target.startsWith(`https:\/\/${DOMAIN}\/users\/${username}`)) {\n      return NextResponse.json({ ok: false, error: 'cannot boost own post' }, { status: 400 });\n    }<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\u2714\ufe0f app\/usersui\/[username]\/page.tsx<\/p>\n\n\n\n<p>&#8212; \uae30\uc874 \ucf54\ub4dc\ub294 inbox_posts\uc5d0 \ub300\ud55c \ubd80\ubd84\uc744 \uace0\ub824\ud558\uc9c0 \uc54a\uc544\uc11c &#8220;cannot boost own post&#8221;\uc624\ub958\ub97c \ubc1c\uc0dd\uc2dc\ud0b5\ub2c8\ub2e4.<br>The existing code does not account for <code>inbox_posts<\/code>, causing a &#8220;cannot boost own post&#8221; error.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  const handleBoost = async (post: any) =&gt; {\n    const targetId = post.original_id || post.id;\n<strong>    const fullTarget = targetId.startsWith('http')\n     ? targetId\n      : `https:\/\/${process.env.NEXT_PUBLIC_DOMAIN || 'aloy-horizon.duckdns.org'}\/users\/${post.username}\/statuses\/${targetId}`;<\/strong>\n\n    await fetch(`\/api\/announce`, {\n      method: post.isMyBoost? 'DELETE' : 'POST',\n      headers: { 'Content-Type': 'application\/json' },\n      body: JSON.stringify({ username, target: fullTarget })\n    });\n    await refreshTimeline();\n  };<\/code><\/pre>\n\n\n\n<p>&#8212; myapp28\uc5d0\uc11c \ubc30\ub2ec\ub41c\uae00(inbox_posts)\uc5d0\ub300\ud55c \ubd80\ubd84\uc744 \uc218\uc815\ud588\uc2b5\ub2c8\ub2e4.<br>I modified the section regarding posts delivered to myapp28 (inbox_posts).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> ... ... \n useEffect(() =&gt; {\n    params.then(p =&gt; {\n      setUsername(p.username);\n      fetch(`\/api\/timeline?username=${p.username}`)\n       .then(r =&gt; r.json())\n       .then(data =&gt; {\n          console.log('\ud83d\udce6 timeline', data&#91;0]); \/\/ \u2705 myapp28\n          setTimeline(data);\n          setLoading(false);\n        });\n    });\n  }, &#91;params]);\n... ...\n \/\/ \u2705 myapp28\n const handleBoost = async (post: any) =&gt; {\n <strong> <\/strong>let target = post.original_id || post.id;\n  console.log('\ud83d\udd0d \uc6d0\ubcf8 post \ub370\uc774\ud130\/original data', post); \n\n  if (!target.startsWith('http')) {\n    \/\/ inbox \uae00\uc774\uba74 actor\uac00 \uc9c4\uc9dc \uc8fc\uc778!\n    \/\/ actor = https:\/\/freelifemakers.com\/users\/user1 \uac19\uc740 \ud615\ud0dc\uc77c \uc218 \uc788\uc74c\n    \/\/ If it's an inbox message, the actor is the actual owner!\n    \/\/ The actor might be in a format like https:\/\/freelifemakers.com\/users\/user1\n\n    if (post.actor &amp;&amp; post.actor.startsWith('http')) {\n      \/\/ actor URL\uc5d0\uc11c username \ucd94\ucd9c \/ Extract username from actor URL\n      try {\n        const actorUrl = new URL(post.actor);\n        const parts = actorUrl.pathname.split('\/');\n        const usersIdx = parts.indexOf('users');\n\n<strong>        if (usersIdx !== -1) {\n        \/\/ inbox_posts\n          const actorUsername = parts&#91;usersIdx + 1];\n          target = `${actorUrl.origin}\/users\/${actorUsername}\/statuses\/${target}`;\n        } else {\n        \/\/ posts\n          target = `https:\/\/${process.env.NEXT_PUBLIC_DOMAIN || 'aloy-horizon.duckdns.org'}\/users\/${post.username}\/statuses\/${target}`;\n        }<\/strong>\n      } catch {\n        target = `https:\/\/${process.env.NEXT_PUBLIC_DOMAIN || 'aloy-horizon.duckdns.org'}\/users\/${post.username}\/statuses\/${target}`;\n      }\n    } else {\n      target = `https:\/\/${process.env.NEXT_PUBLIC_DOMAIN || 'aloy-horizon.duckdns.org'}\/users\/${post.username}\/statuses\/${target}`;\n    }\n  }\n  \n  console.log('\ud83d\udd01 boost click', { target, isMyBoost: post.isMyBoost });\n\n  \/\/ Optimistic UI\n  setTimeline(prev =&gt; prev.map(p =&gt;\n    p.id === post.id\n      ? {...p, isMyBoost:!p.isMyBoost, boostCount: (p.boostCount || 0) + (p.isMyBoost? -1 : 1) }\n      : p\n  ));\n\n  try {   \n    const res = await fetch(`\/api\/announce`, {\n      method: post.isMyBoost? 'DELETE' : 'POST',\n      headers: { 'Content-Type': 'application\/json' },\n      body: JSON.stringify({ username, target })\n    });\n\n    const data = await res.json();\n    console.log('\u2705 announce res', data);\n    if (!res.ok) throw new Error(data.error);\n    await refreshTimeline();\n  } catch (e) {\n    console.error('\u274c boost \uc2e4\ud328', e);\n    await refreshTimeline();\n  }\n};<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/usersui\/[username]\/inbox<\/p>\n\n\n\n<p>&#8212; \ubd80\uc2a4\ud2b8 \ucde8\uc18c\ud560 \ub54c \uc6d0\ubcf8 \uae00 \uc0ad\uc81c \uae08\uc9c0\ucc98\ub9ac<br>Prevent deletion of the original post when cancelling a boost.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>      else if (objType === 'Announce') {\n         const announceId = typeof obj === 'object' ? obj.id : null;\n         const objectId = typeof obj.object === 'string' ? obj.object : obj.object?.id;\n         if (announceId) {\n           db.prepare('DELETE FROM announces WHERE id = ?').run(announceId);\n           \n           \/\/ \u2705 myapp28 - Announce Undo\uc2dc \uc6d0\ubcf8 \uae00 \uc0ad\uc81c \uae08\uc9c0 \ucc98\ub9ac \/ Prevent deletion of the original post when performing \"Announce Undo.\"\n<strong>           \/\/db.prepare('DELETE FROM inbox_posts WHERE original_id = ? AND actor = ?').run(objectId, body.actor);<\/strong>\n           console.log(`\ud83d\uddd1 &#91;${username}] Announce \ucde8\uc18c: ${announceId}`);\n         }\n      }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/timeline\/route.ts <\/p>\n\n\n\n<p>&#8212; isMyBoost , boostCount \ucd94\uac00\ud574\uc11c \ubd80\uc2a4\ud2b8 \ud588\ub294\uc9c0 \uc5ec\ubd80\ub97c \uccb4\ud06c\ud558\uace0 \ubd80\uc2a4\ud2b8 \ub41c \ud69f\uc218\ub97c \ucd94\uac00\ud588\uc2b5\ub2c8\ub2e4.<br>Added <code>isMyBoost<\/code> and <code>boostCount<\/code> to track whether a boost has been applied and to record the number of boosts.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  \/\/ \u2705 myapp28\n  const enriched = timeline.map((p: any) =&gt; {\n    \/\/ \ud480 URL \ub9cc\ub4e4\uae30 \/ Create full URL\n    let fullId = p.original_id || p.id;\n    if (!fullId.startsWith('http')) {\n      fullId = `https:\/\/${DOMAIN}\/users\/${p.username}\/statuses\/${fullId}`;\n    }\n    \n    \/\/ inbox \uae00\uc740 original_id\uac00 \uc774\ubbf8 \ud480 URL\uc77c \uc218 \uc788\uc74c\n    \/\/ The original_id of an inbox post might already be a full URL.\n    let fullOriginalId = p.original_id || p.id;\n    if (p.original_id &amp;&amp; !p.original_id.startsWith('http')) {\n      fullOriginalId = `https:\/\/${DOMAIN}\/users\/${p.username}\/statuses\/${p.original_id}`;\n    }\n\n<strong>    const isMyBoost = boostedSet.has(p.original_id) || \n                      boostedSet.has(p.id) || \n                      boostedSet.has(fullId) ||\n                      boostedSet.has(fullOriginalId) ||\n                      (p.original_id &amp;&amp; boostedSet.has(p.original_id));\n\n    const boostCount = (db.prepare(`SELECT COUNT(*) as c FROM announces WHERE object = ? OR object = ? OR object = ?`)\n      .get(fullId, p.original_id || '', p.id || '') as any)?.c || 0;<\/strong>\n\n    return {\n      ...p,\n      fullId, \/\/ \ub514\ubc84\uae45\uc6a9 \/ For debugging purposes\n      isMyBoost,\n      boostCount\n    };\n  });\n\n  return NextResponse.json(enriched); \/\/ \u2705 myapp28 - \ubc84\uadf8 \uc218\uc815 \/ Bug fixes<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0fapp\/usersui\/[username]\/_components\/themes\/pinafore\/PinaforeTheme.tsx<\/p>\n\n\n\n<p>&#8212; import \uc624\ub958 \uc218\uc815 \/ Fix import error<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import type { ThemeName } from '..\/themeNames'; \/\/ \u2705 myapp28\nimport { themeNames } from '..\/themeNames';<\/code><\/pre>\n\n\n\n<p>&#8212; \ubd80\uc2a4\ud2b8 \uce74\uc6b4\ud130 \/ Boost Counter<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;button onClick={() =&gt; onBoost(p)} className={p.isMyBoost? 'boosted' : ''}&gt;\ud83d\udd01 {p.boostCount || ''}&lt;\/button&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<p>\u2714\ufe0f pinafore\uc5d0\uc11c \uc4f4 \uae00 \ub0b4 \uc11c\ubc84\ub85c \ubc30\ub2ec\ub418\ub294\uc9c0 \ud655\uc778 \ubc0f \ubd80\uc2a4\ud2b8 \ud558\uae30, \uce74\uc6b4\ud130 \ud655\uc778<br>Check if posts from Pinafore are being delivered to my server, boost them, and check the counter.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"430\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui-jpg.jpg\" alt=\"\" class=\"wp-image-9152\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui-jpg-300x161.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui-jpg-768x413.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui-jpg-400x215.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Boost1<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f  \ub0b4\uac00 \ubd80\uc2a4\ud2b8 \ud55c\uae00\uc774 pinafore\uc5d0 \ub3c4\ucc29\ud558\ub294\uc9c0 \ud655\uc778<br>Check if my Boost Hangul arrives at Pinafore.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"284\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-message-jpg.jpg\" alt=\"\" class=\"wp-image-9153\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-message-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-message-jpg-300x107.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-message-jpg-768x273.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-message-jpg-400x142.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Boost Message<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f pinafore\uc5d0\uc11c \ubd80\uc2a4\ud2b8\ud558\uae30 \/ Boosting in Pinafore<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"539\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-button-jpg.jpg\" alt=\"\" class=\"wp-image-9155\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-button-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-button-jpg-300x202.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-button-jpg-768x517.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-boost-button-jpg-400x270.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Pinafore Boost<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f \uce74\uc6b4\ud130 \uc99d\uac00 \ud655\uc778 \/ Verify counter increment<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"366\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui2-jpg.jpg\" alt=\"\" class=\"wp-image-9156\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui2-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui2-jpg-300x137.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui2-jpg-768x351.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp28-pinafore-ui2-jpg-400x183.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">Boost Counter<\/figcaption><\/figure>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\uc694\ud55c\ubcf5\uc74c 8\uc7a5 32\uc808 \/ John 8:32<\/p>\n\n\n\n<p>&#8220;\uadf8\ub9ac\uace0 \ub108\ud76c\ub294 \uc9c4\ub9ac\ub97c \uc54c\uac8c \ub420 \uac83\uc774\uba70, \uc9c4\ub9ac\uac00 \ub108\ud76c\ub97c \uc790\uc720\ub86d\uac8c \ud560 \uac83\uc774\ub2e4.&#8221;<\/p>\n\n\n\n<p>&#8220;Then you will know the truth ,and the truth will set you free&#8221;<\/p>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb myapp28\uc5d0\ub294 \ud14c\ub9c8\ud30c\uc77c\uc5d0 \ubd80\uc2a4\ud2b8\ubc84\ud2bc\uc744 \ucd94\uac00\ud569\ub2c8\ub2e4.In myapp28, a boost button is added to the theme file. \ud83d\udc49\ud83c\udffb \ub0b4\uac00 \uc4f4 \uae00\uc5d0\ub294 \ubd80\uc2a4\ud2b8 \ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.You cannot boost your own posts. \ud83d\udc49\ud83c\udffb \ub0b4\uac00 \ubd80\uc2a4\ud2b8 \ubc84\ud2bc\uc744 \ub204\ub974\uace0 \ub2e4\uc2dc \ud55c\ubc88 \ub354 \ub204\ub974\uba74 \ubd80\uc2a4\ud2b8 \ucde8\uc18c\uac00 \ub429\ub2c8\ub2e4.If I press the boost button and then press it again, the boost is cancelled. \ud83d\udc49\ud83c\udffb [&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-9105","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\/9105","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=9105"}],"version-history":[{"count":55,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9105\/revisions"}],"predecessor-version":[{"id":9166,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/9105\/revisions\/9166"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=9105"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=9105"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=9105"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}