728x90
1. use On Click Outside
- “바깥 클릭 시 닫기” 로직에 대한 커스텀 훅을 따로 만들어줍니다.
- ref 영역 밖을 누르면 handler를 호출하는 로직입니다.
- 이렇게 개별적으로 분리해놓으면 여러 컴포넌트에서 사용할 수 있습니다.
import { useEffect } from "react";
/** ref 영역 밖을 클릭/터치하면 handler 실행 */
export default function useOnClickOutside(ref, handler) {
useEffect(() => {
const onPointerDown = (e) => {
const el = ref?.current;
if (!el || el.contains(e.target)) return;
handler(e);
};
document.addEventListener("pointerdown", onPointerDown, { passive: true });
return () => document.removeEventListener("pointerdown", onPointerDown);
}, [ref, handler]);
}
- 컴포넌트에서 ref(DOM 요소)와 handler(닫기 함수 등)를 넘기면, 지정한 요소 밖에서 클릭/터치가 일어났을 때 handler가 실행되도록 이벤트를 걸어줍니다.
2. Nav Drop down
import { useRef, useState, useEffect } from "react"
import { Link as ScrollLink } from "react-scroll"
import useOnClickOutside from "../hooks/useOnClickOutside"
export default function NavDropdown({ label, items = [] }) {
const [open, setOpen] = useState(false)
const ref = useRef(null)
useOnClickOutside(ref, () => setOpen(false))
// ESC로 닫기 (선택)
useEffect(() => {
const onKey = (e) => e.key === "Escape" && setOpen(false)
window.addEventListener("keydown", onKey)
return () => window.removeEventListener("keydown", onKey)
}, [])
return (
<div ref={ref} className="relative inline-block">
<button
onClick={() => setOpen(v => !v)}
aria-haspopup="menu"
aria-expanded={open}
className="cursor-pointer hover:text-indigo-500 transition-colors"
>
{label}
</button>
{open && (
<div
role="menu"
className="absolute top-full left-0 mt-2 bg-white dark:bg-neutral-900
shadow-lg border border-gray-200 dark:border-neutral-800
rounded-lg py-2 min-w-40 z-50"
>
{items.map((item, i) => (
<ScrollLink
key={i}
to={item.to}
spy={true}
smooth={true}
offset={-80}
duration={500}
onClick={() => setOpen(false)} // 항목 클릭 시 닫힘
className="block px-4 py-2 text-gray-700 dark:text-gray-200
hover:bg-indigo-50 dark:hover:bg-neutral-800
hover:text-indigo-600 transition-colors text-sm cursor-pointer"
>
{item.label}
</ScrollLink>
))}
</div>
)}
</div>
)
}
- 네비게이션용 드롭다운 메뉴 컴포넌트
- 버튼 클릭으로 open 상태 토글 → 열리면 메뉴가 렌더링 됩니다.
- useOnClickOutside → 바깥 클릭 시 자동으로 닫힙니다.
- useEffect로 ESC 키 누르면 닫힙니다.
3. Header. jsx
import { Link as ScrollLink } from "react-scroll";
import DarkModeToggle from "./DarkModeToggle";
import NavDropdown from "./NavDropDown"; // ✅ 새 드롭다운 컴포넌트
function Header() {
const dropdownItems = {
about: [
{ label: "개인정보", to: "about" },
{ label: "경력사항", to: "about" },
{ label: "학력사항", to: "about" },
{ label: "기술스택", to: "about" },
],
projects: [
{ label: "웹 개발", to: "projects" },
{ label: "모바일 앱", to: "projects" },
{ label: "데이터 분석", to: "projects" },
{ label: "AI/ML", to: "projects" },
{ label: "블록체인", to: "projects" },
],
contact: [
{ label: "이메일", to: "contact" },
{ label: "전화번호", to: "contact" },
{ label: "소셜미디어", to: "contact" },
{ label: "위치", to: "contact" },
],
};
return (
<header className="fixed top-0 w-full bg-white dark:bg-neutral-900 shadow-md z-50">
<div className="max-w-5xl mx-auto px-6 py-4 flex justify-between items-center">
<h1 className="text-xl font-bold text-indigo-600 dark:text-indigo-400">
OOO.dev
</h1>
<nav className="flex items-center space-x-6 text-gray-700 dark:text-gray-200 font-medium">
{/* Home: 단일 링크 그대로 유지 */}
<ScrollLink
to="home"
spy={true}
smooth={true}
offset={-80}
duration={500}
className="cursor-pointer hover:text-indigo-500 transition-colors"
>
Home
</ScrollLink>
{/* ✅ 클릭 열림 + 바깥 클릭/ESC 닫힘 */}
<NavDropdown label="About" items={dropdownItems.about} />
<NavDropdown label="Projects" items={dropdownItems.projects} />
<NavDropdown label="Contact" items={dropdownItems.contact} />
{/* 다크모드 토글은 맨 우측 고정 */}
<DarkModeToggle />
</nav>
</div>
</header>
);
}
export default Header;
- 기존 헤더에 NavDropdown 를 추가해줍니다.
시연 영상입니다.
'React' 카테고리의 다른 글
[React] Tailwind v4로 웹페이지 다크모드 설정하기 (클래스 기반) (0) | 2025.09.02 |
---|---|
[React] navigator.share + clipboard로 공유 버튼 간단 구현하기 (0) | 2025.08.25 |
[React] LottieFiles 애니메이션 적용하기 (0) | 2025.08.21 |
[React] 3D 아바타 컴포넌트 만들기 (0) | 2025.08.11 |
[React] 웹페이지 메뉴바 드롭다운 만들기 (0) | 2025.08.06 |