본문 바로가기
개발/Spring

[SpringBoot] React + TypeScript + JPA 프로젝트 (8) - useContext 전역변수 다루기

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

이전글

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

 

[SpringBoot] React + TypeScript + JPA 프로젝트 (7) - 게시판 만들기(글 작성)

이전글https://greed-yb.tistory.com/309 [SpringBoot] React + TypeScript + JPA 프로젝트 (6) - 게시판 만들기(페이징 처리)이전글https://greed-yb.tistory.com/308 [SpringBoot] React + TypeScript + JPA 프로젝트 (5) - 게시판 만들기

greed-yb.tistory.com

 

 

로그인했을 때 유저 정보를 담아두고

페이지마다 기능 동작할 때 확인용으로 사용하려고 한다

 

 

 

userInfo.tsx
import React, {createContext, useState, ReactNode, useContext} from 'react';

// 권한 및 필요한게 있다면 추가하자
interface UserInfoType {
    username: string;
    setUserInfo: (username: string) => void;
}

const UserInfo = createContext<UserInfoType>({
    username: '',
    setUserInfo: () => {},
});

const UserProvider = ({ children }: { children: ReactNode }) => {
    const [username, setUsername]  = useState<string>('');

    return (
        <UserInfo.Provider value={{ username, setUserInfo: setUsername }}>
            {children}
        </UserInfo.Provider>
    );
};

// UserInfo를 사용하는 커스텀 훅 정의
const useUserContext = () => useContext(UserInfo);

export { UserInfo, UserProvider, useUserContext };

 

사용자 정보를 저장하기 위한 코드

현재 로그인한 사용자 id 만 받아오게 했는데

필요에 따라서 권한이나 여러 정보를 담으면 된다

 

 

 

login.tsx 수정

import React, { useState} from "react";
import axios from "axios";
import {Link, useNavigate} from "react-router-dom";
import { useUserContext } from '../utils/UserInfo';

function Login() {

    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');

    const { setUserInfo } = useUserContext();
    const navigate = useNavigate();  // navigate 훅 사용

    const handleLogin = async (e: { preventDefault: () => void; }) => {
        e.preventDefault();
        try {
            const response = await axios.post('http://localhost:7172/api/login', {
                username,
                password,
            }, {withCredentials: true }); // 쿠키를 자동 입력해 준다
            if(response.data.message == "success"){
            
                // 로그인이 성공했기 때문에 해당 id 를 저장한다
                setUserInfo(username );

                // 로그인 성공 후 홈 페이지로 이동
                navigate("/");
            }else {
                alert("로그인 실패하였습니다.");
            }
        } catch (error) {
            console.error('Login failed:', error);
        }
    };



    return(

 

 

 

 

 


 

 

 

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

 

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

이전글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.4Spri

greed-yb.tistory.com

여기에서 생성했던 auth, privateRoute 에 코드를 수정한다

 

 

 

auth.tsx

 

// 유저정보 가져오기
export const userNameCheck = async (): Promise<string | null> => {
    const checkToken = Cookies.get('refreshToken'); // refreshToken 가져오기

    try {
        const response = await axios.post("http://localhost:7172/api/username", {}, {
            params: { checkToken }
        });

        return response.data ? response.data : null;
    } catch (error) {
        console.error('error');
        return null; // 오류가 발생하면 null 반환
    }
};
//////////////////////////////////////////////////////////

 

코드를 추가해 준다

 

 

 

 

privateRoute.tsx

    // user 정보 저장
    const { setUserInfo } = useUserContext();
    React.useEffect(() => {
        const fetchUsername = async () => {
            const username = await userNameCheck(); // 비동기적으로 username 가져오기
            if (username) {
                setUserInfo(username); //  username 저장
            }
        };
        fetchUsername();
    }, [setUserInfo]);
    ////////////////////////////////////////////

 

저장되어 있는 username 이 없다면 페이지 이동 시 확인하여 

auth.tsx 에서 만든 userNameCheck() 를 사용하여

사용자의 username 을 저장한다

 

 

 

 

controller
    @PostMapping("/username")
    @ResponseBody
    public String username(@RequestParam String checkToken) throws Exception {
            // refreshToken 에서 사용자의 username 을 가져온다
            String username = jwtTokenUtil.getUsernameFromRefreshToken(checkToken);
        System.err.println("username : " + username);
            return username;
    }

사용자 username 을 가져오기 위해 refresh token 에서 데이터를 추출하였다

 

Spring Security 가 잘 설정되어 있고 오류가 없다면

// Spring Security 에서 로그인한 사용자 id 가져오기
String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

이 방식을 선호하는데, 현재 Security 가 제대로 동작하지 않아서 refresh 에서 추출하였다

 

 

 

App.tsx
import {BrowserRouter, Route, Routes, useLocation} from "react-router-dom";
import Header from "./components/layout/header";
import Footer from "./components/layout/footer";
import Sidebar from "./components/layout/sidebar";
import Dashboard from "./components/pages/dashboard";
import Profile from "./components/pages/profile";
import Login from "./components/pages/login";
import PrivateRoute from "./components/utils/privateRoute";
import Register from "./components/pages/register";
import Board from "./components/pages/board";
import BoardWrite from "./components/pages/boardWrite";
import BoardRead from "./components/pages/boardRead";
import {UserProvider} from "./components/utils/UserInfo";

// 페이지 layout 구분하기
function AppLayout(){
    const location = useLocation();
    const layoutPass = ["/signin" , "/register"];           // 로그인 , 회원가입 페이지 제외
    const pageCheck = layoutPass.includes(location.pathname);

    return (
            <div className="App">
                {!pageCheck && <Header/>}
                {!pageCheck && <Sidebar/>}
                <Routes>
                    <Route path="/signin" element={<Login />}/>
                    <Route path="/register" element={<Register />}/>
                    <Route path="/" element={
                        <PrivateRoute>
                            <Dashboard />
                        </PrivateRoute>
                    }/>
                    <Route path="/board" element={
                        <PrivateRoute>
                            <Board />
                        </PrivateRoute>
                    }/>
                    <Route path="/boardWrite" element={
                        <PrivateRoute>
                            <BoardWrite />
                        </PrivateRoute>
                    }/>
                    <Route path="/boardRead/:no" element={
                        <PrivateRoute>
                            <BoardRead />
                        </PrivateRoute>
                    }/>
                    <Route path="/profile" element={
                    <PrivateRoute>
                        <Profile />
                    </PrivateRoute>
                    }/>
                </Routes>
                {!pageCheck && <Footer/>}
            </div>
    );
}


function App() {
    return (
        // 저장한 유저정보를 사용하기 위해서 감싼다
        <UserProvider>
            <BrowserRouter>
                <AppLayout />
            </BrowserRouter>
        </UserProvider>
    );
}

export default App;

 

<BrowserRouter> 위에 

<UserProvider> 을 감싸서 username 을 전역변수로 사용할 수 있게 한다

 

 

 

 

 

TEST
import {useUserContext} from "../utils/UserInfo";

function Board() {

    const { username } = useUserContext();
    console.log("Board 페이지 : "+ username);

 

어느 페이지에서든 useUserContext() 로 데이터를 가져올 수 있다

 

 

 

 

이제 username 또는 권한으로 수정, 삭제 기능을 만들면 된다

 

 


 

며칠간 JPA 공부한다고 React 를 안 만졌더니

그새 또 어리바리하고 있다...

 

공부하면서 너무 많은 난관에 부딪히다 보니 

그저 회사에서 일처리 하듯이... 그저 해결하기 위해

머릿속에 남지 않는 코딩을 하고 있는 기분이다

그리고 문법이 도저히 적응이 안 된다...

 

현재 포스팅한 방식도 내 맘대로 개발한 방식이므로 FM 적인 방법은 아니라고 단언할 수 있다

 

그저 이번에도 이런 흐름으로 사용한다 정도로 참고하길 바란다

 

 

 

댓글