연락처 삭제하기
- 메인 js
// 관련파일 // ./routes/contactRoutes-5.js // ./controllers/contactController-15.js // ./views/index-4.ejs // ./views/add-2.ejs const express = require("express"); const dbConnect = require("./config/dbConnect"); const methodOverride = require("method-override"); const app = express(); // 뷰 엔진 설정하기 app.set("view engine", "ejs"); app.set("views", "./views"); const port = 3000; // public파일 app.use(express.static("./public")) // method-override 미들웨어 등록 app.use(methodOverride("_method")) dbConnect(); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use("/contacts", require("./routes/contactRoutes")); // contactRoutes-3.js app.listen(port,()=>{ console.log("3000번 포트에서 서버 실행중..."); });
- index-4.js 파일
<%- include('./include/_header-1') %> <!-- Main --> <main id="site-main"> <div class="button-box"> <a href="#" class="btn btn-light"><i class="fa-solid fa-user-plus"></i>연락처 추가</a> </div> <table class="table"> <head> <tr> <th>이름</th> <th>메일주소</th> <th>전화번호</th> <th> </th> </tr> </head> <tbody> <% contacts.forEach(contact => { %> <tr> <td><%= contact.name %></td> <td><%= contact.email %></td> <td><%= contact.phone %></td> <td> <!-- a 태그는 GET 방식의 호출 --> <a href="/contacts/<%= contact._id %>" class="btn update" title="수정"> <i class="fas fa-pencil-alt"></i> </a> <form action="/contacts/<%= contact._id %>?_method=DELETE"method="POST" style="display:inline;"> <input type="submit" class="btn delete" title="삭제" value="X"> </form> </td> </tr> <% }); %> </tbody> </table> </main> <!-- /Main --> <%- include('./include/_footer') %>
- add-2.js 파일
<!-- include header --> <%- include('./include/_header-1') %> <!-- /include header --> <!-- Main --> <main id="site-main"> <div class="button-box"> <a href="/contacts" class="btn btn-light"><i class="fa-solid fa-list"></i>연락처 목록</a> </div> </div> <h2>연락처 추가</h2> <p>연락처 정보를 추가합니다.</p> <!-- POST localhost:3000/contacts/add --> <form action="/contacts/add" method="POST" id="add-user"> <form method="POST" id="add-user"> <div class="user-info"> <div class="col-12"> <label for="name" class="col-form-label">이름(Full Name)</label> <div> <input type="text" class="form-control" name="name" id="name" placeholder="홍길동"> </div> </div> <div class="col-12"> <label for="email" class="col-form-label">메일 주소(E-mail)</label> <div> <!-- name 속성의 키값에 따라서 서버의 파라미터 값을 전달한다! 중요 --> <input type="text" class="form-control" name="email" id="email" placeholder="hong@abc.def"> </div> </div> <div class="col-12"> <label for="phone" class="col-form-label">전화번호(Mobile)</label> <div> <input type="text" class="form-control" name="phone" id="phone" placeholder="123-4567-8901"> </div> </div> <button type="submit">저장하기</button> </div> </form> </form> </main> <!-- /Main --> <%- include('./include/_footer') %>
- contactController-15.js
const asyncHandler = require("express-async-handler"); const Contact = require("../models/contactModel"); // @desc 전체 연락처 가져오기 // @route GET /contacts const getAllContacts = asyncHandler(async (req,res)=>{ const contacts = await Contact.find(); // 헤더와 푸터를 나눈 index-3.ejs res.render("index-4",{contacts : contacts}); }); // @desc 연락처 추가 폼 // @route GET /add const addContactForm = (req,res) => { res.render("add-2"); }; // @desc 새 연락처 추가하기 // @route POST /contacts const createContact = asyncHandler(async (req,res)=>{ // 객체 구조분해할당, key와 value가 동일함 const {name, email, phone} = req.body; if( !name || !email || !phone ){ return res.status(400).send("필수값이 입력되지 않음"); } const contact = await Contact.create({ name, email, phone }); // 새 연락처 추가 후에 리스트 화면으로 이동 res.redirect("/contacts"); }); // @desc 연락처 상세보기 => 업데이트 화면으로 상세보기 // @route GET /contacts/:id const getContact = asyncHandler(async(req,res)=>{ const contact = await Contact.findById(req.params.id); res.render("update-3",{contact:contact}); // PUT방식 요청하기 }); // @desc 연락처 수정하기 // @route PUT /contacts/:id const updateContact = asyncHandler(async(req,res)=>{ const id = req.params.id; const { name, email, phone } = req.body; const updateContact = await Contact.findByIdAndUpdate( id, {name, email, phone}, {new : true} // 수정한 후의 도큐먼트로 반환해 주는 옵션 ); res.redirect("/contacts"); }); // @desc 연락처 삭제하기 // @route DELETE /contacts/:id const deleteContact = asyncHandler(async(req,res)=>{ const id = req.params.id; await Contact.findByIdAndDelete(id); res.redirect("/contacts"); }); module.exports = { getAllContacts, createContact, getContact, updateContact, deleteContact, addContactForm };삭제 전 연락처 목록
삭제 후 결과
로그인 테스트하기
- 메인 js
// 관리자 등록하기 // 로그인 화면구성 // 로그인 관련 파일 // ./routes/loginRoutes-2.js // ./controllers/loginController-2.js // ./views/home-2.ejs // 관련 파일(그대로) // ./routes/contactRoutes-5.js // ./controllers/contactController-15.js // ./views/index-4.ejs // ./views/add-2.ejs // ./views/update-3.ejs const express = require("express"); const dbConnect = require("./config/dbConnect"); const methodOverride = require("method-override"); const app = express(); // 뷰 엔진 설정하기 app.set("view engine", "ejs"); app.set("views", "./views"); const port = 3000; // public 폴더 app.use(express.static("./public")); // method-override 미들웨어 등록 app.use(methodOverride("_method")); dbConnect(); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use("/", require("./routes/loginRoutes")); app.use("/contacts", require("./routes/contactRoutes")); app.listen(port,()=>{ console.log("3000번 포트에서 서버 실행중..."); });
- loginRoutes-2.js
const express = require("express"); const router = express.Router(); const { getLogin,loginUser } = require("../controllers/loginController-2"); // getLogin : 로그인폼 렌저 // loginUser : 로그인 처리(액션) router.route("/").get(getLogin).post(loginUser); module.exports = router;
- home-2.ejs
<!-- include header --> <%- include('./include/_home_header') %> <!-- /include header --> <!-- Main --> <main id="site-main"> <div class="home-container"> <h3 style="margin-left: 80px;">로그인</h3> <p>로그인이 필요한 서비스입니다.</p> <form action="/" method="POST" class="login"> <label for="username"><b>Username</b></label> <input type="text" placeholder="사용자 아이디" name="username" id="username"> <label for="password"><b>Password</b></label> <input type="password" placeholder="비밀번호" name="password" id="password"> <button type="submit">로그인</button> </form> </div> </main> <!-- /Main --> <!-- include footer--> <%- include('./include/_footer') %> <!-- /include footer -->
- loginController-2.js
로그인 데이터 입력const asyncHandler = require("express-async-handler"); // @desc 로그인폼(화면) // @route GET / const getLogin = (req,res) => { res.render("home-2"); }; // @desc 로그인 처리(액션) // @route POST/ const loginUser = asyncHandler(async(req,res) => { // 로그인 처리를 위해서는 아이디/암호를 로그인 폼으로부터 받아온다. const{ username, password } = req.body; if (username === "admin" && password === "1234"){ res.send("로그인 성공!"); }else{ res.send("로그인 실패!"); } }); module.exports = { getLogin, loginUser };로그인 시도
로그인 성공
없는 정보로 로그인하여 로그인 실패
사용자 정보 DB 등록
- 암호(비밀번호)를 암호화하여 DB에 저장
- BCrypt 모듈을 사용하여 사용자 정보를 암호화(해시함수)하여 DB에 저장
- 해시 : 특정 키워드(문자열)을 통해 암호화하는 방법, 단방향이다.(원문을 알 수 없다.)
npm i bcrypt설치
- 메인 js
/* 로그인 관련 파일 ./routes/loginRoutes-4.js ./controllers/loginController-4.js ./views/home-2.ejs ./views/register-1.ejs 관련 파일(그대로) ./routes/contactRoutes-5.js ./controllers/contactController-15.js ./views/index-4.ejs ./views/add-2.ejs ./views/update-3.ejs */ const express = require("express"); const dbConnect = require("./config/dbConnect"); const methodOverride = require("method-override"); const app = express(); // 뷰 엔진 설정하기 app.set("view engine", "ejs"); app.set("views", "./views"); const port = 3000; // public 폴더 app.use(express.static("./public")); // method-override 미들웨어 등록 app.use(methodOverride("_method")); dbConnect(); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use("/", require("./routes/loginRoutes")); app.use("/contacts", require("./routes/contactRoutes")); app.listen(port,()=>{ console.log("3000번 포트에서 서버 실행중..."); });
- loginRoutes-4.js
const express = require("express"); const router = express.Router(); const { getLogin, loginUser, getRegister, registerUser } = require("../controllers/loginController-4"); // getLogin : 로그인폼 렌저 // loginUser : 로그인 처리(액션) router.route("/").get(getLogin).post(loginUser); // getRegister : 사용자 등록폼 // registerUser : 사용자등록 처리(액션) router.route("/register").get(getRegister).post(registerUser); module.exports = router;
- loginController-4.js
const asyncHandler = require("express-async-handler"); const bcrypt = require("bcrypt"); // @desc 로그인폼(화면) // @route GET / const getLogin = (req,res) => { res.render("home-2"); }; // @desc 로그인 처리(액션) // @route POST/ const loginUser = asyncHandler(async(req,res) => { // 로그인 처리를 위해서는 아이디/암호를 로그인 폼으로부터 받아온다. const{ username, password } = req.body; if (username === "admin" && password === "1234"){ res.send("로그인 성공!"); }else{ res.send("로그인 실패!"); } }); // @desc 사용자 등록폼 // @route GET /register const getRegister = (req,res) => { res.render("register-1"); }; // @desc 사용자등록 처리(액션) // @route POST /register const registerUser = asyncHandler(async(req,res) => { const{ username, password, password2 } = req.body; if(!username || !password){ res.send("필수값이 없습니다."); return; } if(password===password2){ //사용자가 입력한 암호("1234")를 해시암호화 한다. // 10 : 해시함수를 10번 수행 const hashedPassword = await bcrypt.hash(password, 10); console.log(password); console.log(hashedPassword); res.send("사용자 등록되었습니다.") }else{ res.send("비밀번호와 비밀번호2가 다릅니다."); } }); module.exports = { getLogin, loginUser, getRegister, registerUser };
- register-1.ejs 파일
<!-- include header --> <%- include('./include/_home_header') %> <!-- /include header --> <!-- Main --> <main id="site-main"> <form action="/register" method="POST" class="register"> <h3>사용자 등록</h3> <label for="username"><b>아이디</b></label> <input type="text" placeholder="아이디" name="username" id="username"> <label for="password"><b>비밀번호</b></label> <input type="password" placeholder="비밀번호" name="password" id="password"> <label for="password2"><b>비밀번호 확인</b></label> <input type="password" placeholder="비밀번호 확인" name="password2" id="password2"> <input type="submit" value="등록" class="register-btn"> </form> </main> <!-- /Main --> <!-- include footer --> <%- include('./include/_footer') %> <!-- /include footer -->데이터 입력
데이터 입력 후 결과
웹 애플리케이션 사용자 인증
- 쿠키 : 클라이언트(PC,Mobile)에 저장되는 임시데이터
- 세션 : 서버에 저장되는 데이터(DB, 메모리)
- 토큰 : 클라이언트와 서버에 저장되는 암호된 데이터
- 쿠키파서, JWT 미들웨어 설치 :
npm i cookie-parser jsonwebtoken
쿠키 생성/삭제
const express = require("express"); const cookieParser = require("cookie-parser"); const app = express(); app.use(cookieParser()); app.get("/",(req,res)=> { // HTTP 응답 헤더 : setCookie : 값을 넣어서 응답하면, 클라가 쿠키 저장 // httpOnly : HTTP통신을 통해서만 쿠키를 생성하도록 설정. JS 통해서 쿠키 생성을 막음. res.cookie("Kim","1234",{httpOnly:true}); res.send("쿠키 생성"); }); app.listen(5000, ()=>{ console.log("5000번 포트에서 서버 실행 중..."); });
사용자 인증 - 세션처리
- 세션관리 : 미들웨어
npm i express-session connect-mongo설치const express = require("express"); const cookieParser = require("cookie-parser"); const session = require("express-session"); const MongoStore = require("connect-mongo"); require("dotenv").config(); const app = express(); app.use(cookieParser()); // 미들웨어 express-session 등록 app.use( session({ name : "connect.sid", secret : "secret code", resave : false, saveUninitialized : true, store : MongoStore.create({mongoUrl:process.env.DB_CONNECT}), cookie : { maxAge:60 * 60 * 24 * 1000 } }) ); app.get("/",(req,res)=> { if(req.session.count){ // 클라에 세션정보가 있으면(재방문 시) req.session.count++; res.write("<p>count:"+req.session.count+"</p>"); res.end(); }else{ // 첫 방문 시 req.session.count = 1; res.send("첫 번째 방문입니다."); } }); app.get("/session",(req,res) => { console.log(req.session); res.send("세션 정보 조회"); }); app.get("/delete-session",(req,res)=> { req.session.destroy((err)=>{ if(err){ console.log(err); }else{ res.clearCookie("connect.sid"); res.send("세션 삭제"); } }) }); app.listen(5000, ()=> { console.log("5000번 포트에서 서버 실행 중..."); });세번째 방문했을 경우 - 방문의 연속성 확보
사용자 인증 - JWT 토근 생성
- 메인 js
/* 로그인 관련 파일 ./routes/loginRoutes-5.js ./controllers/loginController-5.js ./views/home-2.ejs ./views/register-1.ejs 관련 파일(그대로) ./routes/contactRoutes-5.js ./controllers/contactController-15.js ./views/index-4.ejs ./views/add-2.ejs ./views/update-3.ejs ./.env JWT_SECRET = 12345 new */ const express = require("express"); const dbConnect = require("./config/dbConnect"); const methodOverride = require("method-override"); const app = express(); // 뷰 엔진 설정하기 app.set("view engine", "ejs"); app.set("views", "./views"); const port = 3000; // public 폴더 app.use(express.static("./public")); // method-override 미들웨어 등록 app.use(methodOverride("_method")); dbConnect(); app.use(express.json()); app.use(express.urlencoded({extended:true})); app.use("/", require("./routes/loginRoutes")); app.use("/contacts", require("./routes/contactRoutes")); app.listen(port,()=>{ console.log("3000번 포트에서 서버 실행중..."); });
- loginRoutes-5.js
const express = require("express"); const router = express.Router(); const { getLogin, loginUser, getRegister, registerUser } = require("../controllers/loginController-5"); //getLogin : 로그인폼 렌더 //loginUser : 로그인 처리(액션) router.route("/").get(getLogin).post(loginUser); //getRegister : 사용자등록폼 렌더 //registerUser : 사용자등록 처리(액션) router.route("/register").get(getRegister).post(registerUser); module.exports = router;
- loginController-5.js
const asyncHandler = require("express-async-handler"); const bcrypt = require("bcrypt"); const User = require("../models/userModel"); require("dotenv").config(); const jwt = require("jsonwebtoken"); const jwtSecret = process.env.JWT_SECRET; // @desc 로그인폼(화면) // @route GET / const getLogin = (req, res) => { res.render("home-2"); }; // @desc 로그인 처리(액션) // @route POST / const loginUser = asyncHandler(async (req,res) =>{ //로그인 처리를 위해서는 아이디/암호를 로그인폼으로부터 받아온다. const { username, password } = req.body; // JWT토큰기반의 로그인 인증처리 const user = await User.findOne({username}); if(!user){ return res.status(401).json({message:"일치하는 사용자가 없습니다."}); } // DB의 사용자정보와 입력된 사용자정보가 일치하는지 확인 const isMatch = await bcrypt.compare(password, user.password); if(!isMatch){ return res.status(401).json({message:"비밀번호가 일치하지 않습니다."}); } // 사용자 토큐먼트 id(랜덤생성)를 기반으로 토큰데이터 생성(발행)해줌. const token = jwt.sign({id : user._id}, jwtSecret); res.cookie("tooken",token,{httpOnly:true}); res.redirect("/contacts"); //로그인된 사용자만 연락처 리스트화면으로 이동하도록 한다. }); // @desc 사용자등록폼 // @route GET /register const getRegister = (req, res) => { res.render("register-1"); }; // @desc 사용자등록 처리(액션) // @route POST /register const registerUser = asyncHandler(async (req, res) => { const { username, password, password2 } = req.body; if( !username || !password ){ res.send("필수값이 없습니다."); return; } if( password === password2 ) { //사용자가 입력한 암호("1234")를 해시암호화 한다. //10 : 해시함수를 10번 수행한다. const hashedPassword = await bcrypt.hash(password, 10); console.log( password); console.log( hashedPassword ); const user = await User.create({ username, password: hashedPassword }); res.status(201).json({message: "사용자 등록되었습니다.", user }) }else{ res.send("비번과 비번2가 틀립니다."); } }); module.exports = { getLogin, loginUser, getRegister, registerUser };
- .env파일
JWT_SECRET = 12345저장 후npm start다시 시작
기존에 저장된 아이디로 로그인
쿠키에 정보 저장 확인 가능