[Next.js] 탭바에 따른 조건부 라우팅

Shimmylog·2023년 7월 17일

ISSUES

목록 보기
1/6

손쉬운 페이지 라우팅은 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 공부를 급하게 하면서, 제일 중요한 상태관리에 대해 아직도 부족한 상황이라 프로젝트를 진행하면서 많은 걸 배우고 있다. 당장 위의 코드들도 완벽히 이해가 되지 않은 상태라, 더 차근차근 공부해 나가며 글을 수정해야겠다!

profile
그래요 나 지금 거렁뱅이에요

2개의 댓글

comment-user-thumbnail
2023년 7월 23일

트리피 화이팅입니다~~

1개의 답글