React Navbar 만들기

Jintae Kim·2025년 7월 9일

Nextjs

목록 보기
5/5
post-thumbnail

2뎁스 이상의 메뉴구조와 이벤트 핸들러

메뉴구조 샘플

menuData = [
    {
        id: "1",
        useYn: "Y",
        label: "menu 1",
        url: "#",
        children: [
            {
                id: "1-1",
                useYn: "Y",
                label: "menu 1-1",
                url: "/url",
                children: [],
            },
            {
                id: "1-2",
                useYn: "Y",
                label: "menu 1-2",
                url: "/url",
                children: [],
            },
        ]
    },
    {
        id: "2",
        useYn: "Y",
        label: "menu 2",
        url: "#",
        children: [
            {
                id: "2-1",
                useYn: "Y",
                label: "menu 2-1",
                url: "/url",
                children: [],
            },
        ],
    }
]
  • useYn 값 으로 메뉴의 사용여부를 특정함.
  • children 안에 하위뎁스의 메뉴구조가 들어감.

메뉴 이벤트 핸들러와 메뉴 DOM 렌더링

  • openMenuId : 메뉴 item 을 특정하고 현재 메뉴와 메뉴상태를 특정하기 위한 state
  • navRef : 메뉴 DOM 을 특정, 메뉴 DOM 외부의 이벤트를 감지하기 위함.
  • handleMenuClick : click 이벤트로 openMenuId 값을 변화 시킴(토글 이벤트)
const [openMenuId, setOpenMenuId] = useState<string | null>(null);
const navRef = useRef<HTMLUListElement>(null);

useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
        // 메뉴 DOM(navRef) 가 있고, 메뉴 DOM 의 내부가 아니면 openMenuId 를 null 로 초기화
        if (navRef.current && !navRef.current.contains(event.target as Node)) {
            setOpenMenuId(null);
        }
    }
    
    document.addEventListener("mousedown", handleClickOutside); // mousedown 이벤트 등록
    // 이벤트 중복 등록 방지 클린업 함수
    return () => {
        document.removeEventListener("mousedown", handleClickOutside);
    }
}, []);

// id toggle 이벤트핸들러
const handleMenuClick = (id: string) => (event: React.MouseEvent<HTMLElement>) => {
    event.preventDefault();
    setOpenMenuId((prev) => prev === id ? null : id);
}

return (
    <ul ref={navRef}>
        {menuData
            .filter((item) => item.useYn === "Y")
            .map((item) => (
                <li key={item.id}>
                    <button onClick={handleMenuClick(item.id)}>{item.label}</button>
                    <div style={{ display: openMenuId === item.id ? "block" : "none" }}>
                        {item.children && (
                            <ul>
                                {item.children
                                    ?.filter((child) => child.useYn === "Y")
                                    .map((child) => (
                                        <li key={child.key}>
                                            <a href={child.url}>{child.label}</a>
                                        </li>
                                    ))
                                }
                            </ul>
                        )}
                    </div>
                </li>
            ))
        }
    </ul>
);

결과

  • 1 depth 메뉴 클릭 시 2 depth 메뉴 DOM 이 보여짐
  • 메뉴 DOM 의 바깥을 클릭 시 메뉴상태가 초기화 됨.
profile
공부하고 또 공부하고 또 공부하고

0개의 댓글