혼자 적어보는 노트

[Node.js] 로그인 구현하기 / Express, MySQL 본문

NextJS

[Node.js] 로그인 구현하기 / Express, MySQL

jinist 2022. 3. 19. 03:19

 

로그인을 하게 될 경우 세션에 유저 정보를 추가하고 세션 id를 브라우저 쿠키에 저장하고
서버에 요청을 할 때마다 쿠키정보를 함께 전송함으로써 서버에서 어떤 유저가 요청을 보내는지
파악할 수 있다고 이론 적인 부분만 알고있었다.

하지만 직접 구현을 하려고 하니 생각보다 복잡한 인증 절차가 있다고 한다. (social 로그인시)

 

passport를 사용하여 간단하게 로그인을 구현해보고

머릿속에 개념에 대해 간단하게나마 저장을 해보려 한다.

 


📄 준비된 것

- 회원가입 기능 구현 완료. (DB에 User의 데이터가 있는 상태)

- 프론트에서 로그인 버튼 클릭 시 {email, password} 데이터 전달하는 코드 작성

- Node Express를 이용한 서버 세팅

- Sequalize를 사용하여 모델 세팅

- MySQL으로 연결하여 데이터를 넣을 테이블 세팅

 

이전에 회원가입을 진행했던 포스팅에 이어서

진행을 해보겠다.

 


📂 관련 라이브러리 설치

passport, passport-local

npm i passport passport-local

Passport란 Node.js용 Express 호환 인증 미들웨어이다.

요청을 인증하기위해 Strategy(전략)의 개념을 사용한다.

 

express-session, cookie-parser

npm i express-session cookie-parser

 

express-session: express에서 session을 사용할 수 있도록 해주는 패키지

cookie-paser: cookei헤더를 파싱하고 쿠키 이름들로 된 object key로 req.cookies를 채운다

 


passport 설정

 

passport/index.js

module.exports = () => {
  // passport 설정
  passport.serializeUser((user, done) => {
    done(null, user.id); // 서버에 user.id 저장
  });

  passport.deserializeUser(async (id, done) => {
    //여기서 id는 serializeUser의 done에서 보낸 user.id
    try {
      const user = await User.findOn({
        where: { id },
      });
      done(null, user);
    } catch (error) {
      console.log(error);
      done(error);
    }
  });

  local();
};

index.js에서 passport 설정을 해주고

module.export로 해당 함수를 내보낸다.

 

serializeUser : 사용자의 정보 객체를 세션에 저장한다.

deserializeUser : 서버에 저장된 아이디(객체)를 통해서 DB에서 사용자 정보를 불러온다.

 

local.js파일을 생성하여 연결을 해준다.

 

🔗 config 파일 연결

 

app.js에 아래의 코드를 추가한다.


const passport = require("passport");
const session = require("express-session");
const cookieParser = require("cookie-parser");
const passportConfig = require("./passport");

passportConfig(); // app.js에 연결


app.use(
  session({
    saveUninitialized: false,
    resave: false,
    secret: process.env.COOKIE_SECRET,
  })
);
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(passport.initialize());
app.use(passport.session());

 

passportConfig() : passport/index.js를 연결

passport.initialize(), passport.session() : express에서 passport를 사용할 때 config를 위해 가장 먼저 수행될 미들웨어

 

session()

- resave : request마다 기존에 있던 session을 계속 다시 저장

- saveUninitialized : uninitialized상태의 session을 강제로 저장

- secret: session id를 암호화 할 수 있는 key이다. * 외부에 노출되어서는 안됨!

 

 

passport/local.js

const passport = require("passport");
const { Strategy: LocalStragy } = require("passport-local");
const { User } = require("../models");
const bcrypt = require("bcrypt");

module.exports = () => {
  passport.use(
    new LocalStragy(
      {
        // req.body에 대한 설정
        usernameField: "email", // req.body.email
        passwordField: "password", // req.body.password
      },
      async (email, password, done) => {
        try {
          const user = await User.findOne({
            where: { email },
          });
          if (!user) {
            return done(null, false, { reason: "존재하지 않는 이메일입니다." });
            // 클라이언트 에러
            // 이 세가지의 매개변수들은 err, user, info로 전달된다.
          }
          const result = await bcrypt.compare(password, user.password);
          if (result) {
            return done(null, user); // 로그인 성공
          }
          return done(null, false, { reason: "비밀번호가 일치하지 않습니다." });
          // 클라이언트 에러
          
        } catch (error) {
          // 서버 에러 시
          console.error(error);
          return done(error);
        }
      }
    )
  );
};

local.js에서 본격적인 Strategy(전략)을 생성한다.

일단 passport-local에는 Stategy가 있으며 이 후 소셜 로그인과 구분하기 위해 이름을 LocalStategy로 변경해준다.

 

LocalStategy

- 첫 번째 인자 : req.body에 대한 설정

  usernameField : "email"에서의 email은 req.body.email와 같다.

- 두 번째 인자: 첫 번째 인자에 적은 email과 password, 그리고 done을 매개변수로 받는다.

 

done

- 첫 번째 인자 : DB조회 시 발생하는 서버 에러

- 두 번째 인자 : 요청 성공여부, 성공 시 return 할 값

- 세 번째 인자 : 클라이언트로 보낼 에러

 

passport에서는 직접적인 응답을 보내지 않고 done을 보낸다.

 

routes/user.js

const express = require("express");
const bcrypt = require("bcrypt");
const router = express.Router();
const { User } = require("../models"); // User model
const passport = require("passport");

router.post(
  // POST /user/login
  "/login",
  (req, res, next) => {
    passport.authenticate("local", (err, user, info) => {
      // err, user, info ==> done으로 보낸 것들
      if (err) {
        console.log(error);
        return next(error);
      }
      if (info) {
        // client 에러
        return res.status(401).send(info.reason);
      }
      return req.login(user, async (loginErr) => {
        if (loginErr) {
          console.log(loginErr);
          return next(loginErr);
        }
        return res.status(200).json(user);
      });
    })(req, res, next);
  }
);

 

전달 받은 데이터에 맞게

응답처리를 해준다.

 


Logout

router.post("/logout", (req, res) => {
  req.logout();
  req.session.destroy(); // 세션에 저장된 쿠키와 아이디 삭제
  res.status(201).send("ok"); 
});

간단하게 logout()으로 구현하고 session에 저장된 쿠키와 아이디를 삭제하면 로그아웃이 가능하다.

 

 

따라하는 작업을 하면서도 종종 에러가 발생했는데

네트워크 탭이나 에러 메세지를 보고 해결을 할 수 있었다.  ❗❗ 오타조심

 

 

참고

https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC#

https://fierycoding.tistory.com/36

https://blog.naver.com/bark6462/222663171585

'NextJS' 카테고리의 다른 글

[Next.js] Image height auto 사용  (0) 2022.07.30
[Next.js] Link와 href  (0) 2022.07.29
[Node.js] nodemon 사용 해보기 / nodemon 안될 때  (0) 2022.03.17
[Node.js] Route 분리하기  (0) 2022.03.15
[Node.js] express 사용하기  (0) 2022.03.15
Comments