2023.01.16~01.20 (본 진행 기간)
2023.01.25~02.07 (2차 수정 기간)
프론트엔드 4인
아래의 내용을 학습하기 위해 프로젝트를 진행했다.
나는 여기서 레이아웃과 헤더, 사이드바 구현을 맡았다.
다양한 분기처리가 필요했다.
넓은 사이드바
가 보이고, 메뉴를 누르면 작은 사이드바
가 보이지만작은 사이드바
가 보이고, 메뉴를 누르면 모달 사이드바
가 보인다.모달 사이드바
가 보인다.// App.js
import { Outlet } from 'react-router-dom';
import Layout from './components/Layout/Layout';
// 생략
function App() {
return (
<Layout>
<Outlet />
</Layout>
// 생략
// Layout.jsx
import { useLocation } from 'react-router-dom';
import Header from './Header/Header';
import Sidebar from './Sidebar/Sidebar';
export default function Layout({ children }) {
const [sidebar, setSidebar] = useState(false);
// 현재 영상 디테일 페이지인지?
const location = useLocation();
const findDetailPage = location.pathname.slice(0, 6) === '/watch';
return (
<>
<Header setSidebar={setSidebar} findDetailPage={findDetailPage} />
<div className={`${styles.layout} ${findDetailPage ? styles.detailPage : null}`}>
<div className={findDetailPage ? styles.sidebarNone : null}>
<Sidebar sidebar={sidebar} setSidebar={setSidebar} />
</div>
<main className={`${styles.outlet} ${sidebar ? styles.btnTrue : styles.btnFalse}`}>{children}</main>
</div>
</>
);
}
Header
와 Sidebar
에는 useLocation을 사용해 페이지별 설정을 달리해줬다.sidebar
라는 공통 state를Header
와 Sidebar
로 drilling 해줬다.// Header.jsx
//생략
import HeaderMenu from './HeaderMenu';
import SidebarModal from '../Sidebar/SidebarModal';
//생략
export default function Header({ setSidebar, findDetailPage }) {
const [modal, setModal] = useState(false);
// 생략
return (
// 생략
<HeaderMenu
setModal={setModal}
setSidebar={setSidebar}
menuBtn={findDetailPage ? 'openModal' : resize <= 1300 ? 'openModal' : 'openSidebar'}/>
<SidebarModal modal={modal} setModal={setModal} />
}
// 생략
// HeaderMenu.jsx
// 생략
export default function HeaderMenu({ setModal, setSidebar, menuBtn }) {
const btnClick = () => {
switch (menuBtn) {
case 'openModal':
// 태블릿과 모바일 사이즈, 메뉴 버튼으로 모달 사이드바 열기
(function openModal(e) {
setModal((e) => !e);
})();
break;
case 'openSidebar':
// 데스크탑 사이즈, 메뉴 버튼 클릭
(function openSidebar(e) {
setSidebar((e) => !e);
})();
break;
default:
break;
}
};
return (
<div className={styles.headerMenu}>
<BsList className={styles.headerIcon} size="24" onClick={btnClick} />
<Link to={'/'} className={styles.logo}>
<img src={logo} alt="youtube logo" />
<sup>KR</sup>
</Link>
</div>
);
}
// SidebarModal.jsx
// 생략
import SidebarLarge from './SidebarLarge';
import HeaderMenu from '../Header/HeaderMenu';
export default function SidebarModal({ modal, setModal }) {
const modalRef = useRef(null);
useOnClickOutside(modalRef, () => setModal(false));
return (
<nav className={modal ? `${styles.modalNav} ${styles.open}` : styles.modalNav} ref={modalRef}>
<HeaderMenu setModal={setModal} menuBtn={'openModal'} />
<SidebarLarge />
</nav>
);
Header
안에 HeaderMenu
에서 설정한 대로, SidebarModal
이 열리고 닫힌다. open 이라는 클래스를 state의 조건에 따라 설정해주어 구현했다.// Sidebar.jsx
import SidebarLarge from './SidebarLarge';
import SidebarSmall from './SidebarSmall';
export default function Sidebar({ sidebar }) {
const resize = useWindow();
return (
<>{resize >= 1300 ? sidebar ? <SidebarSmall /> : <SidebarLarge /> : resize >= 792 ? <SidebarSmall /> : null}</>
);
Sidebar
컴포넌트에서 관리했다.// SidebarLarge.jsx
// 생략
const iconList = {
list1: [
{ icon: <RiHome5Fill />, title: '홈' },
{ icon: <BsCollectionPlay />, title: '구독' },
{ icon: <MdOutlineVideoLibrary />, title: '보관함' },
],
list2: [
{ icon: <MdOutlineRestore />, title: '시청기록' },
{ icon: <AiOutlinePlaySquare />, title: '내 동영상' },
{ icon: <MdOutlineQueryBuilder />, title: '나중에 볼 동영상' },
{ icon: <MdKeyboardArrowDown />, title: '더보기' },
],
// 생략
};
const linkList = [
{ title: '정보' },
{ title: '보도자료' },
// 생략
];
export default function SidebarLarge() {
return (
<nav className={styles.largeNav}>
<ol>
{iconList.list1.map((list) => (
<button key={list.title} className={list.title === '홈' ? styles.homeIcon : null}>
<span className={styles.icon}>{list.icon}</span>
{list.title}
</button>
))}
</ol>
// 생략
두번째 협업 프로젝트라서 개발환경 세팅부터 배포까지 프론트엔드 팀원끼리 어떻게 손발을 맞춰야하는지 알고 있었기에, 이 전에 비해 많은 것들이 수월했다. 다같이 오프라인으로 작업한 것도 한 몫한 것 같다.
간단할 줄 알았던 사이드바와 헤더는 진행하다보니 분기처리를 많이 고려해야했고, 컴포넌트도 재사용 때문에 코드를 다 뒤집고 다시 작업했다. 보기보다 많이 어려웠던 구현이었다. 개발이 신기한게 어려워보이는 게 쉬울 때도 있고, 쉬워보이는게 어려울 때도 있다. 그래서 방심할 수 없다. 이번 프로젝트에서 했던 많은 삽질로 prop drilling 을 익숙하게 사용할 수 있게 됐다!