{"id":12731,"date":"2026-10-23T08:17:55","date_gmt":"2026-10-22T23:17:55","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=12731"},"modified":"2026-10-23T13:08:33","modified_gmt":"2026-10-23T04:08:33","slug":"nextjs-sns-server-51-7","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2026\/10\/23\/nextjs-sns-server-51-7\/","title":{"rendered":"[nextjs]SNS Server-51(myapp61+)[Signup,nextjs project upgrade,]\u00a0"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb \uc5ec\uae30\uc11c\ub294 \ud68c\uc6d0\uac00\uc785 \ubd80\ubd84\uc758 \uad6c\uc870\uc5d0 \ub300\ud574\uc11c \uc0b4\ud3b4\ubd05\ub2c8\ub2e4.<br>Here, we examine the structure of the sign-up section.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb nextjs\uc5c5\uadf8\ub808\uc774\ub4dc\ud558\ub824\uba74 \uc544\ub798\ucc98\ub7fc \ud558\uc2dc\uba74 \ub429\ub2c8\ub2e4.<br>To upgrade Next.js, you can follow the steps below.<\/p>\n\n\n\n<p>\ud83d\udcc1Nextjs \uc5c5\uadf8\ub808\uc774\ub4dc \/ Upgrade <\/p>\n\n\n\n<p>\u2714\ufe0f \ud504\ub85c\uc81d\ud2b8\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 \uba54\uc138\uc9c0\uac00 \ubcf4\uc774\uba74 \uc5c5\uadf8\ub808\uc774\ub4dc\ud558\uba74 \ub429\ub2c8\ub2e4.<br>If you see the following message in your project, you can perform an upgrade.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>\u26a0 Installed Next.js version 16.4.0 is affected by a known security vulnerability.\n\nNext.js security version upgrade available: 16.4.0 -&gt; 16.4.2\n\n  \u276f Upgrade now\n    Skip\n    Skip until next version<\/code><\/pre>\n\n\n\n<p>1)AI Agent\uac00 \uc790\ub3d9\uc5c5\uadf8\ub808\uc774\ub4dc \ud558\ub3c4\ub85d \ud5c8\uc6a9\ud558\ub824\uba74 \uc544\ub798\uc640 \uac19\uc774 next.config.ts\ud30c\uc77c\uc744 \uc218\uc815\ud574\uc57c\ud569\ub2c8\ub2e4.<br>To allow the AI \u200b\u200bAgent to perform automatic upgrades, you must modify the <code>next.config.ts<\/code> file as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import type { NextConfig } from \"next\";\n\nconst nextConfig: NextConfig = {\n\n  \/* Allow auto securiy upgrade for AI Agent *\/\n   experimental: {\n     agentUpgrade: \"security\"\n   },\n\n  \/* allow domain*\/\n  allowedDevOrigins: &#91;'aloy-horizon.duckdns.org', '*.duckdns.org'],\n\n  \/* host.domain.org\/@userid *\/\n  async rewrites() {\n    return &#91;\n      {\n        source: '\/@:username',\n        destination: '\/usersui\/:username',\n      },\n      {\n        source: '\/@:username\/following',\n        destination: '\/usersui\/:username?view=following', \/\/ \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  },\n\n};\n\nexport default nextConfig;\n<\/code><\/pre>\n\n\n\n<p>2)\uc218\ub3d9\uc73c\ub85c \uc5c5\uadf8\ub808\uc774\ub4dc\ud558\ub824\uba74 \uc544\ub798\ucc98\ub7fc \ud558\uba74\ub429\ub2c8\ub2e4.<br>To perform a manual upgrade, follow the steps below.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>npm install next@16.4.2<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udcc1 \uc678\ubd80 \uc571 \ubc0f \ub85c\uceec UI\ubd80\ubd84 \uc778\uc99d \ubd80\ubd84 \uad6c\uc870<br>Authentication-related structure for external apps and the local UI<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>app\/api\/auth\/\n  \u251c\u2500\u2500 signup\/route.ts      -&gt; \uc6f9\uc0ac\uc774\ud2b8 \ud68c\uc6d0\uac00\uc785 \/ website signup\n  \u251c\u2500\u2500 login\/route.ts       -&gt; \uc6f9\uc0ac\uc774\ud2b8 \ub85c\uadf8\uc778 \/ website login\n  \u251c\u2500\u2500 logout\/route.ts      -&gt; \ub85c\uadf8\uc544\uc6c3 \/ logout\n  \u251c\u2500\u2500 refresh\/route.ts     -&gt; refresh_token\uc73c\ub85c access token\uc7ac\ubc1c\uae09\n  |                        -&gt; Reissuing an access token using a refresh token\n  \u2514\u2500\u2500 me\/route.ts          -&gt; \ub85c\uadf8\uc778 \uccb4\ud06c \/ Login check\n\napp\/auth\/\n  \u251c\u2500\u2500 signup\/page.tsx      -&gt; \ud68c\uc6d0\uac00\uc785 UI \/ Sign-up UI\n  \u2514\u2500\u2500 login\/page.tsx       -&gt; \ub85c\uadf8\uc778 UI \/ Login UI\n\noauth\/authorize\/route.ts   -&gt; OAuth \ub3d9\uc758 \ud654\uba74 \/ OAuth consent screen\noauth\/token\/route.ts       -&gt; access_token \ubc1c\uae09 \/ Issuance of an access token\n\nlib\/auth.ts                -&gt; \uc2e4\uc81c \uc720\uc800 \uac80\uc99d \ub85c\uc9c1 (getUser, \uc138\uc158 \ud655\uc778)\n                           -&gt; Actual user verification logic (getUser, session check)<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \uc778\uc99d\uad00\ub828 \ub370\uc774\ud130\ubca0\uc774\uc2a4 \ud14c\uc774\ube14<br>Authentication-related database tables<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>users              -&gt; \ud68c\uc6d0\uac00\uc785, \uc720\uc800 \uc815\ubcf4 (username, password hash, mastodon_id \ub4f1)\n                      User registration, user information \n                      (username, password hash, mastodon_id, etc.)\n\nsessions           -&gt; \uc6f9\uc0ac\uc774\ud2b8 \ub85c\uadf8\uc778 \uc138\uc158 (refresh_token \ucfe0\ud0a4)\n                      Website login session (refresh_token cookie)\n\noauth_apps         -&gt; \uc678\ubd80 \uc571 \ub4f1\ub85d (Pinafore, Tusky\uac00 \ub4f1\ub85d\ud558\uba74 client_id \ubc1c\uae09)\n                      Registering an external app \n                      (a `client_id` is issued once an app like Pinafore or Tusky registers)\n\noauth_codes        -&gt; OAuth authorize \ucf54\ub4dc (\uc784\uc2dc)\n                      OAuth authorization code (temporary)\n\noauth_tokens       -&gt; access_token, refresh_token (\uc678\ubd80 \uc571 \ub85c\uadf8\uc778 \uc644\ub8cc \ud6c4)\n                      access_token, refresh_token (after external app login is complete)\n<\/code><\/pre>\n\n\n\n<p>\ud83d\udcc1 \ud68c\uc6d0\uac00\uc785 \/ join the membership<\/p>\n\n\n\n<p>&#8212; \uae30\ubcf8 \ub3d9\uc791 \ud750\ub984\uc740 \uc544\ub798\uc640 \uac19\uc2b5\ub2c8\ub2e4.<br>The basic operational flow is as follows.<\/p>\n\n\n\n<p>\u2714\ufe0f \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc2a4\ud0a4\ub9c8 \/ Database Schema<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>CREATE TABLE users (\n  username TEXT PRIMARY KEY,\n  display_name TEXT DEFAULT '',\n  summary TEXT DEFAULT '',\n  private_key TEXT,\n  public_key TEXT,\n  email TEXT UNIQUE,\n  password_hash TEXT,\n  email_verified INTEGER DEFAULT 0,\n  verification_token TEXT,\n  created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n, mastodon_id TEXT);<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ub77c\uc6b0\ud2b8 \uc2e4\ud589 \/ Execute route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>https:&#47;&#47;aloy-horizon.duckdns.org\/auth\/signup<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ud68c\uc6d0\uac00\uc785\ud654\uba74 \/ Sign-up Screen<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"623\" height=\"1024\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/signup-hello-623x1024.png\" alt=\"\" class=\"wp-image-12811\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/signup-hello-623x1024.png 623w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/signup-hello-183x300.png 183w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/signup-hello-400x657.png 400w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2026\/10\/signup-hello.png 644w\" sizes=\"auto, (max-width: 623px) 100vw, 623px\" \/><\/figure>\n\n\n\n<p>\u2714\ufe0f \ub370\uc774\ud130\ubca0\uc774\uc2a4 \uc800\uc7a5 \ud655\uc778 \/ Confirm Verify database storage<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>sqlite> select username,display_name,email from users where username='hello';\n<strong>hello|hello_display|hello@test.com<\/strong>\nsqlite> <\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ud0a4 \uc0dd\uc131 \ud655\uc778 \/ Confirm Key Generation<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... path... \/hello % pwd\n... path... \/data\/keys\/hello\nhello % ls\n<strong>private.pem     public.pem<\/strong>\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\u2714\ufe0f app\/auth\/signup\/page.tsx &#8211; UI<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code> ... ... \n async function handleSignup(e: React.FormEvent) {\n    e.preventDefault();\n    setError('');\n\n    const res = await fetch('\/api\/auth\/signup', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application\/json' },\n      body: JSON.stringify({ username, email, password, display_name: displayName })\n    });\n\n    const data = await res.json();\n    if (!res.ok) {\n      setError(data.error || '\ud68c\uc6d0\uac00\uc785 \uc2e4\ud328\/Join failed');\n      return;\n    }\n\n    alert(`\u2705 \ud68c\uc6d0\uac00\uc785 \uc131\uacf5\/join membership success! ${data.username} \uc774\uc81c \ub85c\uadf8\uc778 \/ Already login`);\n    router.push('\/auth\/login');\n  }\n... ...\nreturn (\n    &lt;div style={{ maxWidth: 400, margin: '80px auto', padding: 20 }}>\n      &lt;h1>\ud83d\udcdd \ud68c\uc6d0\uac00\uc785 \/ Signup- myapp39&lt;\/h1>\n      &lt;form onSubmit={handleSignup} \n         style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n        &lt;input placeholder=\"username (\uc601\ubb38, \uc22b\uc790, _)\" value={username} onChange={e=>setUsername(e.target.value)} style={{ padding: 10, fontSize: 16 }} required \/>\n... ...\n\n        {error &amp;&amp; &lt;p style={{ color: 'red' }}>{error}&lt;\/p>}\n        &lt;button type=\"submit\" style={{ padding: 12, fontSize: 16, background: '#2b90d9', color: 'white', border: 0, borderRadius: 8 }}>\n          \ud68c\uc6d0\uac00\uc785 \/ Signup\n        &lt;\/button>\n      &lt;\/form>\n      &lt;p style={{ marginTop: 20 }}>\n        &lt;a href=\"\/auth\/login\">\ub85c\uadf8\uc778 \/ Login&lt;\/a>\n      &lt;\/p>\n    &lt;\/div>\n  );\n... ...<\/code><\/pre>\n\n\n\n<p>A.\ud3fc \uc81c\ucd9c\ubc29\uc9c0 &#8211; \uc774 \ucf54\ub4dc\uac00 \uc788\uc5b4\uc57c \ud398\uc774\uc9c0 \uc774\ub3d9 \ud604\uc0c1\uc774 \uc77c\uc5b4\ub098\uc9c0 \uc54a\uc74c<br>Preventing form submission \u2013 this code is required to ensure the page does not navigate away.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>e.preventDefault();<\/code><\/pre>\n\n\n\n<p>B.API \ub77c\uc6b0\ud2b8\ub85c \ud3fc \ub370\uc774\ud130 \uc804\uc1a1<br>Sending form data to an API route<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code> async function <strong>handleSignup<\/strong>(e: React.FormEvent) {\n... ...\n    const res = await fetch('<strong>\/api\/auth\/signup<\/strong>', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application\/json' },\n      body: JSON.stringify({ username, email, password, display_name: displayName })\n    });\n... ...\n}\n... ...\n&lt;form <strong>onSubmit={handleSignup}<\/strong> style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n... ...\n &lt;button type=\"<strong>submit<\/strong>\" style={{ padding: 12, fontSize: 16, background: '#2b90d9', color: 'white', border: 0, borderRadius: 8 }}>\n          \ud68c\uc6d0\uac00\uc785 \/ Signup\n        &lt;\/button>\n... ...\n&lt;\/form><\/code><\/pre>\n\n\n\n<p>C.API\uc5d0\ub7ec \ubc1c\uc0dd\uc2dc UI\uc5d0 \uc5d0\ub7ec\ud45c\uc2dc<br>Display an error in the UI when an API error occurs.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ...\n   const data = await res.json();\n    if (!res.ok) {\n      setError(data.error || '\ud68c\uc6d0\uac00\uc785 \uc2e4\ud328\/Join failed');\n      return;\n    }\n... ... \n{error &amp;&amp; &lt;p style={{ color: 'red' }}>{error}&lt;\/p>}\n<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f app\/api\/auth\/signup\/route.ts &#8211; API<\/p>\n\n\n\n<p>&#8212; \ub370\uc774\ud130 \uccb4\ud06c \ubc0f \uc800\uc7a5 \/ Data check and storage<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>... ...\nexport async function POST(req: Request) {\n  try {\n    const { username, email, password, display_name } = await req.json();\n\n    if (!username || !email || !password) {\n      \/\/ error : No required input values\n      return NextResponse.json({ error: '\ud544\uc218 \uc785\ub825\uac12 \uc5c6\uc74c' }, { status: 400 });\n    }\n\n    if (username.length &lt; 3 || !\/^&#91;a-zA-Z0-9_]+$\/.test(username)) {\n      \/\/ error : Usernames must be at least 3 characters long and consist only of English letters and numbers.\n      return NextResponse.json({ error: 'username\uc740 3\uc790 \uc774\uc0c1 \uc601\ubb38, \uc22b\uc790\ub9cc' }, { status: 400 });\n    }\n\n    if (password.length &lt; 6) {\n      \/\/ error : Password must be at least 6 characters long\n      return NextResponse.json({ error: '\ube44\ubc00\ubc88\ud638 6\uc790 \uc774\uc0c1' }, { status: 400 });\n    }\n\n    \/\/ \uc911\ubcf5 \uccb4\ud06c \/ Duplicate check\n    if (getUser(username)) {\n      \/\/ error : Username already exists\n      return NextResponse.json({ error: '\uc774\ubbf8 \uc874\uc7ac\ud558\ub294 username' }, { status: 409 });\n    }\n    if (getUserByEmail(email)) {\n      \/\/ error : Email already exists!\n      return NextResponse.json({ error: '\uc774\ubbf8 \uc874\uc7ac\ud558\ub294 email!' }, { status: 409 });\n    }\n\n    <strong>const passwordHash = await hashPassword(password);<\/strong>\n   <strong> createUser(username, email, passwordHash, display_name || username);<\/strong>\n\n    console.log(`\u2705 \ud68c\uc6d0\uac00\uc785\/join the membership: ${username} \/ ${email}`);\n\n    return NextResponse.json({ ok: true, username }, { status: 201 });\n  } catch (e) {\n    console.error('signup \uc5d0\ub7ec \/ signup error', e);\n    return NextResponse.json({ error: '\uc11c\ubc84 \uc5d0\ub7ec \/ server error' }, { status: 500 });\n  }\n}<\/code><\/pre>\n\n\n\n<p>1) \ud328\uc2a4\uc6cc\ub4dc\ub97c hashPassword\ud568\uc218\ub85c \uc554\ud638\ud654 \ud569\ub2c8\ub2e4.<br>Encrypt the password using the <code>hashPassword<\/code> function.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const passwordHash = await hashPassword(password);<\/code><\/pre>\n\n\n\n<p>2) UI\uc5d0\uc11c \uc804\ub2ec \ubc1b\uc740 \ub370\uc774\ud130\ub97c \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud558\uace0 \ud0a4 \ud30c\uc77c \uc0dd\uc131<br>Save data received from the UI to the database and generate a key file<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>createUser(username, email, passwordHash, display_name || username);<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f lib\/auth.ts(hashPassword,createUser)<\/p>\n\n\n\n<p>&#8212; hashPassword \ud568\uc218\uc758 \ub2e4\uc74c\uacfc \uac19\uc2b5\ub2c8\ub2e4.<br>The <code>hashPassword<\/code> function is as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import bcrypt from 'bcrypt';\n... ...\nexport async function hashPassword(password: string) {\n  return await bcrypt.hash(password, 10);\n}<\/code><\/pre>\n\n\n\n<p>1)hashPassword\ud568\uc218\uc5d0 \ud30c\ub77c\uba54\ud130\ub85c \uc804\ub2ec\ubc1b\uc740 \ub0b4\uc6a9\uc744 bcrypt\ubaa8\ub4c8\ub85c \uc554\ud638\ud654 \ud569\ub2c8\ub2e4.<br>The content passed as a parameter to the <code>hashPassword<\/code> function is encrypted using the <code>bcrypt<\/code> module.<\/p>\n\n\n\n<p>&#8212; createUser \ud568\uc218\uc758 \uad6c\uc870\ub294 \ub2e4\uc74c\uacfc \uac19\uc2b5\ub2c8\ub2e4.<br>The structure of the createUser function is as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>import db from '.\/db';\n... ...\nexport function createUser(username: string, email: string, passwordHash: string, displayName: string = '') {\n  const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {\n    modulusLength: 2048,\n    publicKeyEncoding: { type: 'spki', format: 'pem' },\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n  });\n\n  const userKeyDir = path.join(process.cwd(), `data\/keys\/${username}`);\n  if (!fs.existsSync(userKeyDir)) fs.mkdirSync(userKeyDir, { recursive: true });\n\n  fs.writeFileSync(path.join(userKeyDir, 'private.pem'), privateKey);\n  fs.writeFileSync(path.join(userKeyDir, 'public.pem'), publicKey);\n  console.log(`\u2705 \ud0a4 \ud30c\uc77c \uc800\uc7a5\/save key file: data\/keys\/${username}\/`);\n\n  \/\/ return db.prepare(`\n  \/\/   INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, created_at)\n  \/\/   VALUES (?,?, '',?,?,?,?, 1, CURRENT_TIMESTAMP)\n  \/\/ `).run(username, displayName || username, privateKey, publicKey, email, passwordHash);\n\n  \/\/ \u2705 myapp58 - mastodon_id \uc0dd\uc131 \/ create mastodon_id\n  const mastodonId = crypto.randomUUID().slice(0, 8); \n\n  return db.prepare(`\n    INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, mastodon_id, created_at)\n    VALUES (?,?, '',?,?,?,?, 1, ?, CURRENT_TIMESTAMP)\n  `).run(username, displayName || username, privateKey, publicKey, email, passwordHash, mastodonId);\n\n}<\/code><\/pre>\n\n\n\n<p>1)crypto\ubaa8\ub4c8\ub85c \ud0a4\ud30c\uc77c \uc0dd\uc131\ud558\ub294 \ubd80\ubd84\uc785\ub2c8\ub2e4.<br>This part handles the generation of key files using the <code>crypto<\/code> module.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {\n    modulusLength: 2048,\n    publicKeyEncoding: { type: 'spki', format: 'pem' },\n    privateKeyEncoding: { type: 'pkcs8', format: 'pem' }\n  });\n\n  const userKeyDir = path.join(process.cwd(), `data\/keys\/${username}`);\n  if (!fs.existsSync(userKeyDir)) fs.mkdirSync(userKeyDir, { recursive: true });\n\n  fs.writeFileSync(path.join(userKeyDir, 'private.pem'), privateKey);\n  fs.writeFileSync(path.join(userKeyDir, 'public.pem'), publicKey);\n <\/code><\/pre>\n\n\n\n<p>A.\ud0a4 2\uac1c \uc0dd\uc131 \/ Generate 2 keys<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>1. \ucf54\ub4dc\uc124\uba85 \/ Code Explanation\nmodulusLength: 2048\n- \ud0a4 \uae38\uc774. 2048\ube44\ud2b8. ActivityPub \ud45c\uc900. \uae38\uc218\ub85d \uc548\uc804, \ub290\ub9bc.\nKey length. 2048 bits. ActivityPub standard. Longer keys are more secure, but slower.\n\npublicKeyEncoding: spki, pem \n- \uacf5\uac1c\ud0a4 \uc800\uc7a5 \ud615\uc2dd \/ Public Key Storage Format\n\nprivateKeyEncoding: pkcs8, pem\n- \uac1c\uc778\ud0a4 \uc800\uc7a5 \ud615\uc2dd \/ Private Key Storage Format\n\n2. \ub2e4\uc74c\uacfc \uac19\uc740 \ud615\uc2dd\uc73c\ub85c \uc800\uc7a5 \/ Save in the following format\nprivateKey = \"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADAN...\"\npublicKey = \"-----BEGIN PUBLIC KEY-----\\nMIIBIjAN...\"<\/code><\/pre>\n\n\n\n<p>B.\ub514\ub809\ud1a0\ub9ac \ub9cc\ub4e4\uae30 \/ Create a directory<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>const userKeyDir = path.join(process.cwd(), `data\/keys\/${username}`);\nif (!fs.existsSync(userKeyDir)) fs.mkdirSync(userKeyDir, { recursive: true });\n\n# ---- ----\n\n1.\ucf54\ub4dc\uc124\uba85 \/ Code Explanation\nprocess.cwd() = \ud504\ub85c\uc81d\ud2b8 \ub8e8\ud2b8 \/ Project root (\/Users\/...\/myapp61)\npath = data\/keys\/bob(username)\n\n\uacbd\ub85c\uc5d0 \ub514\ub809\ud1a0\ub9ac\uac00 \uc5c6\uc73c\uba74 mkdir -p \ub85c \ub514\ub809\ud1a0\ub9ac \uc0dd\uc131\nIf the directory does not exist in the path, create it using `mkdir -p`.\n\n2.\ub514\ub809\ud1a0\ub9ac \uc0dd\uc131 \uc608\uc2dc \/ Example of directory creation\ndata\/keys\/\n  \u251c\u2500\u2500 bob\/\n  \u251c\u2500\u2500 alice\/\n  \u2514\u2500\u2500 charlie\/<\/code><\/pre>\n\n\n\n<p>C.\ud30c\uc77c\ub85c \uc800\uc7a5 \/ Save as a file<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>fs.writeFileSync(path.join(userKeyDir, 'private.pem'), privateKey);\nfs.writeFileSync(path.join(userKeyDir, 'public.pem'), publicKey);\n\n# ---- ----\n\n1. \ucf54\ub4dc\uc124\uba85 \/ Code Explanation\nuserKeyDir(\uc804\uccb4\uacbd\ub85c)\uc544\ub798 private.pem publick.pem\ud30c\uc77c\uc744 \uc0dd\uc131\ud568.\nGenerates `private.pem` and `public.pem` files under `userKeyDir` (full path).<\/code><\/pre>\n\n\n\n<p>D. \ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0\ub3c4 \ud0a4 \uc800\uc7a5 \/ Storing keys in the database as well<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  return db.prepare(`\n    INSERT INTO users (... ... <strong>private_key, public_key<\/strong>, ... ...)\n    VALUES (?,?, '',?,?,?,?, 1, ?, CURRENT_TIMESTAMP)\n  `).run(... ..., <strong>privateKey, publicKey<\/strong>, ... ...);<\/code><\/pre>\n\n\n\n<p>2)\ub370\uc774\ud130\ubca0\uc774\uc2a4\uc5d0 \uc815\ubcf4\ub97c \uc800\uc7a5\ud558\ub294 \ubd80\ubd84\uc785\ub2c8\ub2e4.<br>This is the part that stores information in the database.<\/p>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:14px\"><code>  return db.prepare(`\n    INSERT INTO users (username, display_name, summary, private_key, public_key, email, password_hash, email_verified, mastodon_id, created_at)\n    VALUES (?,?, '',?,?,?,?, 1, ?, CURRENT_TIMESTAMP)\n  `).run(username, displayName || username, privateKey, publicKey, email, passwordHash, mastodonId);<\/code><\/pre>\n\n\n\n<p>1)\uc704\uc758 \ud0a4\uc640 \ud568\uaed8 \uc804\ub2ec\ubc1b\uc740 \ud68c\uc6d0\uac00\uc785\uc815\ubcf4\ub97c \ub370\uc774\ud130 \ubca0\uc774\uc2a4\uc5d0 \uc800\uc7a5\ud569\ub2c8\ub2e4.<br>Store the user registration information received along with the key mentioned above in the database.<\/p>\n\n\n\n<p>\ud83d\udca1 lib\/db.ts \ud30c\uc77c\uc124\uba85\uc740 \uc0dd\ub7b5\ud569\ub2c8\ub2e4.<br>I will skip the explanation for the <code>lib\/db.ts<\/code> file.<\/p>\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","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb \uc5ec\uae30\uc11c\ub294 \ud68c\uc6d0\uac00\uc785 \ubd80\ubd84\uc758 \uad6c\uc870\uc5d0 \ub300\ud574\uc11c \uc0b4\ud3b4\ubd05\ub2c8\ub2e4.Here, we examine the structure of the sign-up section. \ud83d\udc49\ud83c\udffb nextjs\uc5c5\uadf8\ub808\uc774\ub4dc\ud558\ub824\uba74 \uc544\ub798\ucc98\ub7fc \ud558\uc2dc\uba74 \ub429\ub2c8\ub2e4.To upgrade Next.js, you can follow the steps below. \ud83d\udcc1Nextjs \uc5c5\uadf8\ub808\uc774\ub4dc \/ Upgrade \u2714\ufe0f \ud504\ub85c\uc81d\ud2b8\uc5d0 \ub2e4\uc74c\uacfc \uac19\uc740 \uba54\uc138\uc9c0\uac00 \ubcf4\uc774\uba74 \uc5c5\uadf8\ub808\uc774\ub4dc\ud558\uba74 \ub429\ub2c8\ub2e4.If you see the following message in your project, you can perform an upgrade. 1)AI Agent\uac00 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":12811,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[28,27,1],"tags":[],"class_list":["post-12731","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news","category-nextjs","category-uncategorized","has-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/12731","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=12731"}],"version-history":[{"count":72,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/12731\/revisions"}],"predecessor-version":[{"id":12812,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/12731\/revisions\/12812"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media\/12811"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=12731"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=12731"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=12731"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}