본문 바로가기
개발/Spring

[SpringBoot] React + TypeScript + JPA 프로젝트 (12) - 로그아웃 하기

by 코딩하는 흰둥이 2024. 11. 23.

이전글

https://greed-yb.tistory.com/324

 

[SpringBoot] React + TypeScript + JPA 프로젝트 (11) - 게시판 만들기(삭제하기)

이전글https://greed-yb.tistory.com/322 [SpringBoot] React + TypeScript + JPA 프로젝트 (9) - 게시판 만들기(상세보기)이전글https://greed-yb.tistory.com/321 [SpringBoot] React + TypeScript + JPA 프로젝트 (8) - useContext 전역변수

greed-yb.tistory.com

 

 

테스트를 진행할 때마다 

계정을 변경해야하는데

로그아웃 기능이 없어서 매번 token 지우느라 힘들었다...

미리 좀 만들걸....

 

 

 대부분 Bootstrap 에 logout 은 사용자 프로필 dropbox 아래 있다

 

 

 

header.tsx

이전글

https://greed-yb.tistory.com/304

 

[SpringBoot] React + TypeScript + JPA 프로젝트 (2) - Bootstrap 적용

이전글https://greed-yb.tistory.com/303 [SpringBoot] React + TypeScript + JPA 프로젝트 (1) - 생성Java 17GradleSprinb Boot 3.3.4Spring Security 6.3.3Oracle 11gIntelliJ UltimateDBeaverReactJPASpringBoot 프로젝트 생성   프로젝트 생성

greed-yb.tistory.com

 

header.tsx 는 예전에 layout 으로 따로 만들었던 파일이다

 

 

a태그로 되어있는 logout 이다

 

 

 

 

 

첫 번째 방법

import {Link} from "react-router-dom";
import Cookies from "js-cookie";
import {useNavigate} from "react-router-dom";
import axios from "axios";

function Header() {
    const navigate = useNavigate();

    const logoutBtn = () => {
        if(!confirm("로그아웃 하시겠습니까?")){
            return;
        }

        Cookies.remove("accessToken");
        Cookies.remove("refreshToken");
        navigate("/");
    }
    
    return (
    	<div className="App">
            <li>
                <a className="dropdown-item d-flex align-items-center" href="#" onClick={logoutBtn}>
                  <i className="bi bi-box-arrow-right"></i>
                  <span>Sign Out</span>
                </a>
            </li>    
        </div>
    );
}
export default Header;

화면단에서 클라이언트의 쿠키만 삭제하는 방법이다

이렇게 되면 서버단의 쿠키 정보는 남아있게 되어 여러 문제가 생길 수 있다

 

 

 

 

 

 

두 번째 방법

import {Link} from "react-router-dom";
import Cookies from "js-cookie";
import {useNavigate} from "react-router-dom";
import axios from "axios";

function Header() {
    const navigate = useNavigate();

    const logoutAxiosBtn = async () => {
        if (!confirm("로그아웃 하시겠습니까?")) {
            return;
        }

        await axios.post("http://localhost:7172/api/logout", {}, { withCredentials: true })
            .then((response) => {
                if (response.data === "success") {
                    navigate("/");
                } else {
                    alert("에러 발생");
                }
            }).catch((error) => {
                alert("에러 발생");
            })
    }


    return (
        <div className="App">
             <li>
                <a className="dropdown-item d-flex align-items-center" href="#" onClick={logoutAxiosBtn}>
                  <i className="bi bi-box-arrow-right"></i>
                  <span>Sign Out</span>
                </a>
            </li>    
        </div>
    );
}
export default Header;

 

    // 로그아웃 처리
    @PostMapping("/logout")
    public String logout(HttpServletResponse response) {

        try{
            // 쿠키에서 accessToken 제거
            Cookie accessTokenCookie = new Cookie("accessToken", null);
            accessTokenCookie.setMaxAge(0); // 쿠키 만료
            accessTokenCookie.setPath("/"); // 경로 지정
            response.addCookie(accessTokenCookie);

            // 쿠키에서 refreshToken 제거
            Cookie refreshTokenCookie = new Cookie("refreshToken", null);
            refreshTokenCookie.setMaxAge(0); // 쿠키 만료
            refreshTokenCookie.setPath("/"); // 경로 지정
            response.addCookie(refreshTokenCookie);

            return "success";

        }catch (Exception e){
            e.printStackTrace();
            return "fail";
        }
    }

 

서버단에서 HttpServletResponse 을 이용하여 

쿠키 정보를 만료 시키는 방법이다

axios 에 withCredentials: true 설정으로

만료된 쿠키 정보가 반영된다

 

단점으로는 화면단에서 즉각 반영이 되어야 하는데

반영이 안 되는 경우 쿠키 정보가 남아있을 수 도 있다

 

 

 

 

 

세 번째 방법

import {Link} from "react-router-dom";
import Cookies from "js-cookie";
import {useNavigate} from "react-router-dom";
import axios from "axios";

function Header() {
    const navigate = useNavigate();

    const logoutAxiosBtn = async () => {
        if (!confirm("로그아웃 하시겠습니까?")) {
            return;
        }

        await axios.post("http://localhost:7172/api/logout", {}, { withCredentials: true })
            .then((response) => {
                if (response.data === "success") {
                    Cookies.remove("accessToken"); // access token 삭제
                    Cookies.remove("refreshToken");// refresh token 삭제
                    navigate("/");
                } else {
                    alert("에러 발생");
                }
            }).catch((error) => {
                alert("에러 발생");
            })
    }


    return (
        <div className="App">
             <li>
                <a className="dropdown-item d-flex align-items-center" href="#" onClick={logoutAxiosBtn}>
                  <i className="bi bi-box-arrow-right"></i>
                  <span>Sign Out</span>
                </a>
            </li>    
        </div>
    );
}
export default Header;

 

    // 로그아웃 처리
    @PostMapping("/logout")
    public String logout(HttpServletResponse response) {

        try{
            // 쿠키에서 accessToken 제거
            Cookie accessTokenCookie = new Cookie("accessToken", null);
            accessTokenCookie.setMaxAge(0); // 쿠키 만료
            accessTokenCookie.setPath("/"); // 경로 지정
            response.addCookie(accessTokenCookie);

            // 쿠키에서 refreshToken 제거
            Cookie refreshTokenCookie = new Cookie("refreshToken", null);
            refreshTokenCookie.setMaxAge(0); // 쿠키 만료
            refreshTokenCookie.setPath("/"); // 경로 지정
            response.addCookie(refreshTokenCookie);

            return "success";

        }catch (Exception e){
            e.printStackTrace();
            return "fail";
        }
    }

 

첫 번째와 두 번째 방식을 둘 다 사용한다 (Controller 단은 동일)

서버에서도 만료시키고 화면에서도 삭제 시킨다

 

 

 

 

TEST

 

 

 

navigate("/") 경로로 이동 되었을 때  token 정보가 없기 때문에

login 페이지로 이동된 것을 볼 수 있다

댓글