코드스테이츠 13주차 / im-sprint-auth-session

support·2022년 2월 19일
2
post-thumbnail

1. 초기세팅

1) 환경 변수 설정
.env.example 파일이름을 .env로 바꾼 다음 비밀번호 작성

2) 데이터베이스 마이그레이션

.env 파일에 적혀있는 데이터 베이스 이름대로 mysql 접속후 생성해주기

mysql > DATABASE CREATE authentication
npx sequelize-cli db:migrate

mkcert로 발급받은 https 인증서 두개를 서버 폴더에 복사

2. 서버 구현

쿠키 설정은 HTTPS를 사용하는 것을 고려하여 설정
index.js에서 CORS 및 세션 옵션을 설정

index.js

app.use(
  session({
    secret: '@codestates',
    resave: false,
    saveUninitialized: true,
    cookie: {
      domain: "localhost",
      path: "/",
      maxAge: 24 * 6 * 60 * 10000,
      sameSite: "none",
      httpOnly: "true",
      secure: "true",
    },
  })
);

3. 서버 > 컨트롤러 구현

req로 오는 데이터가 db에 존재하는지 확인
-> 존재하면 세션에 userid 저장
-> 없으면 로그인요청 거절

userInfo 가 선언이 되어있으니 userInfo와 req를 콘솔에 찍어 어떤 정보가 들어오는지 확인하자
userInfo는 유저정보가 데이터베이스에 존재하고, 완벽히 일치하는 경우에만 데이터가 존재한다고 알려주고 있으니 userInfo가 존재한다면으로 if문을 짜면 되겠다

userInfo가 없을 경우에는 404 요청을 보내주고
userInfo가 있을 때는 힌트에 req.session을 사용하라고 적혀있다 req.session을 사용해서 세션에 userid를 저장해보자

참고자료
req.session

// 해당 모델의 인스턴스를 models/index.js에서 가져옵니다.
const { Users } = require('../../models');

module.exports = {
  post: async (req, res) => {
    // userInfo는 유저정보가 데이터베이스에 존재하고, 완벽히 일치하는 경우에만 데이터가 존재합니다.
    // 만약 userInfo가 NULL 혹은 빈 객체라면 전달받은 유저정보가 데이터베이스에 존재하는지 확인해 보세요
    const userInfo = await Users.findOne({
      where: { userId: req.body.userId, password: req.body.password },
    });

    // TODO: userInfo 결과 존재 여부에 따라 응답을 구현하세요.
    // 결과가 존재하는 경우 세션 객체에 userId가 저장되어야 합니다.
    if (!userInfo) {
      res.status(404).send({ message: "not authorized" });
    } else {
      // your code here
      // HINT: req.session을 사용하세요.
      req.session.save(function () {
        req.session.userId = userInfo.userId;
        res.json({ data: userInfo, message: "ok" });
      });    
    }
  }
}

이렇게 구현해주고 나면 아까는 뜨지 않았던 쿠키옵션 테스트 케이스까지 통과하게 된다

module.exports = {
  post: (req, res) => {
    // TODO: 세션 아이디를 통해 고유한 세션 객체에 접근할 수 있습니다.
    // 앞서 로그인시 세션 객체에 저장했던 값이 존재할 경우, 이미 로그인한 상태로 판단할 수 있습니다.
    // 세션 객체에 담긴 값의 존재 여부에 따라 응답을 구현하세요.

    if (!req.session.userId) {
      res.status(400).send();
    } else {
      // your code here
      // TODO: 로그아웃 요청은 세션을 삭제하는 과정을 포함해야 합니다.
      res.status(200).send();
      req.session.destroy()
    }
  },
};

세션 객체에 저장한 값이 존재하면 사용자 정보를 보내줘야 하는데
이건 login.js에서 미리 구현되어 있던 userinfo를 사용하자

const { Users } = require("../../models");

module.exports = {
  get: async (req, res) => {
    // console.log("req.session", req.session);
    // TODO: 세션 객체에 담긴 값의 존재 여부에 따라 응답을 구현하세요.
    // HINT: 세션 객체에 담긴 정보가 궁금하다면 req.session을 콘솔로 출력해보세요

    if (!req.session.userId) {
      res.status(400).send({ message: "not authorized" });
    } else {
      const result = await Users.findOne({
        where: { userId: req.session.userId },
      });
      res.status(200).json({ data: result, message: "ok" });
      // console.log("result", result);
      // TODO: 데이터베이스에서 로그인한 사용자의 정보를 조회한 후 응답합니다.
    }
  },
};

이렇게 하면 서버는 구현이 끝난다

4. 클라이언트 구현

Mypage.js

handleLogout과 화면에 랜더링 되는 fill_me_in을 채워주면 된다
props 를 콘솔에 찍어보면 사용해야하는 유저 데이터를 넘겨주고 있다
{ userData: { id: 0, userId: 'test', email: 'test@test.com' } }

props.userData 를 사용해서 fill_me_in을 채워주고
handleLogout에서는 fetch나 아니면 axios 를 사용해서 서버와 연결해주면 된다
이번에는 axios를 사용해 봤다

import React from "react";
import axios from "axios";

const FILL_ME_IN = "FILL_ME_IN";

function Mypage(props) {
  const handleLogout = () => {
    // TODO: 서버에 로그아웃 요청을 보낸다음 요청이 성공하면 props.logoutHandler를 호출하여 로그인 상태를 업데이트 해야 합니다.
    axios
      .post("https://localhost:4000/users/logout", {
        withCredentials: true,
      })
      .then(() => {
        props.logoutHandler();
      });
  };

  return props.userData === null ? (
    <div>Loading...</div>
  ) : (
    <div>
      <div className="mypageContainer">
        <div>
          <span className="title">Mypage</span>
          <button className="logoutBtn" onClick={handleLogout}>
            logout
          </button>
        </div>
        <hr />

        <div>
          안녕하세요. <span className="name">{props.userData.userId}</span>! 로그인이 완료되었습니다.
        </div>
        <br />
        <div className="item">나의 유저 네임: {props.userData.userId}</div>
        <div className="item">나의 이메일 주소: {props.userData.email}</div>
      </div>
    </div>
  );
}

export default Mypage;

login.js

loginRequestHandler를 채워주자

import React, { Component } from "react";
import axios from "axios";

class Login extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: "",
      password: "",
    };
    this.inputHandler = this.inputHandler.bind(this);
    this.loginRequestHandler = this.loginRequestHandler.bind(this);
  }

  inputHandler(e) {
    this.setState({ [e.target.name]: e.target.value });
  }

  loginRequestHandler() {
    // TODO: 로그인 요청을 보내세요.

    // 로그인에 성공하면
    // - props로 전달받은 함수를 호출해, 로그인 상태를 변경하세요.
    // - GET /users/userinfo 를 통해 사용자 정보를 요청하세요

    // 사용자 정보를 받아온 후
    // - props로 전달받은 함수를 호출해, 사용자 정보를 변경하세요.

    axios({
      url: "https://localhost:4000/users/login", // 통신할 웹문서
      method: "post", // 통신할 방식
      data: {
        // 인자로 보낼 데이터
        userId: `${this.state.username}`,
        password: `${this.state.password}`,
      },
    }).then((res) => {
      this.props.loginHandler();
      this.props.setUserInfo(res.data.data);
      console.log(res);
    });
  }

  render() {
    return (
      <div className="loginContainer">
        <div className="inputField">
          <div>Username</div>
          <input
            name="username"
            onChange={(e) => this.inputHandler(e)}
            value={this.state.username}
            type="text"
          />
        </div>
        <div className="inputField">
          <div>Password</div>
          <input
            name="password"
            onChange={(e) => this.inputHandler(e)}
            value={this.state.password}
            type="password"
          />
        </div>
        <div className="passwordField">
          <button onClick={this.loginRequestHandler} className="loginBtn">
            Login
          </button>
        </div>
      </div>
    );
  }
}

export default Login;


이렇게 로그인 되는 웹페이지 완성!

0개의 댓글