Javascript/React

[React] 공개 라우트와 비공개 라우트

kyoulho 2024. 8. 1. 20:46

웹 사이트에 회원가입을 해야지만 이용할 수 있는 페이지가 있다. 이처럼 누구나 접속할 수 있는 경로를 공개 라우트, 로그인한 사용자만 접속 가능한 경로를 비공개 라우트라고 한다.

예제

회원가입을 해야지만 이용할 수 있는 비공개 라우트와 누구나 접근 가능한 공개 라우트를 설정하는 예제이다. 이를 위해 RequireAuth 컴포넌트를 사용해 비공개 라우트를 보호하고, Layout 컴포넌트를 통해 공통 레이아웃을 정의한다.

useAuth.tsx

import type {FC, PropsWithChildren} from "react";
import {createContext, useCallback, useContext, useState} from "react";
import * as U from "../utils";

// LoggedUser 타입 정의: email과 password를 포함
export type LoggedUser = {
    email: string
    password: string
}

// Callback 타입 정의: 인수가 없고 반환 값이 없는 함수
type Callback = () => void

// ContextType 타입 정의: 인증 관련 메서드와 loggedUser 상태를 포함
type ContextType = {
    loggedUser?: LoggedUser
    signup: (email: string, password: string, callback?: Callback) => void
    login: (email: string, password: string, callback?: Callback) => void
    logout: (callback?: Callback) => void
}

// AuthContext 생성: 기본값으로 각 메서드를 빈 함수로 설정
export const AuthContext = createContext<ContextType>({
    signup: (email: string, password: string, callback?: Callback) => {
    },
    login: (email: string, password: string, callback?: Callback) => {
    },
    logout: (callback?: Callback) => {
    }
})

// AuthProviderProps 타입 정의 (현재는 빈 객체)
type AuthProviderProps = {}

// AuthProvider 컴포넌트 정의: 인증 상태와 메서드를 제공
export const AuthProvider: FC<PropsWithChildren<AuthProviderProps>> = ({children}) => {
    const [loggedUser, setLoggedUser] = useState<LoggedUser | undefined>(undefined); // loggedUser 상태 정의

    // signup 메서드 정의: 새로운 사용자 설정
    const signup = useCallback((email: string, password: string, callback?: Callback) => {
        const user = {email, password}
        setLoggedUser(_ => ({email, password})); // loggedUser 상태 업데이트
        U.writeObjectP('user', user).finally(() => callback && callback())
    }, []);

    // login 메서드 정의: 사용자 로그인 설정
    const login = useCallback((email: string, password: string, callback?: Callback) => {
        setLoggedUser(_ => ({email, password})); // loggedUser 상태 업데이트
        callback && callback();
    }, []);

    // logout 메서드 정의: 사용자 로그아웃 설정
    const logout = useCallback((callback?: Callback) => {
        setLoggedUser(undefined); // loggedUser 상태 초기화
        callback && callback();
    }, []);

    // Context 값 설정
    const value = {
        loggedUser,
        signup,
        login,
        logout
    }

    // AuthContext.Provider를 사용하여 하위 컴포넌트에 값 제공
    return <AuthContext.Provider value={value} children={children}/>
}

// useAuth 훅 정의: AuthContext의 값을 쉽게 사용하기 위해
export const useAuth = () => {
    return useContext(AuthContext); // AuthContext의 값을 반환
}

Layout.tsx

Layout 컴포넌트는 내비게이션 바와 푸터를 포함하여 공통 레이아웃을 정의한다. Outlet을 사용해 하위 라우트를 렌더링 한다.

import {Outlet} from "react-router-dom";
import NavigationBar from "./NavigationBar";
import Footer from "./Footer";

export default function Layout() {
    return (
        <>
            <NavigationBar/>
            <Outlet/>
            <Footer/>
        </>
    )
}

RequireAuth.tsx

RequireAuth 컴포넌트는 사용자가 인증되지 않은 경우 이전 페이지로 리디렉션 한다. useAuth 훅을 사용해 현재 로그인 상태를 확인한다.

import type {FC, PropsWithChildren} from "react";
import {useAuth} from "../../contexts";
import {useNavigate} from "react-router-dom";
import {useEffect} from "react";

type RequireAuthProps = {}

const RequireAuth: FC<PropsWithChildren<RequireAuthProps>> = ({children}) => {
    const {loggedUser} = useAuth();
    const navigate = useNavigate();

    useEffect(() => {
        if (!loggedUser) navigate(-1)
    }, [loggedUser, navigate])

    return <>{children}</>
};

export default RequireAuth

RoutesSetup.tsx

RoutesSetup 컴포넌트는 모든 라우트를 설정한다. Layout 컴포넌트를 사용해 공통 레이아웃을 제공하고, RequireAuth를 사용해 비공개 라우트를 보호한다.

import {Route, Routes} from "react-router-dom";
import NoMatch from "./NoMatch";
import Layout from "./Layout";
import Board from "../pages/Board";
import LandingPage from "./LandingPage";
import Login from "./Auth/Login";
import Logout from "./Auth/Logout";
import SignUp from "./Auth/SignUp";
import RequireAuth from "./Auth/RequireAuth";

export default function RoutesSetup() {
    return (
        <Routes>
            <Route path={"/"} element={<Layout/>}>
                <Route index element={<LandingPage/>}/>
                <Route
                    path={"/board"}
                    element={
                        <RequireAuth>
                            <Board/>
                        </RequireAuth>
                    }/>
                <Route path={"*"} element={<NoMatch/>}/>
            </Route>
            <Route path={"/signup"} element={<SignUp/>}/>
            <Route path={"/login"} element={<Login/>}/>
            <Route
                path={"/logout"}
                element={
                    <RequireAuth>
                        <Logout/>
                    </RequireAuth>
                }/>
        </Routes>
    )
}
728x90

'Javascript > React' 카테고리의 다른 글

[React] 리액트 라우터  (0) 2024.08.01
[React] React Beautiful DnD 라이브러리  (0) 2024.07.31
[React] React DnD 라이브러리  (0) 2024.07.31
[React] 리덕스 미들웨어  (0) 2024.07.29
리듀서 활용하기  (0) 2024.07.22