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

박해진님의 프로필 이미지
박해진

작성한 질문수

[리뉴얼] React로 NodeBird SNS 만들기

미들웨어로 라우터 검사하기

isLoggedIn추가 후 로그아웃 안되는 문제 발생

해결된 질문

작성

·

265

·

수정됨

0

안녕하세요 선생님

상황)req.logout안에 콜백 함수넣어서 로그아웃이 잘 되고 있었는데, isLoggedIn추가 후 상태코드 401이 뜨고 preview에는 로그인이 필요합니다가 뜨며 로그아웃 안되는 상황입니다.

 

network

로그인 했을 때
스크린샷 2024-01-08 오후 3.10.51.png

로그아웃 했을 때
network

스크린샷 2024-01-08 오후 3.11.03.png

로그아웃 했을 때

preview

스크린샷 2024-01-08 오후 3.06.14.png

 

redux

로그인 했을 때

스크린샷 2024-01-08 오후 3.16.17.png

스크린샷 2024-01-08 오후 3.09.00.png

로그아웃 했을 때

스크린샷 2024-01-08 오후 3.09.11.png스크린샷 2024-01-08 오후 3.09.17.png

시도해본것)

로그인 후 세션정보 콘솔 출력

router.post('/login', isNotLoggedIn, (req, res, next)=> {
    passport.authenticate('local',(err, user, info) => {
        if(err) {
            console.error(err);
            return next(err);
        }
        if(info) {
            return res.status(401).send(info.reason);
        }
        return req.login(user,async(loginErr)=> {
            if(loginErr) {
                console.error(loginErr);
                return next(loginErr);
            }
            console.log('로그인 후 세션 정보:', req.session);
//생략
        });
    })(req, res, next);
}); //POST /user/login

스크린샷 2024-01-08 오후 4.11.03.png

user.js의 logout에서 에러 발생시 출력 => 출력 x

router.post('/logout', isLoggedIn, (req, res) => {
    req.logout((err) => {
        if (err) {
            console.error(err);
            return res.status(500).send('로그아웃 중 오류가 발생했습니다.');
        }
       res.send('ok');
       req.session.destroy();
    });
});

Middlewares에서 req.isAuthenticated()확인 => 결과 false

exports.isLoggedIn = (req, res, next) => {
    console.log('로그인 상태 확인:', req.isAuthenticated());
    if(req.isAuthenticated()) {
        next();
    } else {
        res.status(401).send('로그인이 필요합니다.');
    }
}


질문)req.isAuthenticated가 false로 나와서 로그아웃이 안되는데 원인과 해결방법이 궁금합니다. 혹시 로그인하면 이것을 true가 되게 바꾸는 방법이 있나요?

req.isAuthenticated()



작성한 코드)
UserProfile

import React, {useCallback} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {Card, Avatar, Button} from 'antd';
import styled from 'styled-components';
import {logoutRequestAction} from '../reducers/user';
const ButtonWrapper = styled(Button)`
    display: block;
    margin-left: auto;
    margin-right: auto;
`
const UserProfile = () => {
    const dispatch = useDispatch();
    const { me, logOutLoading } = useSelector((state) => state.user);
    const onLogout = useCallback(()=>{
        dispatch(logoutRequestAction());
    }, []);
    return (
      //생략
        <ButtonWrapper onClick={onLogout} loading={logOutLoading}>로그아웃</ButtonWrapper>
 
    );
    
}
export default UserProfile;

reducers/user

import {produce} from 'immer';

export const initialState = {
    logOutLoading: false,//logout시도중
    logOutDone: false,
    logOutError: null,
}
export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';
export const logoutRequestAction = () => {
    return {
        type: LOG_OUT_REQUEST
    }
}


const reducer = (state = initialState, action) => produce(state, (draft) => {
    switch(action.type){
        case LOG_OUT_REQUEST:
            draft.logOutLoading = true;
            draft.logOutDone = false;
            draft.logOutError = null;
            break; 

        case LOG_OUT_SUCCESS:
            draft.logOutLoading = false;
            draft.logOutDone = true;
            draft.me = null;
            break;

        case LOG_OUT_FAILURE:
            draft.logOutLoading = false;
            draft.logOutError = action.error;
            break;
        default:
            break;
    }
});

export default reducer;

sagas/user

import axios from 'axios';
import { all, call, delay, fork, put, takeLatest } from 'redux-saga/effects';
import {
    LOG_OUT_FAILURE,
    LOG_OUT_REQUEST, LOG_OUT_SUCCESS,
} from '../reducers/user';
function logOutAPI(){
    return axios.post('/user/logout');
}

function* logOut() {
    try{
        yield call(logOutAPI);
        yield put({
            type: LOG_OUT_SUCCESS,
        });
    } catch(err) {
        yield put({
            type: LOG_OUT_FAILURE,
            error: err.response.data
        }); 
    }
}

 function*  watchLogOut(){
     yield takeLatest(LOG_OUT_REQUEST, logOut);
 }
export default function* userSaga() {
    yield all ([
        fork(watchLogOut),
    ])
}

routes/user.js

const express = require('express');
const bcrypt = require('bcrypt');
const {User, Post} = require('../models');
const router = express.Router();
const passport = require('passport');
const {isLoggedIn, isNotLoggedIn} = require('./middlewares');
router.post('/login', isNotLoggedIn, (req, res, next)=> {
    passport.authenticate('local',(err, user, info) => {
        if(err) {
            console.error(err);
            return next(err);
        }
        if(info) {
            return res.status(401).send(info.reason);
        }
        return req.login(user,async(loginErr)=> {
            if(loginErr) {
                console.error(loginErr);
                return next(loginErr);
            }
           const fullUserWithoutPassword = await User.findOne({
                where: {id: user.id},
                attributes:{
                    exclude:['password']
                },
                include: [{
                    model: Post
                }, {
                    model: User,
                    as:'Followings',
                }, {
                    model: User,
                    as:'Followers'
                }]
            })
            return res.status(200).json(fullUserWithoutPassword);
        });
    })(req, res, next);
}); //POST /user/login
//생략
const {isLoggedIn, isNotLoggedIn} = require('./middlewares');
router.post('/logout', isLoggedIn, (req, res) => {
    req.logout(() => {
        req.session.destroy();
        res.send('ok'); 
    });
});

routes/middlewares

exports.isLoggedIn = (req, res, next) => {
    if(req.isAuthenticated()) {
        next();
    } else {
        res.status(401).send('로그인이 필요합니다.');
    }
}

exports.isNotLoggedIn = (req, res, next) => {
    if(!req.isAuthenticated()){
        next();
    } else {
        res.status(401).send('로그인 하지 않은 사용자만 접근이 가능합니다.');
    }
}


passport/index

const passport = require('passport');
const local = require('./local');
const { User } = require('../models');

module.exports = () => {
    passport.serializeUser((user,done) => {
        done(null,user.id);//첫번째 인자 에러, 두번째 인자 성공(쿠키와 묶어줄 user아이디만 저장)
    });
    passport.deserializeUser(async(id, done) => {
        try {
           const user = await User.findOne({where:{id}})
           done(null,user);
        } catch(error) {
            console.error(error);
            done(error);
        }
    });
    local();
};

passport/local

const passport = require('passport');
const {Strategy:LocalStrategy} = require('passport-local');
const bcrypt = require('bcrypt');
const {User} = require('../models');
const express = require('express');
const router = express.Router();
router.post('/login', passport.authenticate('local'));

module.exports = () => {
    passport.use(new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password',
    }, async(email, password, done) => {
        try {
            const user = await User.findOne({
                where:{email}
            });
            if(!user) {
                return done(null, false, {reasone: '존재하지 않는 이메일입니다!'})
            }
            const result = await bcrypt.compare(password, user.password);
            if(result) {
                return done(null, user);//성공에 사용자 정보 넘겨줌
            }
            return done(null, false, {reason:'비밀번호가 틀렸습니다.'});
        } catch(error) {
            return done(error);
        } 
    }));
}

 

사용 하는 OS )mac
설치 버전)

back

{
  "name": "react-nodebird-back",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "luckyhaejin",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^5.1.1",
    "cookie-parser": "^1.4.6",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "express-session": "^1.17.3",
    "mysql2": "^3.6.5",
    "passport": "^0.7.0",
    "passport-local": "^1.0.0",
    "sequelize": "^6.35.2",
    "sequelize-cli": "^6.6.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.22"
  }
}

 

답변 2

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

로그아웃 시에도 쿠키 전송하셨나요?

axios에 withCredentials: true 넣어주셔야합니다

0

박해진님의 프로필 이미지
박해진
질문자

감사합니당!!! 다음 강의 보고 따라했더니 해결됐오용 !!
다른 분들을 위해 남겨놓을게욧

back/app.js에 credentials:true 추가

app.use(cors({
    origin: 'http://localhost:3060',
    credentials:true
}));


front/sagas/index.js에 추가

axios.defaults.withCredentials = true;

 

박해진님의 프로필 이미지
박해진

작성한 질문수

질문하기