시간표 페이지를 비롯해 일부 페이지는 DB의 사용자 데이터를 기반으로 동작한다. 즉, 사용자가 저장되어 있지 않으면 이 페이지를 사용하는데 문제가 생긴다. 이미 SchedulePage.js 내부에서 사용자 정보가 확인되지 않았을 경우에 대한 예외처리가 되어 있지만. 그럼에도 관리와 사용자 편의를 위해 로그인해야만 접근할 수 있도록 PrivateRoute를 구현했다.
PrivateRoute로 감싸진 라우트에 접근 시, 로그인이 필요함을 alert하고 로그인 페이지로 이동시킨다.
// src/main/frontend/src/components/PrivateRoute.js
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
const PrivateRoute = ({ element }) => {
const { userInfo } = useAuth();
if (!userInfo) {
alert('로그인이 필요한 서비스입니다');
return <Navigate to="/login" replace />;
}
return element;
};
export default PrivateRoute;
useAuth로부터 현재 로그인한 사용자를 불러오고, 만약 사용자가 없을 경우 로그인 페이지로 보낸다. 그리고 아래와 같이 App.js에서 라우트를 수정한다.
<Route path="/schedule" element={<PrivateRoute element={<SchedulePage />} />} />
이렇게 하면 로그인하지 않은 상태로 SchedulePage에 접속하려 하면 "로그인하세요"라는 alert 메시지가 나타나고 LoginPage로 이동한다.
(사이바 수정 및 시간표 기능이 추가된 UI)
다음으로 기존의 사이드바(NavBar)을 펼치고 접을 수 있게 하고, 그에 따라 화면이 조정되도록 반응형 사이드바로 수정한다.
햄버거 아이콘을 추가하고, 햄버거 아이콘이 토글될 때 상태를 isOpen으로 전환하여 isOpen의 상태에 따라 NavBar과 App.css에서 정의하는 콘텐츠 페이지를 조정한다.
NavBar.js
// src/main/frontend/components/NavBar.js
function NavBar({ isOpen, toggleNavBar }) {
const { userInfo, logout } = useAuth();
const [isFullyOpen, setIsFullyOpen] = useState(isOpen);
useEffect(() => {
if (isOpen) {
setTimeout(() => setIsFullyOpen(true), 300); // NavBar가 열린 후 0.3초 후에 `opened` 클래스 추가
} else {
setIsFullyOpen(false); // 닫힐 때 즉시 `opened` 클래스 제거
}
}, [isOpen]);
return (
<nav className={`navbar ${isOpen ? 'open' : 'closed'} ${isFullyOpen ? 'opened' : ''}`}>
<div className="navbar-header">
<GiHamburgerMenu className="hamburger-icon" onClick={toggleNavBar} />
</div>
<ul className="navbar-list">
<li className="navbar-item">
<Link to="/home" className="navbar-link">
<FaHome className="navbar-icon" />
<span className="navbar-text">홈</span>
</Link>
{/* 나머지 */}
</ul>
</nav>
);
}
export default NavBar;
NavBar.css
/* src/main/frontend/src/components/NavBar.css */
.navbar {
/* 나머지 */
transition: width 0.3s ease; /* 너비 변경 시 부드러운 애니메이션 적용 */
}
/* NavBar가 닫혔을 때의 스타일 설정 */
.navbar.closed {
width: 65px; /* 닫힌 상태에서의 NavBar 너비 */
}
/* NavBar 상단 헤더의 스타일 설정 */
.navbar-header {
/* 나머지 */
justify-content: flex-end; /* 요소를 오른쪽 끝에 정렬 */
width: 100%; /* 헤더의 너비를 NavBar 전체로 설정 */
transition: justify-content 0.3s; /* 정렬 방식 변경 시 부드러운 애니메이션 적용 */
}
/* NavBar가 닫혔을 때 헤더의 스타일 변경 */
.navbar.closed .navbar-header {
justify-content: center; /* 닫힌 상태에서는 헤더 요소를 중앙 정렬 */
}
/* 햄버거 아이콘의 스타일 설정 */
.hamburger-icon {
font-size: 24px; /* 아이콘 크기 */
cursor: pointer; /* 마우스 커서를 포인터로 변경 */
transition: font-size 0.3s, left 0.3s; /* 크기 및 위치 변경 시 부드러운 애니메이션 적용 */
position: relative; /* 상대 위치 설정 */
}
/* NavBar가 열렸을 때의 햄버거 아이콘 위치 조정 */
.navbar:not(.closed) .hamburger-icon {
left: -10px; /* 열렸을 때 왼쪽으로 이동 */
}
/* NavBar가 닫혔을 때의 햄버거 아이콘 위치 조정 */
.navbar.closed .hamburger-icon {
left: 0; /* 닫혔을 때 원래 위치로 돌아옴 */
}
/* NavBar 항목 스타일 설정 */
.navbar-item {
/* 나머지 */
transition: justify-content 0.3s ease; /* 정렬 방식 변경 시 부드러운 애니메이션 적용 */
}
/* NavBar가 닫혔을 때 항목의 정렬 방식 변경 */
.navbar.closed .navbar-item {
justify-content: center; /* 닫힌 상태에서는 항목을 중앙 정렬 */
}
/* NavBar 텍스트 스타일 설정 */
.navbar-text {
/* 나머지 */
transition: opacity 0.3s ease-in-out; /* 불투명도 변경 시 부드러운 애니메이션 적용 */
opacity: 0; /* 기본적으로 텍스트 숨김 */
visibility: hidden; /* 텍스트가 보이지 않도록 설정 */
}
/* NavBar가 완전히 열렸을 때 텍스트 표시 */
.navbar.opened .navbar-text {
opacity: 1; /* 텍스트를 완전히 표시 */
visibility: visible; /* 텍스트를 보이도록 설정 */
}
/* NavBar가 닫혔을 때 텍스트 숨김 */
.navbar.closed .navbar-text {
opacity: 0; /* 텍스트를 숨김 */
visibility: hidden; /* 텍스트가 보이지 않도록 설정 */
}
/* NavBar 아이콘 스타일 설정 */
.navbar-icon {
/* 나머지 */
transition: margin-right 0.3s ease; /* 여백 변경 시 부드러운 애니메이션 적용 */
flex-shrink: 0; /* 아이콘 크기 유지 */
}
/* NavBar가 닫혔을 때 아이콘의 여백 변경 */
.navbar.closed .navbar-icon {
margin-right: 0; /* 닫혔을 때 오른쪽 여백 제거 */
}
/* 나머지 */
css는 모든걸 적을 필요는 없을 것 같아서 수정사항과 관련된 부분만 남겼다. 어렵다.
App.js
const [isNavBarOpen, setIsNavBarOpen] = useState(true);
const toggleNavBar = () => {
setIsNavBarOpen(!isNavBarOpen);
};
return (
<div className="App">
<AuthProvider>
<BrowserRouter>
<NavBar isOpen={isNavBarOpen} toggleNavBar={toggleNavBar} />
<div className={`App-content ${isNavBarOpen ? 'navbar-open' : 'navbar-closed'}`}>
{/* 라우트 */}
</div>
</BrowserRouter>
</AuthProvider>
</div>
);
}
export default App;
App.js는 NavBar의 상태를 추가해 상태에 따라 달라지도록 하였다. 전에도 쓰긴 했지만 삼항연산자는 매우 편리한 것 같다. 근데 융프 때 귀찮아서 조건문 죄다 삼항연산자로 처리했던 생각이 나는데, 삼항연산자의 중첩으로 처리했던거 생각하면 미쳤던거 같다.
App.css
/* src/main/frontend/src/App.css */
.App {
display: flex;
}
.App-content {
flex-grow: 1;
padding: 20px;
transition: margin-left 0.3s;
}
.App-content.navbar-open {
margin-left: 250px;
}
.App-content.navbar-closed {
margin-left: 65px;
}
NavBar의 상태에 따라 간단하게 레이아웃이 변경된다.
참고자료 및 출처