본문 바로가기

Database(DB)/MySQL(2022 version)

MySQL 기초(3) - cookie, 로그인/로그아웃 구현, AJAX

728x90
반응형

1) MySQL 기초

   1-1) cookie

   1-2) 코드 실습 - 로그인, 로그아웃 구현

   1-3) AJAX

 

 

 

 

 

1) MySQL 기초

1-1) cookie

HTTP의 특징 : "비연결성"을 가진다!

HTTP는 한 번 request와 response를 주고 받으면 연결을 끊기 때문에
다시 request와 response를 주고 받으려면 반드시 다시 연결(3-way Handshaking)을 먼저 한 뒤
request와 response를 주고 받아야 한다!!(HTTP의 "비연결성")

request와 response가 파일 단위로 계속 주고 받는다는 점을 명심할 것!!

이러한 "비연결성"을 보완하기 위해 "cookie"가 등장함!!

브라우저(Client) 안에 작은 저장소에 데이터를 저장하고(마치 이전에 배운 로컬 스토리지와 유사함),
request message에 해당 데이터를 넣어서 보내준다!

(즉, 브라우저가 요청을 보낼때마다 매번 request message에 해당 데이터를 넣어서 보내줌!!

--> 정확히는 request header 영역에 넣어서 보내줌)

 

이것이 바로 "cookie"이다!

--> cookie는 request message의 header 부분에 string 타입으로 cookie가 들어감!

 

로그인 기능에 cookie가 사용됨!

"cookie"는 데이터를 client(브라우저)에 저장시키는 것이고
"session"은 서버에 데이터를 저장시킨다는 차이가 있다!

 

$ npm init -y
$ npm install express nunjucks

 

server.js

const express = require("express");
const nunjucks = require("nunjucks");
const cookieParser = require("./middlewares/cookieParser");
const app = express();

app.set("view engine", "html");
nunjucks.configure("views", {
  express: app,
});

app.use(cookieParser());

app.get("/", (req, res) => {
  res.render("index.html");
});

app.get("/createCookie", (req, res) => {
  // res.setHeader("name", "sangbeom"); // name 속성에 "sangbeom"이라는 값을 넣어서 응답을 보내준다는 의미
  // res.setHeader("Set-Cookie", "express=1;");

  // sever 측에서 브라우저, 즉 client에 응답을 보내기 전에 header에 데이터를 껴서 보내주기 위한 코드
  res.setHeader(
    "Set-Cookie",
    "token=web7722; Domain=localhost; Path=/; Secure; HttpOnly" // 이와 같이 값을 여러 개 넣는 것도 가능함!!
  );
  res.send("응답");
});

app.get("/profile", (req, res) => {
  console.log(req.cookies.token);
  res.send("profile~~");
});

app.listen(3000, () => {
  console.log("server start");
});

 

views/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    쿠키 테스트
  </body>
</html>

 

middlewares/cookieParser.js

module.exports = () => {
  return (req, res, next) => {
    // console.log(req.headers.cookie); // 'token=web7722' --> {token:'web7722'}
    if (req.headers.cookie === undefined) return next();

    const cookies = req.headers.cookie
      .split(";")
      .map((v) => v.split("="))
      .reduce((acc, [k, v]) => {
        acc[k.trim()] = v;
        return acc;
      }, {});

    // req.cookies = { token: "web7722" };
    req.cookies = cookies;
    next();
  };
};

 

 

 

 

1-2) 코드 실습 - 로그인, 로그아웃 구현

사전세팅

$ npm install express nunjucks mysql2 cookie-parser

 

(1) 라우팅을 완벽하게 구현하기
http://localhost:3000

http://localhost:3000/user/login GET
http://localhost:3000/user/login POST

http://localhost:3000/user/logout GET

GET /

GET /user/login

POST /user/login
GET /user/logout
GET /user/join --> 미구현

GET /board/list --> 미구현

 

(2) GET을 완성시키기(즉, 화면을 먼저 완성시킨다는 의미)
router --> controller --> service --> repository --> db

 

(3) DB 확인
SELECT * FROM user WHERE user_id='web7722' and user_pw='1234';

 

(4) repository 구현 및 테스트

findOne();
const obj = {
  where: {
    user_id: "web7722",
    user_pw: "1234",
  },
};

 

(5) service 구현 및 테스트


(6) controller service 장착

 

- controller : 브라우저(client)의 요청과 응답만 담당함!(요청과 응답에 대한 코드만 넣겠다는 의미)
- service : 사용자 측에서 받았던 정보를 가공함!(즉, 로직적인 부분이 들어간다고 보면 됨)
- repository : service의 로직적인 부분을 처리하는 과정에서 데이터를 불러와야 하는데 이를 repository가 담당함!
- db : 실제 데이터를 저장하는 공간으로서 요청이 들어오면 그에 맞는 응답을 보내주는 역할을 수행함!

+) MVC : Model-View-Controller의 약자로 애플리케이션을 세 가지 역할로 구분한 개발 방법론 혹은 소프트웨어 디자인 패턴

  • M(Model) : 데이터를 말함
  • V(View) : views 디렉토리를 말함
  • C(Controller) : controller, service, repository를 말함

 

 

server.js

const express = require("express");
const nunjucks = require("nunjucks");
const cookieParser = require("cookie-parser");
const app = express();
const router = require("./routes");

app.set("view engine", "html");
nunjucks.configure("views", {
  express: app,
});

app.use(express.static("public"));
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(router);

app.use((error, req, res, next) => {
  console.error(error, error.message);
  res.send(`
    <script type='text/javascript'>
      alert('${error.message}');
      history.back();
    </script>
  `);
});
// history.back(); : 이전 페이지로 돌아가게 하는 코드

app.listen(3000, () => {
  console.log("server start");
});

 

 

views

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    {% if token %} {{token}}님 환영합니다~
    <a href="/user/logout">로그아웃</a>
    {% else %}
    <a href="/user/login">로그인</a>
    {% endif %}
    <a href="/user/join">회원가입</a>
    <br />
    <a href="/board/list">게시판 바로가기</a>
  </body>
</html>

 

user/login.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>로그인 페이지</h1>
    <form method="post" action="/user/login">
      <ul>
        <li>아이디 : <input type="text" name="user_id" /></li>
        <li>패스워드 : <input type="password" name="user_pw" /></li>
      </ul>
      <button type="submit">로그인</button>
    </form>
    <script type="text/javascript" src="/js/login.js"></script>
  </body>
</html>

 

 

routes

index.js

const express = require("express");
const router = express.Router();
const user = require("./user.route");

router.get("/", (req, res) => {
  const { token } = req.cookies;
  res.render("index.html", { token });
});

router.use("/user", user);

module.exports = router;

 

user.route.js

const express = require("express");
const router = express.Router();
const userController = require("../controllers/user.controller");

router.get("/login", userController.getLogin);

router.post("/login", userController.postLogin);

router.get("/logout", userController.logout);

// router.get("/join", (req, res) => {
//   res.render("user/join.html");
// });

module.exports = router;

 

 

controllers

user.controller.js

const userService = require("../services/user.service");

exports.getLogin = (req, res, next) => {
  res.render("user/login.html");
};

exports.postLogin = async (req, res, next) => {
  const { user_id, user_pw } = req.body;
  const user = await userService.getUser({ user_id, user_pw });

  if (user === undefined)
    return next(new Error("아이디와 패스워드가 일치하지 않습니다."));
  res.setHeader("Set-Cookie", `token=${user.user_id}; path=/;`);
  res.redirect("/");
};

exports.logout = async (req, res, next) => {
  res.setHeader("Set-Cookie", `token=; path=/;`);
  res.redirect("/");
};

 

 

services

user.service.js

const userRepository = require("../repository/user.repository");

exports.getUser = async ({ user_id, user_pw }) => {
  // console.log(user_id, user_pw);
  const where = { user_id, user_pw };
  const user = await userRepository.findOne({ where });
  return user;
};

// const user_id = req.body.user_id
// const user_pw = req.body.user_pw
// this.getUser(req.body)

// const user_id = "web7722";
// const user_pw = "1234";
// this.getUser({ user_id, user_pw });

 

 

repository

user.repository.js

const db = require("./db");

exports.findOne = async ({ where }) => {
  // {} or undefined
  try {
    // console.log(where);
    const payload = Object.entries(where)
      .map(([key, value]) => `${key}='${value}'`)
      .join(" and ");
    // console.log(payload);

    const sql = `SELECT * FROM user WHERE ${payload}`;
    // console.log(sql);
    const [[result]] = await db.query(sql); // [[], []]
    // console.log(result);
    return result; // {} or undefined
  } catch (e) {
    throw new Error(e);
  }
};

const obj = {
  where: {
    user_id: "web7722",
    user_pw: "1234",
  },
};
this.findOne(obj);

 

db.js

const mysql = require("mysql2");

const pool = mysql
  .createPool({
    host: "127.0.0.1",
    port: "3306",
    user: "root",
    password: "[본인 mysql 비밀번호]",
    database: "sangbeom",
  })
  .promise();

module.exports = pool;

 

 

 

 

1-3) AJAX(Asynchronous JavaScript And XML)

AJAX : 서버와 통신하기 위해 XMLHttpRequest 객체를 사용하는 것을 말함

AJAX의 2가지 특징

  • 페이지 새로고침 없이 서버에 요청
  • 서버로부터 데이터를 받고 작업을 수행

XML : 데이터를 마크업으로 저장하는 형태(즉, 데이터를 정의하는 규칙을 제공하는 마크업 언어)

{user_id: 'web7722', user_pw: '1234'}  -->  이러한 객체 형식이 아래 XML 형식보다 비교적 짧고 간단해서 많이 사용함!!

 

XML 형태

<root>
  <user_id>web7722</user_id>
  <user_pw>1234</user_pw>
</root>

 

AJAX의 요점: Javascript 문법으로 요청을 보낸다!
request message를 만들어 실제 client를 담당하는 부분을 통해 요청을 보낸다는 의미이다!

new Promise --> fetch, axios
"AJAX" 개념 안에 "fetch", "axios"가 들어가 있다고 보면 된다!!

 

 

public

js/login.js

const form = document.querySelector("form");
const user = document.querySelector("input[type=text]");
const pass = document.querySelector("input[type=password]");

const xhr = new XMLHttpRequest();
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

console.log(xhr);
// xhr.open('method', 'url') // 여기서 "open"은 client가 요청할 내용을 정의한다고 보면된다!(초기화 개념)

form.addEventListener("submit", (e) => {
  e.preventDefault();
  xhr.open("post", "http://localhost:3000/user/login"); // 여기서 "open"은 client가 요청할 내용을 정의한다고 보면된다(초기화 개념)!
  xhr.send(`user_id=${user.value}&user_pw=${pass.value}`);

  xhr.addEventListener("readystatechange", (e) => {
    const { target } = e;
    // console.log("hello!", xhr.readyState); // 1)socket을 열었다  2)응답 대기 중  3)완료 --> 이와 같은 3단계가 끝날 때마다 자바스크립트 코드를 실행시키겠다는 의미이다!!
    if (xhr.readyState === 4) {
      console.log(target);
    }
  });
});