손쉬운 페이지 라우팅은 NEXT.JS의 큰 장점 중 하나이다.
단지 폴더 하나를 생성하고, page.tsx와 같은 파일을 만든 다음 그 파일 내에 내가 만든 컴포넌트들을 때려박으면 끝이다.
그러나 다른 시각에서 보면 페이지를 만들 때 폴더 하나를 만들어야 하는 꼴이 되는데, 뭐 사람마다 다르겠지만 웹 공부와 프로젝트를 갓 시작한 내가 보더라도 페이지가 많아지면 안될 것 같은 느낌이 든다.

위와 같이 탭바에서 어떤 걸 누르냐에 따라 다른 페이지를 출력하려 할 때, 나 뿐만 아니라 팀원들의 의견으로도 각기 다른 페이지를 만드는 것은 꽤나 비효율적이라고 보았다.
따라서 상태 관리를 이용하여 탭바의 항목을 누르면 각 항목에 해당하는 컴포넌트를 불러오는 조건부 라우팅을 시도해 보았다.
기본적으로 마이페이지의 화면을 보여주는 mypage/page.tsx 가 부모 컴포넌트로 자리잡고 있고,
그 아래에는 다음과 같이 자식 컴포넌트들을 때려박았다.
export default function mypage () {
return (
<div>
<MypageMenus />
<ProfilePic />
<MyInfo />
<ConfirmBtns />
<MyFriends />
<FriendReq />
</div>
}
MypageMenus 는 탭바 컴포넌트로 위에 첨부한 이미지와 같고,
ProfilePic, MyInfo, ConfirmBtns 컴포넌트는 정보 화면에 표시되어야 할 컴포넌트,
MyFriends, FriendReq 는 친구관리 화면에 표시되어야 할 컴포넌트이다.
지금은 모두 때려박았으니 탭바, 정보, 친구관리가 한 화면에 모두 표시될 것이다.
그 중 MypageMenus 에서 이미 탭바의 클릭 효과는 팀원의 코드의 도움을 받아 만들어 놓은 상태인데, 여기서 다음과 같은 배열을 작성했었다.
const [menus, setMenus] = useState<[string, boolean][]>([
['정보', true],
['여행일정', false],
['내가방', false],
['저장한 정보', false],
['친구관리', false],
]);
아래쪽 코드를 대충 설명하자면, 선택된 메뉴를 true 상태로 하여 하이라이팅만 주는 기능이다.
사실 이 코드는 아직 나 자신도 잘 숙지가 되지 않았기에... 설명하기는 어렵다. 조금만 더 공부하면 될듯 😓
따라서, 이를 조금 다듬어서 어떤 메뉴가 true일 때 그 메뉴에 해당하는 컴포넌트만 조건부로 불러오면 되지 않을까? 라는 생각에서 시작했다.
우선 page.tsx 에 요런 조건문을 추가했다.
export default function mypage () {
const [activeMenu, setActiveMenu] = useState('정보');
const handleMenuClick = (menu: string) => {
setActiveMenu(menu);
};
let content;
if(activeMenu === '정보') {
content = (
<>
<ProfilePic />
<MyInfo />
<ConfirmBtns />
</>
);
} else if (activeMenu === '여행일정') {
content = (
<div>여행일정 페이지</div>
)
} else if (activeMenu === '내가방') {
content = (
<div>내가방 페이지</div>
)
} else if (activeMenu === '저장한 정보') {
content = (
<div>저장한 정보 페이지</div>
)
} else if (activeMenu === '친구관리') {
content = (
<>
<MyFriends />
<FriendReq />
</>
);
}
return (
<div>
<MypageMenus activeMenu={activeMenu} onMenuClick={handleMenuClick}/>
{content}
</div>
)
}
activeMenu가 무엇인지에 따라, 서로 다른 컴포넌트를 불러오도록 했다.
content 변수에는 불러올 컴포넌트를 저장했고, 이후 탭바와 content를 반환하였다.
이 때 자식 컴포넌트인 MypageMenus 에 우리가 선택한 activeMenu와 handleMenuClick 값을 전해줘야지 뭐 작동이라도 될텐데, 요 부분에서 애를 먹었다.
우선 전달은 받아야 하니까 다음과 같이 인터페이스를 정의하고, 넣어주었다.
interface MypageMenusProps {
activeMenu: string;
onMenuClick: (menu: string) => void;
}
export default function MypageMenus ({ activeMenu, onMenuClick }: MypageMenusProps) {
const [menus, setMenus] = useState<[string, boolean][]>([
['정보', true],
['여행일정', false],
['내가방', false],
['저장한 정보', false],
['친구관리', false],
]);
const Menu = ({ menu, select, index, onClick }: MenuProps) => {
const handleMenuClick = () => {
onClick(index);
};
return (
<div className='relative flex flex-col items-center'>
{select && (
<img
className='스타일'
src='이미지 파일'
alt='none'
/>
)}
<span
className={
select
? '선택된 것의 스타일'
: '선택되지 않은 것의 스타일'
}
onClick={handleMenuClick}
>
{menu}
</span>
</div>
);
};
<!-- 아래에서 계속! -->
위의 menu 컴포넌트에 onClick={handleMenuClick} 을 넣어준 뒤,
부모 컴포넌트에서 받아온 activeMenu와 onMenuClick을 처리하기 위해 아래와 같이 작성했다.
const menuClick = (selectedIndex: number) => {
const selectedMenu = menus[selectedIndex][0];
onMenuClick(selectedMenu);
};
return (
<div>
<div className="text-5xl font-bold mx-4 mt-20 mb-12">
마이페이지
</div>
<div className="flex justify-between">
<nav className="flex items-end">
{menus.map((menu, index) => (
<Menu
key={`menu${index}`}
menu={menu[0]}
select={menu[0] === activeMenu}
index={index}
onClick={menuClick}
/>
))}
</nav>
</div>
</div>
)
}
리액트와 JS 공부를 급하게 하면서, 제일 중요한 상태관리에 대해 아직도 부족한 상황이라 프로젝트를 진행하면서 많은 걸 배우고 있다. 당장 위의 코드들도 완벽히 이해가 되지 않은 상태라, 더 차근차근 공부해 나가며 글을 수정해야겠다!
트리피 화이팅입니다~~