본문 바로가기

프로젝트

[오류수정] 로그아웃 후 다시 로그인했을 때 보호된 리소스에 접근 오류

현재까지의 상황 : 

1. 맨처음 localhost:3000으로 npm start를 하면 /home에 접속된다. 

허접하고 부끄러운 UI ㅠㅠ

2.

로그인 상태가 아니므로 미션 목록을 보려고 시작하기를 누르면

login화면으로 자동 넘어가게 만들어 놓았다. 

 

3. 로그인을 하면 /home에 접속되지만, login상태가 아닐때와는 다른 화면이 나온다.

(내정보와 로그아웃 버튼 렌더링, 회원가입과 로그인버튼 미표시)

(오늘의 미션을 수행해볼까요? 문구와 시작하기 버튼 표시)

 

4. 시작하기 버튼을 누르면 미션목록 렌더링

 

 

문제 상황 정리: 

1. 현재까지의 상황 4.의 화면에서 로그아웃을 하면

 

import { createSlice } from "@reduxjs/toolkit";
import Cookies from "universal-cookie";

const cookies = new Cookies();

const initialAuthState = {
  token: null,
  user: null,
};

const authSlice = createSlice({
  name: "authentication",
  initialState: initialAuthState,
  reducers: {
    login(state, action) {
      state.token = action.payload;
    },

    logout(state) {
      state.token = null;
      state.user = null;
      cookies.remove("refreshToken");
    },
    user(state, action) {
      state.user = action.payload;
    },
    setToken(state, action) {
      state.token = action.payload;
    },
  },
});

export const authActions = authSlice.actions;
export default authSlice.reducer;

위의 logout reducer에 의해 user와 token이 null 로 비워지게 된다. 

cookie에 저장해두었던 refresh token도 비워지게 된다. 

 

2. 네트워크 탭에서는 refresh token을 가져오는 api와 mission목록을 가져오는 api가 아래와 같이

400오류로 실패하게 된다. 

 

 

3. 그리고 user가 없으므로 /mission에서  /login으로 이동하여 로그인을 유도한다.

4. 이 상태에서 다시 로그인을 하면 다시 현재까지의 상황 2처럼 /home으로 이동

5. 시작하기를 누르면 오류가 나게 된다. 

 

6. 정상적으로 작동하는 상황 : 

localhost:3000으로 들어가서 다시 로그인을 하면 mission목록 가져오는 api는 정상적으로 작동하고 있었다. 

 

문제의 이유 : 

 

네트워크 탭에서 해답을 얻을 수 있었다. 

즉, 로그아웃을 할 때 

 

import { useEffect, useState } from "react";
import {
  AiOutlineArrowLeft,
  AiOutlineArrowRight,
  AiOutlinePlusCircle,
} from "react-icons/ai";
import { Link, useNavigate } from "react-router-dom";
import Card from "../../UI/Card";
import classes from "./Mission.module.css";
import AddMissionForm from "./AddMissionForm";
import MissionList from "./MissionList";
import { useDispatch, useSelector } from "react-redux";
import Cookies from "universal-cookie";
import { fetchMissionData, getToken } from "../../store/missionAct";
import { authActions } from "../../store/authSlice";
// import auth, { authActions } from "../../store/auth";

const Mission = () => {
  const [onAddMission, setOnAddMission] = useState(false);
  const user = useSelector((state) => state.auth.user);
  const loadedData = useSelector((state) => state.mission.items);
  const dispatch = useDispatch();
  const navigate = useNavigate();
  let date = new Date();
  const thisMonth = date.getMonth() + 1;
  const authToken = useSelector((state) => state.auth.token);
  const cookies = new Cookies();
  const refreshToken = cookies.get("refreshToken");
  console.log("refresh Token : " + refreshToken);

  console.log("access Token : " + authToken);

  const onAddMissionHandler = () => {
    setOnAddMission((prev) => !prev);
  };

  useEffect(() => {
    if (!user || !refreshToken) {
      dispatch(authActions.logout());
      navigate("/login");
    }
  }, [dispatch, navigate, refreshToken, user]);

  useEffect(() => {
    dispatch(getToken(refreshToken));
    dispatch(fetchMissionData(authToken));
  }, [dispatch, navigate, authToken, refreshToken, user]);

  return (
    <Card>
      {user && (
        <>
          <section className={classes.month}>
            <Link to="/last-month">
              <AiOutlineArrowLeft />
            </Link>
            <div>{`${thisMonth}월 나의미션`}</div>
            <Link to="/next-month">
              <AiOutlineArrowRight />
            </Link>
          </section>
          <section>
            <ul>
              {loadedData.map((data) => (
                <MissionList
                  key={data.id}
                  id={data.id}
                  title={data.title}
                  cur_count={data.comp_cur}
                  tot_count={data.comp_tot}
                />
              ))}
            </ul>
            <Card className="act" onClick={onAddMissionHandler}>
              <p>
                미션추가하기 <AiOutlinePlusCircle />
              </p>
            </Card>
          </section>
        </>
      )}
      {onAddMission && <AddMissionForm onformClose={onAddMissionHandler} />}
    </Card>
  );
};

export default Mission;

 

user가 삭제가 되어 상태가 변경되었으니 56번째 줄 useEffect가 실행된다. 

그러므로 refresh token으로 access token을 가져오는 api 통신과 

가지고 있는  access token을 첨부하여 fetchMissionData api통신을 하는 건데 당연히 실패한다. 

 

여기서 문제는 로그인하면 다시 api 통신을 진행해야 하건만, 

이미 실패했고 어쩐일인지 user가 변경되었음에도

useEffect가 발동되지 않아 api통신이 진행되지 않는다.(ㅠㅠ)

 

 

문제 해결 : 

 

 

고민한 결과, 너무나 간단하게도 해결할 수 있었다 (ㅠㅠ)

 

  //미션 내용 가져오기
  useEffect(() => {
    if (user && refreshToken) {
      dispatch(getToken(refreshToken));
      dispatch(fetchMissionData(authToken));
    } else {
      //로그아웃을 누르거나 refreshToken이 만료되면 logout후 login화면으로
      dispatch(authActions.logout());
      navigate("/login");
    }
  }, [dispatch, navigate, authToken, refreshToken, user]);

useEffect를 발동시킬 때 한번 더 user와 refreshToken이 있는지 묻는것.

그리고 그렇지 않다면 그냥 logout을 진행해버렸다. 

그랬더니 user가 삭제되었을 때 useEffect가 발동되는 불상사를 막을 수 있었다.!!!