이제 하위 컴포넌트들을 관리하는 최상위 컴포넌트 Nav 를 정리해보자.
처음 Nav 를 설계할 때, 생각 이상으로 상위에서 props 로 전달해주어야 할 변수 및 함수가 많을 것이라 예상되었다. 그도 그럴 것이 실시간으로 조건에 맞는 Nav 를 랜더링 해야하며, 그 각각의 Nav에 대해 따른 모달 컴포넌트도 정상적으로 랜더링 되어야 하기 때문이며 또한 사용자가 편리하게 사용할 수 있도록 신경써야 하였기에 Nav 가 유기적으로 움직이기 위해 좀 더 체계적으로 설계했어야 했다.
이로 인해 Nav 의 코드는 직관적이지 않게 코드가 짜여졌다.
import React, { useState, useRef, useEffect } from 'react';
import LoginModal from '../Modal/LoginModal';
import SignModal from '../Modal/SignModal';
import BeforeSearch from './BeforeSearch';
import OnClickSearch from './OnClickSearch';
const Nav = () => {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [location, setLocation] = useState('지도표시지역');
const [guest, setGuest] = useState(0);
const [toggleNavbar, setToggleNavbar] = useState(true);
const [profileModal, setProfileModal] = useState(false);
const [modalIsOpen, setModalIsOpen] = useState(false);
const [signupIsOpen, setSignupIsOpen] = useState(true);
const [isToken, setIsToken] = useState(false);
const modalRef = useRef();
useEffect(() => {
if (localStorage.getItem('Token')) {
setIsToken(true);
} else {
setIsToken(false);
}
}, [isToken]);
const reroad = () => {
window.location.replace('/');
};
const onChange = dates => {
const [start, end] = dates;
setStartDate(start);
setEndDate(end);
};
const increseNum = () => {
setGuest(prev => prev + 1);
};
const decreseNum = () => {
setGuest(prev => prev - 1);
};
const toggleNav = () => {
setToggleNavbar(false);
};
const clickUserInfo = () => {
setProfileModal(prev => !prev);
};
const switchModal = () => {
setProfileModal(prev => !prev);
setModalIsOpen(prev => !prev);
};
return (
<>
<div>
<BeforeSearch
toggleNav={toggleNav}
toggleNavbar={toggleNavbar}
startDate={startDate}
endDate={endDate}
location={location}
guest={guest}
setLocation={setLocation}
onChange={onChange}
increseNum={increseNum}
decreseNum={decreseNum}
profileModal={profileModal}
setProfileModal={setProfileModal}
clickUserInfo={clickUserInfo}
isToken={isToken}
setIsToken={setIsToken}
modalIsOpen={modalIsOpen}
setModalIsOpen={setModalIsOpen}
switchModal={switchModal}
reroad={reroad}
/>
<OnClickSearch
toggleNav={toggleNav}
toggleNavbar={toggleNavbar}
startDate={startDate}
endDate={endDate}
location={location}
guest={guest}
setLocation={setLocation}
onChange={onChange}
increseNum={increseNum}
decreseNum={decreseNum}
modalRef={modalRef}
setToggleNavbar={setToggleNavbar}
profileModal={profileModal}
setProfileModal={setProfileModal}
clickUserInfo={clickUserInfo}
isToken={isToken}
setIsToken={setIsToken}
modalIsOpen={modalIsOpen}
setModalIsOpen={setModalIsOpen}
switchModal={switchModal}
reroad={reroad}
/>
</div>
{localStorage.getItem('key') ? (
<SignModal modalIsOpen={modalIsOpen} setModalIsOpen={setModalIsOpen} />
) : (
<LoginModal modalIsOpen={modalIsOpen} setModalIsOpen={setModalIsOpen} />
)}
</>
);
};
export default Nav;
무수한 props 들이 하위 컴포넌트로 전달이 되며 (추후 리펙토링이 필요해보인다..) 라이브러리에 사용되는 함수 역시 하위컴포넌트로 전달해주어야 했다. 솔직히 지금봐도 무엇이 무엇을 의미하는지 햇갈릴 정도니깐..
다만 구조 자체는 심플!
위 사진과 같이 Nav 컴포넌트는 두개의 컴포넌트 BeforeSearch(검색창을 누르지 않을때), OnClickSearch(눌렀을 때) 를 감싸면서 조건에 따라 두 컴포넌트의 opacity 를 조정해준다 (이건 앞서 설명하였음)
SignModal, LoginModal 역시 로그인 여부에 따라서 조건이 달려있는 모달창이며 (역시 앞에서 설명), modalIsOpen state 상태에 따라 화면에 랜더링 될지 아닐지가 결정된다. Nav 에는 연동되어있지만 엄밀히 말하면 두 모달창은 Nav 와는 별개로 독립적이기에 Nav 컴포넌트에 하지 않았어도 됬지 않을까 하는 생각도 해봤었는데, Nav 내 state 와 연동이 되기 때문에 가장 상위인 Nav 컴포넌트에 연동 시켰다.
실제 실무를 경험해보진 못해서 얼마나 많이 props가 전달되는지는 모르겠지만, 당시 작업할 때 느껴진 점은 그저 머리속에 어떻게해서든 props 전달을 최소화 시키고 싶었던것 뿐이었다. (사실 돌아가는게 우선이라 좀 깊이 고려 안하고 코드를 작성하기도 하였다.)
구조는 간단한데 뭔가 복잡해보이는게 별로 마음에 안들었다..
최대한 함수는 위쪽으로
예전에 가장 애를 먹게 했다는 Date picker 사용 때 사용되는 함수가 있는데, 처음 작성 시에는 말그대로 캘린더 컴포넌트부터 시작하였다.
import React, { useState, useRef, useEffect } from 'react';
// 생략
const Nav = () => {
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
//생략
const onChange = dates => {
const [start, end] = dates;
setStartDate(start);
setEndDate(end);
};
//생략
export default Nav;
하지만 결국 Nav 라는 컴포넌트까지 올라오게 되었는데, 그럴수 밖에 없기도 하였고 실제로도 상위 컴포넌트에 위치할 수록 추후 캘린더 적용시 상태관리가 용이하였다.
그럴수 밖에 없던 이유라면, 위 사진처럼 Nav 로 부터 2개의 검색창 클릭전, 검색창 클릭 후 Nav 컴포넌트로 나뉘어 지는데, 달력을 사용하는 Nav는 검색창을 클릭 한 후 Search 컴포넌트 안 캘린더에서 사용이 된다. 다만, 시작날과 끝날을 결정하고 검색 시 다시 검색창 전 Nav 로 변경이 되는데 이 때 선택한 날짜 데이터가 같이 표시가 되게 된다.
그렇기에 이 날짜 데이터를 사실상 Nav 에서 전달해주어야 하고, useState 가 Nav 에 위치하니 함수 역시 Nav에 위치 시킬수밖에 없었다.
위는 하나의 예시이며, 이렇게 컴포넌트 간 유기적으로 데이터가 전달되어야 하는 경우, 트리구조로서 가장 상위의 컴포넌트에서 아래로 뿌려주는 방식밖에는 없었다. 하위 컴포넌트에서 상위 컴포넌트로는 전달하지 못하기 때문이니깐.
간단한 프로젝트도 이런데 현업에서는 얼마나 복잡할지......
리펙토링
좀 더 구현해보고 싶은 것이 많은 사이트 프로젝트로서, 만일 Nav 에 기존 에어비엔비 처럼 좀 더 많은 기능들 (유연한 날짜, 더 많은 지역, 인원 필터, 검색 등등) 이 추가가 된다면 더 많은 props 전달이 필요해질 것이고, 가독성에서도 관리측면에서도 좋아보이지 않기에 기능 추가 이전에 먼저 코드를 정리해야 겠다는 생각이 든다.
다만 리펙토링이라는 것을 해본 경험이 많진 않아서 어떤식으로 구조를 작성하고, 어떠한 기술을 사용하는것이 가장 적합할지 감이 오질 않는다. 그저 머리속에는 예전에 잠깐 댓글생성할 때 사용해본 useReducer 가 떠올라, 더 응용하여 사용되는 Redux 를 학습하여 적용시켜 dispatch 로 store 에 접근하는 방식으로 바꾼다면, 좀 더 유지보수가 좋은 코드가 작성될것 같은 감(?) 이 온다. 빨리 학습해야지...
시간은 오래걸릴 것 같다만 ^^
'Programing > React' 카테고리의 다른 글
Next.js 튜토리얼(1) (설치~CSS) (2) | 2022.09.29 |
---|---|
SSR(Server Side Rendering) & CSR (2) | 2022.09.25 |
2차 프로젝트 - 에어비엔비 2개의 Nav로 구성하기(6) (0) | 2022.09.14 |
2차 프로젝트 - 에어비엔비 ProfileContainer(5) (0) | 2022.09.04 |
2차 프로젝트 - 에어비엔비 우측 상단 프로필 버튼(4) (0) | 2022.09.03 |