ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • React 전역 변수를 사용하자 (부모-자식 컴포넌트 접근, Context)
    DEV/react 2024. 2. 21. 16:57

     

    React를 사용하면 Component로 화면을 분리하여 사용하게 된다.

     

    물론 Props을 통해 변수나 함수를 던지고 사용할 수 있지만 나처럼 멍청한 사람은 React의 특징을 이해하지도 못한채 router로 페이지를 나눠버리고 그 안에서도 여러 depth의 Component를 구성하게 되는 경우도 있다.

     

    API 통신을 통해 가져온 Token을 전역 변수로 설정하려고 했는데 로그인 페이지와 접속 이후 페이지가 React router로 나눠버리는 바람에 Props로 던지기가 힘들었다.

     

    그래서 전역 변수로 사용하는 법을 찾게 되었고 그 방법에 대해 설명하려고 한다.

     

    1. tkContext.js 생성

    index.js의 Router render 부분을 감싸줄 Provider를 만들어야 했다.

     

    나는 토큰을 저장하는 전역 변수를 만들기 위해 tk.Context.js를 생성하고 다음과 같이 작성하였다.

    import React, {createContext, useState} from "react";
    
    // Context 생성
    const tkContext = createContext();
    
    const TkProvider = ({ children }) => {
        // token을 관리할 공간 생성
        const [token, setToken] = useState('');
    
        return (
            <tkContext.Provider value={{ token, setToken }}>
                {children}
            </tkContext.Provider>
        )
    }
    
    // Context와 Provider export
    export { tkContext, TkProvider };

     

     

    2. Index.js 수정

    나는 Index.js에서 react router를 통해 페이지를 구성했다.

     

    render를 하고 있던 RouterProvider를 위에 작성한 TkProvider로 감싸주었다.

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import reportWebVitals from './reportWebVitals';
    import LoginPage from "./view/./page/LoginPage";
    import ManagerLoginPage from "./view/./page/ManagerLoginPage";
    import ManagementPage from "./view/./page/ManagementPage";
    import { createBrowserRouter } from 'react-router-dom';
    import { RouterProvider } from "react-router";
    import { TkProvider } from "./tkContext";   // Context 불러오기
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    const router = createBrowserRouter([
      {
        path: "/",
        element: <LoginPage />,
        // errorElement: <ErrorPage />,
      },
      {
        path: "/manager",
        element: <ManagerLoginPage />,
        // errorElement: <ErrorPage />,
      },
      {
        path: "/management",
        element: <ManagementPage />,
        // errorElement: <ErrorPage />,
      },
    ]);
    
    root.render(
        <TkProvider>
            <RouterProvider router={router} />
        </TkProvider>
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();

     

     

    3. Login API 호출에서 발급된 Token값을 provider value에 set(저장)

    본인 환경에서는 로그인 성공시 서버에서는 response header에 token을 적재하여 보내주고 있다.

     

    로그인 하는 컴포넌트와 로그인 후 이동하는 컴포넌트가 react route의 navigate로 이동하고 있어 props로 데이터 전송이 불가했다.

     

    서버에서 response를 받고 context provider value에 저장하자

    import style from '../../style/./page/LoginPage.module.css'
    import React, {useContext, useEffect, useState} from "react";
    import {useNavigate} from "react-router-dom";
    import axios from "axios";
    import Alert from "../component/Alert";
    import { tkContext } from "../../tkContext";	// 1. 생성한 컨텍스트 import
    
    const ManagerLoginPage = () => {
        // ...코드생략
    
        // Route navigate
        const navigate = useNavigate();
    
        const toLoginPage = () => { navigate('/'); }
        const toManagementPage = () => { navigate('/management'); }
        const enterKeyEvent = (e) => { if(e.key === 'Enter') { managerLogin(); } }
    
        // ...코드생략
    
        // 2. Token Context 사용 선언
        const { setToken } = useContext(tkContext);
    
        const managerLogin = async () => {
            try {
                const response = await axios.post('http://localhost:8080/api/auth/managerSignIn', {
                    email: email,
                    password: password,
                });
    
                if (response.status === 200) {
                    if(response.data.result === true) {
                        const authorizationHeader = response.headers['authorization'];
    
                        if (authorizationHeader) {
                            // 응답 헤더의 authorizationHeader에서 토큰 분리 후 provider의 setToken으로 전역으로 값을 저장
                            const newToken = authorizationHeader.split('Bearer ')[1];
                            setToken(newToken);
                        // ...코드생략
            } catch (error) {
                // request error
                addAlert('error', error, 3);
            }
        }
    
        return (
            <div className={style.backgroundContainer}>
                {/* ...코드생략 */}
            </div>
        );
    };
    
    export default ManagerLoginPage;

     

    4. Route navigate로 이동한 페이지에서 Token값 꺼내 쓰기

    본인 환경에서는 로그인 성공한 후 토큰을 저장하고 다른 페이지로 이동한다.

     

    또한 이동한 패이지 내부의 특정 컴포넌트에서 API를 요청할 때 전역 변수로 저장한 token값을 꺼내서 사용해야 했다.

     

    사용은 다음과 같다.

    import style from '../../style/component/CreateAccount.module.css';
    import React, {useContext, useState} from "react";
    import axios from "axios";
    import { tkContext } from "../../tkContext";	// 1. Context 불러오기
    
    const CreateAccount = ({addAlert}) => {
        //... 코드 생략
    
        // 2. Token 사용을 위해 등록
        const { token } = useContext(tkContext);    // tkContext 구독
    
        const singUp = async () => {
            try {
                const response = await axios.post(
                    'http://localhost:8080/api/auth/signUp',
                    {
                        name: name,
                        email: email,
                        password: password,
                        confirmPassword: confirmPassword,
                        phoneNumber: phoneNumber,
                        userType: userType,
                    },
                    {
                        headers: {
                            Authorization: `Bearer ${token}`	// 3. 불러온 토큰 사용
                        }
                    }
                );
    
    
                // ...코드생략
        }
    
        return (
            <div className={style.formContainer}>
                {/* ...코드생략 */}
            </div>
        )
    };
    
    export default CreateAccount;

     

     

    결과

    불러온 토큰값을 잘 넣어서 보내짐 :)

     

    댓글

Designed by Tistory.