본문 바로가기

지출 내역 APP (개인 프로젝트)

지출 내역 리스트 기능 추가하기 : 3

기존 지출 내역 리스트에 기능 추가하기

구현 순서

  1. 회원가입, 로그인 페이지 및 폼의 UI 생성
  2. jwt 인증서버를 사용한 회원가입, 로그인 기능 추가 및 인증 상태 관리
  3. 내비게이션 바 생성 및 라우터 설정
  4. 프로필 수정 페이지 생성 및 프로필 수정 기능 추가
  5. json-server와 Tanstack Query를 사용해 지출 CRUD 구현
  6. 이후 디테일한 부분 및 선택 구현 사항 다루기

3. 내비게이션 바 생성 및 라우터 설정

(1) 내비게이션 바 UI 생성

내비게이션 바를 만들어서 홈 화면, 프로필 수정화면으로 이동 및 로그아웃 버튼 추가

src/components/navBar/NavBar.jsx

더보기
<S.Container>
            <S.LeftDiv>
                <S.NavButton onClick={() => navigate("/")}>Home</S.NavButton>{" "}
                <S.NavButton onClick={() => navigate("/mypage")}>내 프로필</S.NavButton>
            </S.LeftDiv>
            <S.RightDiv>
                <S.ProfileDiv>
                    <S.ProfileImage src={avatar} />
                    <S.Span>"{nickname}"님 환영합니다!</S.Span>
                </S.ProfileDiv>
                <S.Button onClick={() => handleLogout()} $color="red">로그아웃</S.Button>
            </S.RightDiv>

        </S.Container>

src/components/navBar/NavBar.styled.jsx

더보기
import styled from "styled-components";

export const Container = styled.div`
    width: 100%;
    background-color: white;
    height: 40px;
    padding: 20px;
    border: 5px solid black;
    border-radius: 8px;
    margin: 0 10px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-around;
`;

export const LeftDiv = styled.div`
    width: 50%;
    height: 40px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-around;
`;

export const RightDiv = styled.div`
    width: 50%;
    height: 40px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-around;
`;

export const NavButton = styled.button`
    border: none;
    background-color: transparent;
    font-family: inherit;
    font-size: 25px;
    cursor: pointer;
`;

export const ProfileDiv = styled.div`
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 10px;
`;

export const ProfileImage = styled.img`
    width: 50px;
    height: 50px;
    border-radius: 25px;
`;

export const Span = styled.span``;

export const Button = styled.button`
    border: none;
    border-radius: 4px;
    background-color: ${props => props.$color || "blue"};
    padding: 10px 20px;
    width: 20%;
    color: white;
    font-family: inherit;
    font-size: 16px;
    cursor: pointer;
    &:hover{
        filter:brightness(0.8);
    }
`;

 결과 UI

 

(2) 라우터 설정

-1. 내비게이션 바는 모든 화면에서 나타나야함

=> DefaultLayout을 생성해 라우트들을 감싸 Outlet을 사용해 모든 페이지에 내비게이션 바 뿌려주기

src/layouts/DefaultLayout.jsx

더보기
const DefalutLayout = () => {
    return (
        <div id="defalut-layout">
            <NavBar />
            <Outlet />
        </div>
    )
}

-2.

로그인 상태에서만 홈화면, 프로필 수정화면, 내비게이션 바가 보여야하고 로그아웃 상태에서 해당 페이지들로 이동하  려고 하면 로그인 페이지로 리다이렉트 시켜주기 &

로그인 페이지, 회원가입 페이지는 로그아웃 상태에서만 이동가능해야하고 로그인 상태에서 해당 페이지들로 이동하    려고 하면 메인페이지로 리다이렉트 시켜주기

=> PrivateRoutes, PublicRoutes를 만들어 따로 관리

 

src/routes/Router.jsx

더보기
// Redux로 관리하는 로그인 상태에 따라 화면에 보여줄지, 리다이렉트 시킬지 결정

const PrivateRoutes = ({ element: Element, ...rest }) => {
    const { isLogin } = useSelector(state => state.auth);
    
    return isLogin ? <Element {...rest} /> : <Navigate to="/login" />
    // 로그인 상태라면 보여주고 아니라면 로그인 페이지로 리다이렉트
}

const PublicRoutes = ({ element: Element, ...rest }) => {
    const { isLogin } = useSelector(state => state.auth);

    return !isLogin ? <Element {...rest} /> : <Navigate to="/" />
    // 로그아웃 상태라면 보여주고 아니라면 로그인 페이지로 리다이렉트
}

const Router = () => {

    return (
        <BrowserRouter>
            <Routes>
                <Route path='/login' element={<PublicRoutes element={Login} />} />
                <Route path='/signup' element={<PublicRoutes element={SignUp} />} />
                <Route path='/' element={<PrivateRoutes element={DefalutLayout} />}>
                    <Route index element={<PrivateRoutes element={Home} />} />
                    <Route path='/mypage' element={<PrivateRoutes element={MyPage} />} />
                    <Route path='/detail/:postId' element={<PrivateRoutes element={Details} />} />
                </Route>
            </Routes>
        </BrowserRouter>
    )
}

export default Router

 

(3) 내비게이션 바 기능 추가

더보기
const NavBar = () => {
    const navigate = useNavigate();
    const dispatch = useDispatch();
    
    // 로그인 상태와 유저 정보를 store에서 불러오기
    const { isLogin } = useSelector(state => state.auth);
    const { userInfo } = useSelector(state => state.auth);
    const nickname = userInfo?.nickname;
    const avatar = userInfo?.avatar;

    // 로그아웃 버튼 클릭시
    const handleLogout = () => {
        const isLogout = confirm("정말 로그아웃 하시겠습니까?");
        if (isLogout) {
            dispatch(logout());
            navigate("/login");
        }
    };

    // 로그인 상태가 변할 때 마다 서버에 유저정보를 요청해 담거나 비워줌
    useEffect(() => {
        const fetchUserInfo = async () => {
            try {
                const response = await authApi.get("/user");
                dispatch(setUserInfo(response.data));
            } catch (error) {
                console.error("Failed to fetch user info:", error);
            }
        };
        fetchUserInfo();
    }, [isLogin])

 

결과 - 로그인 상태

1. 내비게이션 바를 이용해 페이지 이동

2. 리다이렉트

 

결과 - 로그아웃 상태

리다이렉트