{"id":1154,"date":"2025-04-18T09:42:49","date_gmt":"2025-04-18T09:42:49","guid":{"rendered":"https:\/\/www.freelifemakers.org\/wordpress\/?p=1154"},"modified":"2026-07-14T15:13:45","modified_gmt":"2026-07-14T06:13:45","slug":"nodejs7-login-logout","status":"publish","type":"post","link":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/2025\/04\/18\/nodejs7-login-logout\/","title":{"rendered":"nodejs7 &#8211; \ub85c\uadf8\uc778,\ub85c\uadf8\uc544\uc6c3(\ubc30\uc5f4)\/Login ,Logout(Array)"},"content":{"rendered":"\n<p>\ud83d\udc49\ud83c\udffb \ub85c\uadf8\uc778 \ub85c\uadf8\uc544\uc6c3 \uad6c\ud604\ud558\uae30 \uc704\ud55c \uae30\ubcf8 \ub85c\uc9c1\uc5d0 \ub300\ud55c \ucf54\ub4dc\uc785\ub2c8\ub2e4.<br>This is the code for the basic logic used to implement login and logout functionality.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ub370\uc774\ud130 \ubca0\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 \ubc30\uc5f4\uc744 \uc0ac\uc6a9\ud569\ub2c8\ub2e4.<br>It uses an array instead of a database.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc2e4\uc81c\ub85c \uad6c\ud604\ub418\ub294 \ub85c\uadf8\uc778 \uae30\ub2a5\uc740 \ud6e8\uc52c \ub354 \ubcf5\uc7a1\ud558\uc9c0\ub9cc \uc774 \ucf54\ub4dc\ub97c \ud1b5\ud574\uc11c \uac04\ub2e8\ud55c \uac1c\ub150\uc744 \uc774\ud574\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. <br>While the actual implementation of a login function is far more complex, this code allows you to understand the basic concepts.<\/p>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ud30c\uc77c \/ Files<\/p>\n\n\n\n<p>&#8212; node_modules,package-lock.json,package.json \uc774 \ub514\ub809\ud1a0\ub9ac\uc640 \ud30c\uc77c\uc740 npm install \uba85\ub839\uc5b4 \uc2e4\ud589\ud558\uba74 \uc790\ub3d9\uc0dd\uc131\ub429\ub2c8\ub2e4.<br>The <code>node_modules<\/code> directory and the <code>package-lock.json<\/code> and <code>package.json<\/code> files are automatically generated when you run the <code>npm install<\/code> command.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># projet directory\n\nnode_modules\t\tpackage-lock.json\tpackage.json\t\tpublic\t\t\tserver.js<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># public directory\n\nlogin.ejs<\/code><\/pre>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ubaa8\ub4c8 \uc124\uce58 \/ Install Module<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> npm install express express-session bcrypt ejs<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc804\uccb4 \ucf54\ub4dc \/ Full Code <\/p>\n\n\n\n<p>\u2714\ufe0fserver.js<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const express = require('express');\nconst session = require('express-session');\nconst bcrypt = require('bcrypt');\nconst app = express();\nconst port = 3000;\n\n\/\/ \uac04\ub2e8\ud55c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130\ubca0\uc774\uc2a4 (\ubc30\uc5f4) : simple user database (array)\nconst users = &#91;\n  { id: 1, username: 'testuser', passwordHash: '$2b$10$abKhb8aJsQ02T2TwSi0J4eVK42eRojFDTe.3EmWUQkdiXvaFVboxq' } \/\/ '11111111'\uc758 hash\n];\n\n\/\/ \ubbf8\ub4e4\uc6e8\uc5b4 \uc124\uc815 : middleware settings\napp.use(express.urlencoded({ extended: false }));\napp.use(express.json());\napp.use(session({\n  secret: 'your-secret-key',\n  resave: false,\n  saveUninitialized: false,\n  cookie: { secure: false }\n}));\n\n\/\/ \ub85c\uadf8\uc778 \uc0c1\ud0dc \ud655\uc778 \ubbf8\ub4e4\uc6e8\uc5b4 (\ubaa8\ub4e0 \uc694\uccad\uc5d0\uc11c \uc2e4\ud589) : middleware for loging status checking \napp.use((req, res, next) => {\n\n  \/\/ \uae30\ubcf8\uac12 \uc138\ud305 (\ub85c\uadf8\uc778 \uc548 \ub41c \uc0c1\ud0dc\ub97c \uac00\uc815) : Default settings (assuming the user is not logged in)\n  res.locals.isLoggedIn = false;\n  res.locals.userid = \"\";\n  res.locals.name = \"\";\n  res.locals.loggedInUser = null;\n\n  \/\/ \ub85c\uadf8\uc778 \uc138\uc158(member)\uc774 \uc874\uc7ac\ud558\ub294 \uacbd\uc6b0 \ub370\uc774\ud130 \ucc44\uc6cc\ub123\uae30 : If the login session (member) exists, fill in the data\n  if (req.session.member) {\n    res.locals.isLoggedIn = true;\n    res.locals.userid = req.session.member.userid;\n    res.locals.name = req.session.member.name;\n    \n    \/\/ \ub370\uc774\ud130\ubca0\uc774\uc2a4(users \ubc30\uc5f4)\uc5d0\uc11c \ub85c\uadf8\uc778\ud55c \uc720\uc800 \uac1d\uccb4 \ucc3e\uc544\uc11c \ub123\uc5b4\uc8fc\uae30\n     \/\/ Find the logged-in user object in the database (users array) and assign it.\n    res.locals.loggedInUser = users.find(user => user.username === req.session.member.userid) || null;\n  }\n\n  next(); \/\/ \ub2e4\uc74c \ub77c\uc6b0\ud130\ub85c ... \/ To the next router...\n});\n\n\n\/\/ \ubdf0 \uc5d4\uc9c4 \uc124\uc815 (ejs \uc0ac\uc6a9) : 'view engine settings(use ejs)\napp.set('view engine', 'ejs');\napp.set('views', '.\/public');\n\n\/\/ \uc815\uc801 \ud30c\uc77c \uc81c\uacf5 (css \ub4f1) : static file setting (css) \napp.use(express.static('public'));\n\n\/\/ bcrypt\ud328\uc2a4\uc6cc\ub4dc \uc0dd\uc131 : generate bcrype password\nasync function generateHash(password) {\n  try {\n      const saltRounds = 10;\n      const hash = await bcrypt.hash(password, saltRounds);\n      console.log(\"\ud574\uc2dc\ub41c \ube44\ubc00\ubc88\ud638\/Hashed password:\", hash);\n      return hash;\n  } catch (err) {\n      console.error(\"\ud574\uc2f1 \uc624\ub958\/Hashing error:\", err);\n  }\n}\n\n\n\n\/\/ \ub8e8\ud2b8 \ub77c\uc6b0\ud130 : root router\napp.get('\/', (req, res) => {\n  res.render('login');\n});\n\n\/\/ \ub85c\uadf8\uc778 \ud3fc \ud45c\uc2dc :  login form \napp.get('\/login', (req, res) => {\n  res.render('login');\n});\n\n\n\n\/\/ \ub85c\uadf8\uc778 \ucc98\ub9ac : login process\napp.post('\/loginProc', async (req, res) => {\n  const { username, password } = req.body;\n  const user = users.find(u => u.username === username);\n  \n  \/\/ generate bcrypt password\n  const bbcrypt = await generateHash(password);\n  \/\/console.log(bbcrypt);\n  \n  \n  if (!user) {\n    return res.send('\ub85c\uadf8\uc778 \uc2e4\ud328 \/login failed: , \uc0ac\uc6a9\uc790 \uc5c6\uc74c \/ cannot find user');\n  }\n\n  \/\/ search user\n  const isMatch = await bcrypt.compare(password, user.passwordHash);\n\n  if (isMatch) {\n    req.session.member = {\n      userid: req.body.username, \/\/ \n      name: 'anomymous' \/\/ \n  };\n    res.redirect('\/');\n  } else {\n    res.send('\ub85c\uadf8\uc778 \uc2e4\ud328 \/ login failed: ,\ube44\ubc00\ubc88\ud638 \ubd88\uc77c\uce58 \/ password error');\n  }\n});\n\n\/\/ \ub85c\uadf8\uc544\uc6c3 \ucc98\ub9ac : logout process\napp.get('\/logout', (req, res) => {\n  req.session.destroy((err) => {\n    if (err) {\n      console.error('\uc138\uc158 \uc0ad\uc81c \uc2e4\ud328 session delete failed:', err);\n      res.send('\ub85c\uadf8\uc544\uc6c3 \uc2e4\ud328 logout failed');\n    } else {\n      res.redirect('\/');\n    }\n  });\n});\n\napp.listen(port, () => {\n  console.log(`\uc11c\ubc84\uac00 http:\/\/localhost:${port} \uc5d0\uc11c \uc2e4\ud589 \uc911\uc785\ub2c8\ub2e4.\\nServer is running at http:\/\/localhost:${port}`);\n});\n\n\/\/ npm install express express-session bcrypt ejs\n<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \/public\/login.ejs<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html>\n&lt;html>\n&lt;head>\n    &lt;title>Login&lt;\/title>\n    &lt;style>\n    input&#91;type=\"submit\"] {\n        background-color: #4CAF50;\n        color: white;\n        padding: 10px 20px;\n        border: none;\n        border-radius: 4px;\n        cursor: pointer;\n    }\n    input&#91;type=\"submit\"]:hover {\n        background-color: #45a049;\n    }\n    input&#91;type=\"text\"],\n    input&#91;type=\"password\"],\n    textarea,\n    select {\n        \/* \uc785\ub825 \ud544\ub4dc\uc758 \uac00\ub85c \uc0ac\uc774\uc988\ub97c \ud3fc\uc5d0 \ub9de\ucda4  \/ \nFit the width of the input field to the form *\/\n        width: 20%; \n        padding: 8px;\n        margin-bottom: 10px;\n        border: 1px solid #ccc;\n        border-radius: 4px;\n        box-sizing: border-box;\n    }\n    &lt;\/style>\n&lt;\/head>\n&lt;body>\n    &lt;div>\n        &lt;%if(locals.userid){ %> ID:&lt;%= locals.userid %> \uc774\ub984:&lt;%= locals.name%> \n            &lt;a href=\"\/logout\">Log out&lt;\/a>\n        &lt;%}else{%>\n            &lt;form id=\"loginForm\" action=\"\/loginProc\" method=\"post\">\n\n                &lt;label for=\"title\">Login:&lt;\/label>\n                &lt;input type=\"text\" id=\"username\" name=\"username\" required>&lt;br>&lt;br>\n    \n                &lt;label for=\"title_group\">Password:&lt;\/label>\n                &lt;input type=\"password\" id=\"password\" name=\"password\" required>&lt;br>&lt;br>\n    \n                &lt;input type=\"submit\" value=\"submit\">\n            &lt;\/form>  \n        &lt;%}%>\n    &lt;\/div>\n&lt;\/body>\n&lt;\/html>\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \uc2e4\ud589 \/ Run<\/p>\n\n\n\n<p>\u2714\ufe0f \uc11c\ubc84\uc2e4\ud589 \/ Run server<\/p>\n\n\n\n<p>&#8212; server.js\ub97c \uc544\ub798\ucc98\ub7fc \uc2e4\ud589\ud569\ub2c8\ub2e4.<br>Run server.js as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>nodejs7 % <strong>node server.js<\/strong>\n\n\uc11c\ubc84\uac00 http:\/\/localhost:3000 \uc5d0\uc11c \uc2e4\ud589 \uc911\uc785\ub2c8\ub2e4.\nServer is running at http:\/\/localhost:3000<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ube0c\ub77c\uc6b0\uc800 \uc811\uc18d \/ Access Browser<\/p>\n\n\n\n<p>&#8212; http:\/\/localhost:3000\uc73c\ub85c \ube0c\ub77c\uc6b0\uc800\uc5d0 \uc811\uc18d\ud569\ub2c8\ub2e4.<br>Access http:\/\/localhost:3000 in your browser.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"974\" height=\"568\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/br1-jpg.jpg\" alt=\"\" class=\"wp-image-6418\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/br1-jpg.jpg 974w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/br1-jpg-300x175.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/br1-jpg-768x448.jpg 768w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/br1-jpg-400x233.jpg 400w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/br1-jpg-800x467.jpg 800w\" sizes=\"auto, (max-width: 974px) 100vw, 974px\" \/><figcaption class=\"wp-element-caption\">Access Browser<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f \uc544\uc774\ub514\uc5d0 testuser \ud328\uc2a4\uc6cc\ub4dc\ub294 11111111 \uc744 \uc785\ub825\ud558\uace0 submit\ubc84\ud2bc\uc744 \ub204\ub985\ub2c8\ub2e4. \uadf8\ub7ec\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \ud654\uba74\uc744 \ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4.<br>Enter &#8220;testuser&#8221; for the ID and &#8220;11111111&#8221; for the password, then click the submit button. You will then see the following screen.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"730\" height=\"356\" src=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/loginok-jpg.jpg\" alt=\"\" class=\"wp-image-6423\" srcset=\"https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/loginok-jpg.jpg 730w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/loginok-jpg-300x146.jpg 300w, https:\/\/www.freelifemakers.org\/wordpress\/wp-content\/uploads\/2025\/04\/loginok-jpg-400x195.jpg 400w\" sizes=\"auto, (max-width: 730px) 100vw, 730px\" \/><figcaption class=\"wp-element-caption\">\ub85c\uadf8\uc778 \uc644\ub8cc \ud654\uba74 \/ Login Complete Screen<\/figcaption><\/figure>\n\n\n\n<p>\u2714\ufe0f \ud130\ubbf8\ub110\uc5d0\ub294 \uc785\ub825\ub41c \ube44\ube4c\ubc88\ud638\ub97c \uc554\ud638\ud654\ud55c \ucf54\ub4dc\ub97c \ucd9c\ub825\ud574\uc90d\ub2c8\ub2e4.<br>The terminal outputs a code representing the encrypted version of the entered password.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\ud574\uc2dc\ub41c \ube44\ubc00\ubc88\ud638\/Hashed password: $2b$10$ZYVlLeO2BIWLyWHsdZgRYuFMn6Tb2C.5IeF6.mQFOf65ZTdtAmPLe<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>\ud83d\udc49\ud83c\udffb \ucf54\ub4dc \uc124\uba85  \/ Code Explanation<\/p>\n\n\n\n<p>\u2714\ufe0f \uc124\uce58\ub41c \ubaa8\ub4c8 \ubd88\ub7ec\uc624\uae30<br>Load installed modules<\/p>\n\n\n\n<p>&#8212; npm install\ub85c \uc124\uce58\ub41c \ubaa8\ub4c8\uc744 \ubd88\ub7ec\uc635\ub2c8\ub2e4.<br>Import the module installed via npm install.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const express = require('express');\nconst session = require('express-session');\nconst bcrypt = require('bcrypt');<\/code><\/pre>\n\n\n\n<p>\u2714\ufe0f \ubbf8\ub4e4\uc6e8\uc5b4 \uc124\uc815<br>Middleware Configuration<\/p>\n\n\n\n<p>&#8212; \uc694\uccad\uacfc \uc751\ub2f5\uc0ac\uc774\uc5d0\uc11c \uc77c\uc5b4\ub098\ub294 \uc791\uc5c5\uc744 \ubbf8\ub4e4\uc6e8\uc5b4\uac00 \uc218\ud589\ud569\ub2c8\ub2e4.<br>Middleware performs the tasks that take place between the request and the response.<\/p>\n\n\n\n<p>&#8211;urlencode,json<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.use(express.urlencoded({ extended: false }));\napp.use(express.json());<\/code><\/pre>\n\n\n\n<p>1) express.urlencode : <br>HTML\uc758 \ud0dc\uadf8\ub97c \ud1b5\ud574 \uc804\uc1a1\ub41c \ub370\uc774\ud130\ub97c \uc11c\ubc84\uac00 \uc77d\uc744 \uc218 \uc788\ub3c4\ub85d \ud30c\uc2f1(\ud574\uc11d)\ud574\uc8fc\ub294 \ubbf8\ub4e4\uc6e8\uc5b4\uc785\ub2c8\ub2e4.<br>It is middleware that parses (interprets) data transmitted via HTML tags so that the server can read it.<\/p>\n\n\n\n<p>2)express.json : <br>Axios\/Fetch\ub97c \uc774\uc6a9\ud574 JSON \ud615\uc2dd\uc73c\ub85c \ubcf4\ub0b8 \ub370\uc774\ud130\ub97c \ucc98\ub9ac.<br>Process data sent in JSON format using Axios\/Fetch.<\/p>\n\n\n\n<p>&#8212; \ub85c\uadf8\uc778 \uc644\ub8cc\ud6c4 \uc138\uc158\uc815\ubcf4\uc5d0 \ub300\ud55c \uc124\uc815\uc785\ub2c8\ub2e4.<br>These are the settings for session information after login is complete.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.use(session({\n  secret: 'your-secret-key',\n  resave: false,\n  saveUninitialized: false,\n  cookie: { secure: false }\n}));<\/code><\/pre>\n\n\n\n<p>1)\ub85c\uadf8\uc778\ub41c \uc815\ubcf4\ub294 \uba54\ubaa8\ub9ac\uc5d0 \uc800\uc7a5\ub429\ub2c8\ub2e4.<br>Logged-in information is stored in memory.<\/p>\n\n\n\n<p>2)secret : &#8216;your-secret-key&#8217; <br>\uc11c\ubc84\uac00 \uc0ac\uc6a9\uc790\uc5d0\uac8c \ubc1c\uae09\ud558\ub294 \uc138\uc158 ID \ucfe0\ud0a4\uac00 \uc870\uc791\ub418\uc9c0 \uc54a\uc558\ub294\uc9c0 \uac80\uc99d\ud560 \ub54c \uc0ac\uc6a9\ud558\ub294 \uc554\ud638\ud654 \ud0a4\uc785\ub2c8\ub2e4. \uc2e4\uc81c \uc0ac\uc6a9\ud560\ub54c\uc5d0\ub294 \ub354 \ubcf5\uc7a1\ud558\uac8c \ub9cc\ub4ed\ub2c8\ub2e4.<br>This is an encryption key used to verify that the session ID cookie issued by the server to the user has not been tampered with. In a real-world scenario, a more complex key would be used.<\/p>\n\n\n\n<p>3)resave : false<br>false\ub294 \ubcc0\uacbd\uc0ac\ud56d\uc774 \uc788\uc744\ub54c\ub9cc \uc138\uc158\uc744 \uc800\uc7a5\ud569\ub2c8\ub2e4. \uae30\ubcf8\uac12\uc744 false\ub85c \uc124\uc815\ud569\ub2c8\ub2e4.<br>Setting this to <code>false<\/code> saves the session only when there are changes. The default value is <code>false<\/code>.<\/p>\n\n\n\n<p>4)saveUninitialized: false<br>\ucd08\uae30\ud654 \ub418\uc9c0 \uc54a\uc740 \uc138\uc158\uc740 \uc800\uc7a5\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. \uae30\ubcf8\uac12\uc744 false\ub85c \uc124\uc815\ud569\ub2c8\ub2e4.<br>Uninitialized sessions are not saved. The default value is set to false.<br><br>5)cookie: { secure: false }<br>secure: true\ub85c \uc124\uc815\ud558\uba74 \uc624\uc9c1 \uc554\ud638\ud654\ub41c https:\/\/ \ud658\uacbd\uc5d0\uc11c\ub9cc \ucfe0\ud0a4\uac00 \uc8fc\uace0\ubc1b\uc544\uc9d1\ub2c8\ub2e4. \uae30\ubcf8\uac12\uc744 false\ub85c \uc124\uc815\ud569\ub2c8\ub2e4.<br>When <code>secure: true<\/code> is set, the cookie is transmitted only over an encrypted HTTPS connection. The default value is <code>false<\/code>.<\/p>\n\n\n\n<p>&#8212; \ub85c\uadf8\uc778 \ub418\uc5b4 \uc788\ub294 \uacbd\uc6b0\uc640 \uc5c6\ub294 \uacbd\uc6b0\ub97c \ud655\uc778\ud574\uc11c res.locals \ubcc0\uc218\ub97c \uc14b\ud305\ud569\ub2c8\ub2e4.<br>The <code>res.locals<\/code> variable is set based on whether the user is logged in or not.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.use((req, res, next) => {\n\n  \/\/ \uae30\ubcf8\uac12 \uc138\ud305 (\ub85c\uadf8\uc778 \uc548 \ub41c \uc0c1\ud0dc\ub97c \uac00\uc815) : Default settings (assuming the user is not logged in)\n  res.locals.isLoggedIn = false;\n  res.locals.userid = \"\";\n  res.locals.name = \"\";\n  res.locals.loggedInUser = null;\n\n  \/\/ \ub85c\uadf8\uc778 \uc138\uc158(member)\uc774 \uc874\uc7ac\ud558\ub294 \uacbd\uc6b0 \ub370\uc774\ud130 \ucc44\uc6cc\ub123\uae30 : If the login session (member) exists, fill in the data\n  if (req.session.member) {\n    res.locals.isLoggedIn = true;\n    res.locals.userid = req.session.member.userid;\n    res.locals.name = req.session.member.name;\n    \n    \/\/ \ub370\uc774\ud130\ubca0\uc774\uc2a4(users \ubc30\uc5f4)\uc5d0\uc11c \ub85c\uadf8\uc778\ud55c \uc720\uc800 \uac1d\uccb4 \ucc3e\uc544\uc11c \ub123\uc5b4\uc8fc\uae30\n    \/\/ Find the logged-in user object in the database (users array) and assign it.\n    res.locals.loggedInUser = users.find(user => user.username === req.session.member.userid) || null;\n  }\n\n  next(); \/\/ \ub2e4\uc74c \ub77c\uc6b0\ud130\ub85c \ud1b5\uacfc! \/ To the next router...\n});<\/code><\/pre>\n\n\n\n<p>1)res.locals\ubcc0\uc218\ub294 login.ejs\uc5d0\uc11c \uc544\ub798\ucc98\ub7fc \uc0ac\uc6a9\ud569\ub2c8\ub2e4.<br>The <code>res.locals<\/code> variable is used in <code>login.ejs<\/code> as shown below.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;%if(locals.userid){ %> ID:&lt;%= locals.userid %> \uc774\ub984:&lt;%= locals.name%> \n&lt;%}else{%>\n&lt;%}%><\/code><\/pre>\n\n\n\n<p>2) res.locals\ubcc0\uc218\uc5d0 \ub123\uc73c\uba74 \ub530\ub85c\ubcc0\uc218\ub97c \ub118\uae30\uc9c0 \uc54a\uc544\ub3c4 ejs\ud30c\uc77c\uc5d0\uc11c \uc0ac\uc6a9\uac00\ub2a5\ud569\ub2c8\ub2e4.<br>If you store data in the <code>res.locals<\/code> variable, you can use it in the EJS file without having to pass a separate variable.<\/p>\n\n\n\n<p>&#8212; \uc544\ub798\uc758 \ud568\uc218\uc5d0\uc5d0 \ud328\uc2a4\uc6cc\ub4dc\uac00 \uc785\ub825\ub418\uba74 \uc554\ud638\ud654\uc2dc\ud0a8 \ube44\ubc00\ubc88\ud638\ub97c \ucd9c\ub825\ud569\ub2c8\ub2e4.<br>When a password is entered into the function below, it outputs the encrypted password.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ bcrypt\ud328\uc2a4\uc6cc\ub4dc \uc0dd\uc131 : generate bcrype password\nasync function generateHash(password) {\n  try {\n      const saltRounds = 10;\n      const hash = await bcrypt.hash(password, saltRounds);\n      console.log(\"\ud574\uc2dc\ub41c \ube44\ubc00\ubc88\ud638\/Hashed password:\", hash);\n      return hash;\n  } catch (err) {\n      console.error(\"\ud574\uc2f1 \uc624\ub958\/Hashing error:\", err);\n  }\n}<\/code><\/pre>\n\n\n\n<p>&#8211;\ub85c\uadf8\uc778 \ucc98\ub9ac \ud558\ub294 \ubd80\ubd84\uc785\ub2c8\ub2e4.<br>This is the section that handles the login process.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.post('\/loginProc', async (req, res) => {\n  const { username, password } = req.body;\n  const user = users.find(u => u.username === username);\n  \n  \/\/ generate bcrypt password\n  const bbcrypt = await generateHash(password);\n  \/\/console.log(bbcrypt);\n  \n  \n  if (!user) {\n    return res.send('\ub85c\uadf8\uc778 \uc2e4\ud328 \/login failed: , \uc0ac\uc6a9\uc790 \uc5c6\uc74c \/ cannot find user');\n  }\n\n  \/\/ search user\n  const isMatch = await bcrypt.compare(password, user.passwordHash);\n\n  if (isMatch) {\n    req.session.member = {\n      userid: req.body.username, \/\/ \n      name: 'anomymous' \/\/ \n  };\n    res.redirect('\/');\n  } else {\n    res.send('\ub85c\uadf8\uc778 \uc2e4\ud328 \/ login failed: ,\ube44\ubc00\ubc88\ud638 \ubd88\uc77c\uce58 \/ password error');\n  }\n});<\/code><\/pre>\n\n\n\n<p>1)\ubc30\uc5f4\uc5d0\uc11c  \uc544\uc774\ub514\uc544 \uac19\uc740 \uc774\ub984\uc744 \ucc3e\uc2b5\ub2c8\ub2e4.<br>Find a name like &#8216;Idea&#8217; in the array.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> const user = users.find(u => u.username === username);<\/code><\/pre>\n\n\n\n<p>1)\ud328\uc2a4\uc6cc\ub4dc\ub97c generateHash\ud568\uc218\ub85c \uc554\ud638\ud654\ud569\ub2c8\ub2e4. \ube44\ub3d9\uae30 \ud568\uc218\uc774\ubbc0\ub85c await\uc774 \ubd99\uc5b4 \uc788\uc2b5\ub2c8\ub2e4.<br>The password is encrypted using the <code>generateHash<\/code> function. Since it is an asynchronous function, <code>await<\/code> is used.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const bbcrypt = await generateHash(password);<\/code><\/pre>\n\n\n\n<p>2)user\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud569\ub2c8\ub2e4.<br>Check if the user exists.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  if (!user) {\n    return res.send('\ub85c\uadf8\uc778 \uc2e4\ud328 \/login failed: , \uc0ac\uc6a9\uc790 \uc5c6\uc74c \/ cannot find user');\n  }<\/code><\/pre>\n\n\n\n<p>3)\ud3fc\uc5d0\uc11c \uc785\ub825\ub41c \ud328\uc2a4\uc6cc\ub4dc\uc640 \uc554\ud638\ud654\ub3c8 \ud328\uc2a4\uc6cc\ub4dc\ub97c bcrypt.compare\ud568\uc218\ub85c \ud655\uc778\ud569\ub2c8\ub2e4.<br>The password entered in the form and the encrypted password are verified using the <code>bcrypt.compare<\/code> function.<\/p>\n\n\n\n<p>4) isMatch\uac00 true\uc774\uba74 req.session.member\uc5d0 \uc544\uc774\ub514\uc640 anonymous\uac00 \uc785\ub825\ub429\ub2c8\ub2e4.<br>If isMatch is true, the ID and anonymous status are stored in req.session.member.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  const isMatch = await bcrypt.compare(password, user.passwordHash);\n\n  if (isMatch) {\n    req.session.member = {\n      userid: req.body.username, \/\/ \n      name: 'anomymous' \/\/ \n  };<\/code><\/pre>\n\n\n\n<p>&#8212; \ub85c\uadf8\uc544\uc6c3\ud560 \uacbd\uc6b0 req.session.destroy\ub85c \uc138\uc158\uac12\uc744 \uc9c0\uc6c1\ub2c8\ub2e4.<br>When logging out, the session value is cleared using <code>req.session.destroy<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.get('\/logout', (req, res) => {\n  req.session.destroy((err) => {\n    if (err) {\n      console.error('\uc138\uc158 \uc0ad\uc81c \uc2e4\ud328 \/ session delete failed:', err);\n      res.send('\ub85c\uadf8\uc544\uc6c3 \uc2e4\ud328 \/ logout failed');\n    } else {\n      res.redirect('\/');\n    }\n  });\n});<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\ud83d\udc49\ud83c\udffb \ub85c\uadf8\uc778 \ub85c\uadf8\uc544\uc6c3 \uad6c\ud604\ud558\uae30 \uc704\ud55c \uae30\ubcf8 \ub85c\uc9c1\uc5d0 \ub300\ud55c \ucf54\ub4dc\uc785\ub2c8\ub2e4.This is the code for the basic logic used to implement login and logout functionality. \ud83d\udc49\ud83c\udffb \ub370\uc774\ud130 \ubca0\uc774\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \uc54a\uace0 \ubc30\uc5f4\uc744 \uc0ac\uc6a9\ud569\ub2c8\ub2e4.It uses an array instead of a database. \ud83d\udc49\ud83c\udffb \uc2e4\uc81c\ub85c \uad6c\ud604\ub418\ub294 \ub85c\uadf8\uc778 \uae30\ub2a5\uc740 \ud6e8\uc52c \ub354 \ubcf5\uc7a1\ud558\uc9c0\ub9cc \uc774 \ucf54\ub4dc\ub97c \ud1b5\ud574\uc11c \uac04\ub2e8\ud55c \uac1c\ub150\uc744 \uc774\ud574\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. While the [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,1],"tags":[],"class_list":["post-1154","post","type-post","status-publish","format-standard","hentry","category-nodejs","category-uncategorized","missing-thumbnail"],"_links":{"self":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/1154","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=1154"}],"version-history":[{"count":98,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/1154\/revisions"}],"predecessor-version":[{"id":6498,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/posts\/1154\/revisions\/6498"}],"wp:attachment":[{"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/media?parent=1154"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/categories?post=1154"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.freelifemakers.org\/wordpress\/index.php\/wp-json\/wp\/v2\/tags?post=1154"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}