Inflearn brand logo image
Inflearn brand logo image
채널톡 아이콘

인프런 커뮤니티 질문&답변

Schwartz Melvin님의 프로필 이미지

작성한 질문수 1

React, Node.js, MongoDB로 만드는 나만의 회사 웹사이트: 완벽 가이드

Ch4-6. 관리자 계정 로그아웃, 삭제

req.cookies.token == undefined 현상

해결된 질문

작성

·

92

0

logout을 해보니 400BadRequest:이미 로그아웃 됨 상태가 지속됩니다. console.log(req.cookies.token); 출력해보니 undefined 라고 출력됩니다. 쿠키를 제대로 읽지 못하는것 같은데 왜 이런 현상이 뜨는지 궁금합니다..

 

 


 

const express = require("express");
const router = express.Router();
const bcrypt = require("bcrypt");
const User = require("../models/User");
const axios = require("axios");
const jwt = require("jsonwebtoken");

router.post("/signup", async (req, res) => {
  try {
    const { username, password } = req.body;

    const existingUser = await User.findOne({ username });
    if (existingUser) {
      return res.status(400).json({ message: "이미 존재하는 사용자입니다." });
    }

    const hashedPassword = await bcrypt.hash(password, 10);

    const user = new User({
      username,
      password: hashedPassword,
    });

    await user.save();
    res.status(201).json({ message: "회원가입이 완료되었습니다." });
  } catch (error) {
    res.status(500).json({ message: "서버 오류가 발생했습니다." });
    console.log(error);
  }
});

router.post("/login", async (req, res) => {
  try {
    const { username, password } = req.body;
    const user = await User.findOne({ username }).select("+password"); //왜인지 password가 select되지 않아서 추가함
    if (!user) {
      return res.status(401).json({ message: "존재하지 않는 사용자입니다." });
    }

    if (!user.isActive) {
      return res.status(401).json({ message: "비활성화된 사용자입니다." });
    }

    if (user.isLoggedIn == true) {
      return res.status(401).json({ message: "이미 접속 중인 사용자입니다." });
    }

    const isValidPassword = await bcrypt.compare(password, user.password); //비밀번호 비교
    if (!isValidPassword) {
      user.failedLoginAttempts += 1;
      user.lastLoginAttempt = new Date();

      if (user.failedLoginAttempts >= 5) {
        user.isActive = false; //다섯번 틀리면 계정 비활성화 ㅋㅋ
        await user.save();
        return res.status(401).json({
          message: "비밀번호를 5회 이상 틀려 계정이 비활성화되었습니다.",
        });
      }
      await user.save();
      return res.status(401).json({
        message: "비밀번호가 일치하지 않습니다.",
        remainingAttempts: 5 - user.failedLoginAttempts,
      });
    }

    user.failedLoginAttempts = 0;
    user.lastLoginAttempt = new Date();
    user.isLoggedIn = true;

    try {
      const response = await axios.get("https://api.ipify.org?format=json"); //공공장소 사용금지요
      const ipAddress = response.data.ip; //한번 정제함
      user.lastLoginIp = ipAddress;
    } catch (error) {
      console.log("IP 주소를 가져오는데 실패했습니다: ", error.message);
    }

    await user.save();

    const token = jwt.sign(
      { userId: user._id, username: user.username },
      process.env.JWT_SECRET,
      { expiresIn: "24h" } //토큰 만료는 24시간
    );

    console.log(token); //오 된다

    res.cookie("token", token, {
      httpOnly: true, //자바스크립트에서 쿠키 접근 불가
      secure: "production", //https에서만 쿠키 전송
      sameSite: "strict", //같은 사이트에서만 쿠키 전송
      maxAge: 24 * 60 * 60 * 1000, //24시간
    });

    const userWithoutPassword = user.toObject(); //구글링 해보니 이렇게 하면 문서 타입을 일반 객체로 변환할 수 있다고 한다
    delete userWithoutPassword.password; //보안때문에 비밀번호는 삭제할 수 있다고 한다

    res.json({ user: userWithoutPassword });
  } catch (error) {
    console.log("서버 오류: ", error);
    res.status(500).json({ message: "서버 오류가 발생했습니다." });
  }
});

router.post("/logout", async (req, res) => {
  console.log(req.cookies.token);
  try {
    const token = req.cookies.token;

    if (!token) {
      return res.status(400).json({ message: "이미 로그아웃된 상태입니다." });
    }

    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      const user = await User.findById(decoded.userId);

      if (user) {
        user.isLoggedIn = false;
        await user.save();
      }
    } catch (error) {
      console.log("토큰 검증 오류: ", error.message);
    }

    res.clearCookie("token", {
      httpOnly: true,
      secure: "production",
      sameSite: "strict",
    });

    res.json({ message: "로그아웃되었습니다." });
  } catch (error) {
    console.log("로그아웃 오류: ", error.message);
    res.status(500).json({ message: "서버 오류가 발생했습니다." });
  }
});

router.delete("/delete/:userId", async (req, res) => {
  try {
    const user = await User.findByIdAndDelete(req.params.userId);
    if (!user) {
      return res.status(404).json({ message: "사용자를 찾을 수 없습니다." });
    }
    res.json({ message: "사용자가 성공적으로 삭제되었습니다." });
  } catch (error) {
    res.status(500).json({ message: "서버 오류가 발생했습니다." });
  }
});

module.exports = router;

 


답변 1

0

닭강정님의 프로필 이미지
닭강정
지식공유자

안녕하세요. 질문 남겨주셔서 감사합니다!

백엔드 코드로는 큰 문제가 보이지 않지만 몇 가지 확인해주시면 감사하겠습니다.

1. F12 개발자 도구 > 응용 프로그램 > 쿠키

관리자 로그인 후 브라우저에 쿠키가 제대로 저장되지 않아서 request에 제대로 전송되지 않았을 가능성이 있습니다.

image.png

 

2. index.js에서 cookie-parser 설정

const express = require("express");
const cookieParser = require("cookie-parser");

const app = express();

app.use(cookieParser()); // 쿠키 파서 미들웨어 추가

cookie-parser - npm

cookie-parser를 패키지를 설치 후 프론트엔드에서 전송되는 쿠키 값을 읽을 수 있습니다. 이 부분도 확인 부탁드립니다.

추가적으로, 이것말고 여러가지 해결방법이 있지만 제시된 2가지 방법으로 해결이 어려우시다면 adminLogin.jsx 및 관리자 메뉴바의 로그아웃 부분과 개발자 도구의 캡쳐사진도 부탁드리겠습니다. 감사합니다!

Schwartz Melvin님의 프로필 이미지

현재 강의에서는 관리자페이지, adminLogin.jsx 생성 전입니다. 일단 거기까지 진행을 하고 답변을 드리면 될까요?

index.js

require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const cookieParser = require("cookie-parser");
const app = express();
const PORT = 3000;

const userRoutes = require("./routes/user");

app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded());

app.use("/api/auth", userRoutes);

app.get("/", (req, res) => {
  res.send("Hello world");
});

mongoose
  .connect(process.env.MONGO_URI)
  .then(() => console.log("MongoDB 연결 성공."))
  .catch((error) => console.log("MongoDB 연결 실패: ", error));

app.listen(PORT, () => {
  console.log("Server is running");
});
닭강정님의 프로필 이미지
닭강정
지식공유자

네 그렇습니다. 개발 진행하시면서 관리자 로그인 후 브라우저 쿠키에 제대로 토큰이 저장되어 있는지 확인하시면 될 것 같습니다!