Home 로그인 페이지 만들기 - 05 (cookie & session)
Post
X

로그인 페이지 만들기 - 05 (cookie & session)

쿠키와 세션을 활용해 자동 로그인 기능을 구현해보자.


유저 정보 페이지 만들기

간단하게 사용자의 이름을 전달받으면 줄력해주는 페이지를 만들고 Router에 추가해줍니다.

로그아웃 기능도 같이 추가해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!-- info.ejs -->
<!DOCTYPE html>
<html lang="ko">
  <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" />
    <link rel="shortcut icon" href="#" />

    <!-- CSS link -->
    <link rel="stylesheet" href="/css/user/login.css" />

    <title>메인 페이지</title>
  </head>
  <body>
    <div class="login-page">
      <div>
        <% if (name) { %>
        <ul>
          <li><p>유저 정보</p></li>
          <li><%= name %></li>
          <li><a href="/user/logout">Logout</a></li>
        </ul>
        <% } else { %>
        <a href="/user/login">Login</a>
        <% } %>
      </div>
    </div>

    <!-- script 삽입 -->
  </body>
</html>

<!-- Copyright (c) 2022 by Aigars Silkalns (https://codepen.io/colorlib/pen/rxddKy) -->
1
2
3
4
5
6
7
8
9
10
11
12
13
// routes/route_user.js

...

Router.get("/info", ctrl.output.info);
Router.get("/login", ctrl.output.login);
Router.get("/register", ctrl.output.register);
Router.get("/logout", ctrl.process.logout);

Router.post("/login", validator_user.login, ctrl.process.login);
Router.post("/register", validator_user.register, ctrl.process.register);

export default Router;

쿠키와 세션으로 기능 구현

cookie-parser 모듈과 express-session 모듈을 활용합니다.

  1. 먼저 app.js 파일에 cookie-parser 모듈을 import해 미들웨어로 등록합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    import express from "express";
    import path from "path";
    import "dotenv/config";
    import bodyParser from "body-parser";
    import cookieParser from "cookie-parser";
    
    import userRouter from "./src/routes/route_user.js";
    
    const app = express();
    const __dirname = path.resolve();
    
    // 앱 세팅
    app.set("views", "./src/views");
    app.set("view engine", "ejs");
    
    // 미들웨어
    app.use("/", express.static(`${__dirname}/src/public`));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(cookieParser(process.env.COOKIE_SECRET));
    // cookieParser 메서드에 인자로 주어지는 secret은 환경변수로 따로 관리합니다.
    
    // router
    app.use("/user", userRouter);
    
    // 3000 포트로 서버 오픈
    app.listen(3000, () => {
      console.log("express server on port 3000");
    });
    
  2. session은 config 폴더session.js 파일을 만들어 모듈화해서 사용하겠습니다.

    세션 스토어로 Mysql을 사용하기 위해 express-mysql-session 모듈을 설치해 사용합니다.

    참고 글 : express-session 모듈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    
    import session from "express-session";
    import mysqlStore from "express-mysql-session";
    
    const mysqlStoreSession = mysqlStore(session);
    
    const options = {
      host: process.env.DB_HOST, //실제로 연결할 데이터베이스의 위치
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      port: process.env.DB_PORT,
      database: process.env.DB_DATABASE,
      clearExpired: true, // 만료된 세션 자동 삭제 기능
      checkExpirationInterval: 30 * 60 * 1000, // 30분 ,만료된 세션이 지워지는 빈도 (밀리초)
      createDatabaseTable: true,
      expiration: 1 * 60 * 60 * 1000, // 1시간, 유효한 세션의 최대 시간 (밀리초)
      schema: {
        tableName: "sessions",
        columnNames: {
          session_id: "session_id",
          expires: "expires",
          data: "data",
        },
      },
    };
    
    const sessionStore = new mysqlStoreSession(options);
    
    sessionStore
      .onReady()
      .then(() => {
        // 세션 스토어 준비 완료
        console.log("MySQLStore ready");
      })
      .catch((err) => {
        // 에러 발생
        console.error(err);
      });
    
    export default session({
      secure: true, // https 환경에서만 session 정보 처리
      secret: process.env.COOKIE_SECRET, // 암호화하는 데 쓰일 키
      resave: false, // 세션을 언제나 저장할지
      saveUninitialized: false,
      name: "sessionId",
      store: sessionStore,
      cookie: {
        maxAge: 24 * 60 * 60 * 1000, // 쿠키 유효기간 24시간
      },
    });
    
  3. app.js 파일에 정의한 session을 import해 미들웨어로 등록합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    import express from "express";
    import path from "path";
    import "dotenv/config";
    import bodyParser from "body-parser";
    import cookieParser from "cookie-parser";
    import sessionMiddleware from "./src/config/session.js";
    
    import userRouter from "./src/routes/route_user.js";
    
    const app = express();
    const __dirname = path.resolve();
    
    // 앱 세팅
    app.set("views", "./src/views");
    app.set("view engine", "ejs");
    
    // 미들웨어
    app.use("/", express.static(`${__dirname}/src/public`));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(cookieParser(process.env.COOKIE_SECRET));
    app.use(sessionMiddleware);
    
    // router
    app.use("/user", userRouter);
    
    // 3000 포트로 서버 오픈
    app.listen(3000, () => {
      console.log("express server on port 3000");
    });
    

세션과 쿠키를 미들웨어로 등록했으니 자동 로그인 기능과 사용자의 id를 출력하는 페이지를 구현해보겠습니다.


Controller에 로직 추가

  • POST 방식으로 login을 요청할 때 로그인 성공하면 세션에 isLogined 값을 true로 name 값과 함께 저장합니다.

  • GET 방식으로 로그인 페이지를 요청하면 세션의 isLogined 값을 확인하고 true라면 name 값을 넘겨줍니다.

  • session.destroy 메서드를 이용해 session을 삭제하는 logout 기능을 구현합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import UserStorage from "../models/model_user.js";

const output = {
  info: (req, res) => {
    res.render("user/info", {
      // 저장된 세션으로 로그인 된 상태인지 확인
      name: req.session.isLogined ? req.session.name : null,
    });
  },

  login: (req, res) => {
    res.render("user/login");
  },

  register: async (req, res) => {
    res.render("user/register");
  },
};

const process = {
  login: async (req, res) => {
    var response = {};

    try {
      const user = await UserStorage.getUserInfo(req.body.id);

      if (user) {
        if (password === req.body.password) {
          // 로그인 성공 시 세션에 기록
          req.session.isLogined = true;
          req.session.name = user.name;

          return (response = { success: true });
        }

        return (response = { success: false, msg: "비밀번호가 틀렸습니다." });
      }

      response = { success: false, msg: "존재하지 않는 아이디입니다." };
    } catch (err) {
      response = {
        success: false,
        msg: "로그인을 할 수 없습니다.\n잠시 후 다시 시도해주세요.",
      };
    } finally {
      res.json(response);
    }
  },

  register: async (req, res) => {
    let response = {};

    try {
      const user = await UserStorage.getUserInfo(req.body.id);

      if (user) {
        return (response = { success: false, msg: "아이디가 존재합니다." });
      }

      response = await UserStorage.createUser(req.body);
    } catch (err) {
      // 간단한 에러 처리문 작성
      response = {
        success: false,
        msg: "회원가입을 할 수 없습니다.\n 잠시 후 다시 시도해주세요.",
      };
    } finally {
      res.json(response);
    }
  },

  logout: (req, res) => {
    // 로그아웃 시 세션 삭제
    req.session.destroy((err) => {
      if (err) throw err;
      // 세션 파괴후 로그인 페이지로 이동
      res.redirect("/user/login");
    });
  },
};

export default { output, process };
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.