{"id":8712,"date":"2026-08-25T07:59:03","date_gmt":"2026-08-24T22:59:03","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=8712"},"modified":"2026-08-25T10:37:55","modified_gmt":"2026-08-25T01:37:55","slug":"nextjs-sns-server","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/08\/25\/nextjs-sns-server\/","title":{"rendered":"[nextjs]SNS Server-12(myapp22)"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb \ud314\ub85c\uc789\uacfc \ud314\ub85c\uc6cc\uac00 \ud14c\uc774\ube14\uc5d0 \uc800\uc7a5\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\ub97c \uc218\uc815\ud558\uc600\uc2b5\ub2c8\ub2e4.<br>Fixed an issue where following and follower data were not being saved to the table.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb IGNORE INTO\ub97c \ucd94\uac00\ud574\uc11c \ub370\uc774\ud130 \uc911\ubcf5\uc785\ub825 \ubc1c\uc0dd\uc2dc \uc5d0\ub7ec\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uace0 \uc9c0\ub098\uac11\ub2c8\ub2e4.<br>By adding <code>IGNORE INTO<\/code>, the operation proceeds without throwing an error when duplicate data is encountered.<\/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>myapp22\/  \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\/posts\/route.ts     -&gt; Writing API\n\u2502   \u251c\u2500\u2500 api\/follow\/route.ts    -&gt; Follow API(temporary)\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]\/page.tsx  -&gt; Timeline UI\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 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\u2514\u2500\u2500 package.json<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud504\ub85c\uc81d\ud2b8 \uc2dc\uc791(myapp22)<br>Project Start (myapp22)<\/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 ~\/myapp22\nnpm install better-sqlite3\nnpm install -D @types\/better-sqlite3<\/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=\"2wF0swXgO9\"><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=WZYMmM03ef#?secret=2wF0swXgO9\" data-secret=\"2wF0swXgO9\" 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 \ucf54\ub4dc \uc218\uc815 \/ Code modification<\/p>\n\n\n\n<p>\u2714\ufe0f myapp22\/app\/users\/[username]\/inbox\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>      \/\/ \u2705 myapp22 - insert or ignore\n      try {\n<strong>        db.prepare('INSERT OR IGNORE followers (id, actor, inbox, username) VALUES (?,?,?,?)')\n          .run(body.id, actorId, inboxUrl, username);<\/strong>\n        console.log(`\u2705 &#91;${username}] \ud314\ub85c\uc6b0 \uc800\uc7a5 \/ follow save : ${actorId}`);\n      } catch (e: any) {\n        if (e.code === 'SQLITE_CONSTRAINT_UNIQUE') {\n          console.log(`\u2139\ufe0f \uc911\ubcf5 \ud314\ub85c\uc6cc \ucc28\ub2e8 \/ Block Duplicate Followers (UNIQUE): ${actorId}`);\n          return new Response('', { status: 202 });\n        }\n        throw e;\n      }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f myapp22\/api\/follow\/route.ts<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/\/ myapp22 \u2705 \n    \/\/ \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc911\ubcf5 \uc785\ub825 \ubc29\uc9c0 \/ Preventing duplicate entries in the database\n    if (result.ok) {\n      try {\n<strong>        db.prepare('INSERT OR IGNORE INTO following (id, actor, username) VALUES (?, ?, ?)')\n          .run(result.followDoc.id, target, username);<\/strong>\n        console.log(`\u2705 DB \uc800\uc7a5 \/ DB Save: ${username} -&gt; ${target}`);\n      } catch (e: any) {\n        if (e.code === 'SQLITE_CONSTRAINT_UNIQUE') {\n          console.log(`\u2139\ufe0f \uc774\ubbf8 \uc800\uc7a5\ub428  \/ Already saved: ${target}`);\n        } else {\n          console.error('DB \uc800\uc7a5 \uc2e4\ud328 \/ DB save failed:', e);\n        }\n      }\n    }<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f myapp22\/lib\/db.ts<\/p>\n\n\n\n<p>&#8212;  DB\uc2a4\ud0a4\ub9c8 \uc7ac\ubc18\uc601(\ud504\ub85c\uc81d\ud2b8 \ucf54\ub4dc \ucc38\uc870)<br>Re-apply DB schema (refer to project code)<\/p>\n\n\n\n<p>\ud83d\udcc1 \ud14c\uc2a4\ud2b8 \/ Test<\/p>\n\n\n\n<p>\u2714\ufe0f \ud130\ubbf8\ub110\uc5d0\uc11c \uc544\ub798\uc758 \ub77c\uc6b0\ud2b8\ub85c \ud14c\uc2a4\ud2b8 \ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>You can test using the route below in the terminal.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \ud314\ub85c\uc6b0 (following 1\ub85c \uc99d\uac00)\n# Follow (following increased to 1)\n\ncurl -X POST https:\/\/aloy-horizon.duckdns.org\/api\/follow \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\"username\":\"user1\",\"target\":\"https:\/\/freelifemakers.com\/users\/user1\"}'\n\n# \uc5b8\ud314\ub85c\uc6b0 (following 0\uc73c\ub85c \uac10\uc18c)\n# Unfollow (following count reduced to 0)\n\ncurl -X DELETE https:\/\/aloy-horizon.duckdns.org\/api\/follow \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\"username\":\"user1\",\"target\":\"https:\/\/freelifemakers.com\/users\/user1\"}'\n\n# \uae00 \uc4f0\uae30 (outbox 7\ub85c \uc99d\uac00 + \ud314\ub85c\uc6cc\ub4e4\uc5d0\uac8c \ubc30\ub2ec)\n# Writing a post (added to outbox 7 + delivered to followers)\n\ncurl -X POST https:\/\/aloy-horizon.duckdns.org\/api\/posts \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\"username\":\"user1\",\"content\":\"Hello Follow! #test\"}'<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f https:\/\/freelifemakers.com\/users\/user1 \uc5d0\uac8c \ud314\ub85c\uc6b0\ub97c \uc694\uccad\ud558\uba74 following \ud14c\uc774\ube14\uc5d0 \ub370\uc774\ud130 \uc800\uc7a5\ub418\ub294\uc9c0\ud655\uc778\ud569\ub2c8\ub2e4.<br>Check if data is saved to the <code>following<\/code> table when a follow request is sent to https:\/\/freelifemakers.com\/users\/user1.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \ud314\ub85c\uc6b0 \uc694\uccad \/ Follow request\nmyapp22 % curl -X POST https:\/\/aloy-horizon.duckdns.org\/api\/follow \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\"username\":\"user1\",\"target\":\"https:\/\/freelifemakers.com\/users\/user1\"}'\n\n# \uc751\ub2f5 \/ response\n{\"ok\":true,\"inbox\":\"https:\/\/freelifemakers.com\/users\/user1\/inbox\",\"target\":\"https:\/\/freelifemakers.com\/users\/user1\",\"username\":\"user1\",\"result\":\"{\\\"status\\\":\\\"Accepted\\\"}\",\"follow\":{\"@context\":\"https:\/\/www.w3.org\/ns\/activitystreams\",\"id\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\/follows\/1787615594327\",\"type\":\"Follow\",\"actor\":\"https:\/\/aloy-horizon.duckdns.org\/users\/user1\",\"object\":\"https:\/\/freelifemakers.com\/users\/user1\"}}%                                \n\n# \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \ub370\uc774\ud130 \uc800\uc7a5 \uc0c1\ud0dc \ud655\uc778 \/ Checking the data storage status in the database\nmyapp22 % sqlite3 data.sqlite \"SELECT * FROM following;\"\nhttps:\/\/aloy-horizon.duckdns.org\/users\/user1\/follows\/1787615594327|https:\/\/freelifemakers.com\/users\/user1|user1|2026-08-24 23:53:14\ngimdaegyeong@gimdaegyeong-ui-MacBookAir myapp22 % <\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f freelifemakers.com\/@user1(pinafore)\uc774 aloy-horizon.ducdns.org\uc5d0\uac8c \ud314\ub85c\uc6b0\ub97c \uc694\uccad\ud558\uba74 follower\ud14c\uc774\ube14\uc5d0 \uc800\uc7a5\ub429\ub2c8\ub2e4.<br>When freelifemakers.com\/@user1(pinafore) requests to follow aloy-horizon.ducdns.org, the request is stored in the <code>follower<\/code> table.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># sqlite db \uc811\uc18d \/ Connect to SQLite database\nmyapp22 % sqlite3 data.sqlite\nSQLite version 3.51.0 2025-06-12 13:14:41\nEnter \".help\" for usage hints.\nsqlite> <strong>select * from followers<\/strong>;\nhttps:&#47;&#47;freelifemakers.com\/users\/user1\/follow\/01RR6E0D7QZA98S06VJYC99FNH|https:\/\/freelifemakers.com\/users\/user1|https:\/\/freelifemakers.com\/users\/user1\/inbox|user1\nsqlite> <\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f API \ud655\uc778 \/ Check API<\/p>\n\n\n\n<p>&#8212; \ubc30\uc5f4\uc5d0 \uc800\uc7a5\ub41c \uac12\uc774 \ucd9c\ub825\ub418\ub294\uc9c0 \ud655\uc778\ud569\ub2c8\ub2e4.<br>Check whether the values \u200b\u200bstored in the array are output.<\/p>\n\n\n\n<p>&#8212; following<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;aloy-horizon.duckdns.org\/users\/user1\/following<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"375\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-following-jpg.jpg\" alt=\"\" class=\"wp-image-8751\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-following-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-following-jpg-300x141.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-following-jpg-768x360.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-following-jpg-400x188.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">following API<\/figcaption><\/figure>\n\n\n\n<p>&#8212; followers<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>https:&#47;&#47;aloy-horizon.duckdns.org\/users\/user1\/followers<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"534\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-followers-jpg.jpg\" alt=\"\" class=\"wp-image-8754\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-followers-jpg.jpg 800w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-followers-jpg-300x200.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-followers-jpg-768x513.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/08\/myapp22-followers-jpg-400x267.jpg 400w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><figcaption class=\"wp-element-caption\">followers API<\/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 \ud314\ub85c\uc789\uacfc \ud314\ub85c\uc6cc\uac00 \ud14c\uc774\ube14\uc5d0 \uc800\uc7a5\ub418\uc9c0 \uc54a\ub294 \ubb38\uc81c\ub97c \uc218\uc815\ud558\uc600\uc2b5\ub2c8\ub2e4.Fixed an issue where following and follower data were not being saved to the table. \ud83d\udc49\ud83c\udffb IGNORE INTO\ub97c \ucd94\uac00\ud574\uc11c \ub370\uc774\ud130 \uc911\ubcf5\uc785\ub825 \ubc1c\uc0dd\uc2dc \uc5d0\ub7ec\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uc9c0 \uc54a\uace0 \uc9c0\ub098\uac11\ub2c8\ub2e4.By adding IGNORE INTO, the operation proceeds without throwing an error when duplicate data is encountered. \ud83d\udcc1 \uc804\uccb4 \ud504\ub85c\uc81d\ud2b8 \uad6c\uc870 \/ Overall Project [&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-8712","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\/8712","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=8712"}],"version-history":[{"count":38,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/8712\/revisions"}],"predecessor-version":[{"id":8757,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/8712\/revisions\/8757"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=8712"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=8712"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=8712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}