Next.jsReact반응형 디자인Tailwind CSS

모바일에서 메뉴가 잘린다 — 햄버거 메뉴로 해결하기

June 2, 20261 min read

모바일에서 메뉴가 밀려나고 있었다

블로그를 만들고 나서 한동안 데스크톱에서만 확인했다.

어느 날 폰으로 들어가봤더니 상단 네비게이션이 이렇게 생겼다.

[로고]  Home Posts Portfolio About [EN]

화면 너비가 좁아서 메뉴가 꽉 차 있고, 잘 보이지 않는 경우도 있었다. 스크롤을 해야 메뉴 전체가 보이는 상황.

이건 고쳐야 했다.


어떤 방식으로 해결할까

모바일 네비게이션 처리 방법은 크게 세 가지다.

방식설명단점
글자 크기 축소폰트를 줄여서 한 줄에 억지로 맞춤가독성 나빠짐
하단 탭바화면 아래에 아이콘 메뉴 배치구조 변경이 크고, 앱 느낌
햄버거 메뉴버튼 클릭 시 드롭다운 펼침클릭 한 번이 추가됨

블로그는 콘텐츠가 중심이고, 메뉴 항목이 4개라서 햄버거 메뉴가 제일 깔끔하다고 판단했다.

데스크톱은 기존 가로 메뉴 그대로 유지하고, 모바일(md 미만)에서만 햄버거로 전환하는 방식으로 구현했다.


구현

Step 1 — 아이콘 컴포넌트 추가

라이브러리 없이 SVG로 직접 만들었다. 용량이 가볍고, 스타일 커스터마이징도 자유롭다.

const HamburgerIcon = () => (
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
    stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <line x1="3" y1="6" x2="21" y2="6"/>
    <line x1="3" y1="12" x2="21" y2="12"/>
    <line x1="3" y1="18" x2="21" y2="18"/>
  </svg>
);

const CloseIcon = () => (
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
    stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
    <line x1="18" y1="6" x2="6" y2="18"/>
    <line x1="6" y1="6" x2="18" y2="18"/>
  </svg>
);

Step 2 — 메뉴 열림 상태 관리

const [menuOpen, setMenuOpen] = useState(false);

열렸는지 닫혔는지를 단순 boolean으로 관리한다.


Step 3 — 햄버거 버튼 추가

기존 헤더 안에, 데스크톱 nav 옆에 버튼을 붙인다.

md:hidden으로 모바일에서만 보이게 하고, 데스크톱 nav는 hidden md:flex로 모바일에서는 숨긴다.

{/* 데스크톱 nav — md 이상에서만 표시 */}
<nav className="hidden md:flex items-center">
  {/* ...링크들... */}
</nav>

{/* 햄버거 버튼 — md 미만에서만 표시 */}
<button
  className="flex md:hidden items-center justify-center rounded-lg transition-colors"
  style={{
    padding: "0.375rem",
    color: "hsl(var(--muted-foreground))",
    background: menuOpen ? "hsl(var(--primary) / 0.1)" : "transparent",
  }}
  onClick={() => setMenuOpen((v) => !v)}
  aria-label="메뉴 열기"
>
  {menuOpen ? <CloseIcon /> : <HamburgerIcon />}
</button>

버튼이 열려있을 때는 배경색을 살짝 주어서 상태를 시각적으로 표현했다.


Step 4 — 드롭다운 메뉴 패널

헤더 바로 아래에 조건부로 드롭다운을 렌더링한다.

{menuOpen && (
  <div
    className="md:hidden"
    style={{
      borderTop: "1px solid hsl(var(--border) / 0.4)",
      background: "hsl(var(--background) / 0.95)",
      backdropFilter: "blur(20px)",
    }}
  >
    <nav className="flex flex-col px-6 py-4" style={{ gap: "0.25rem" }}>
      {links.map(({ href, label }) => {
        const active = href === "/" ? cleanPath === "/" : cleanPath.startsWith(href);
        return (
          <Link
            key={href}
            href={href}
            onClick={() => setMenuOpen(false)} // 링크 클릭 시 메뉴 닫기
            className="flex items-center py-3 text-base font-medium transition-colors"
            style={{
              color: active ? "hsl(var(--primary))" : "hsl(var(--foreground))",
              borderBottom: "1px solid hsl(var(--border) / 0.3)",
              gap: "0.75rem",
            }}
          >
            <span style={{
              fontFamily: "var(--font-mono), monospace",
              fontSize: "0.8rem",
              color: active ? "hsl(var(--primary))" : "hsl(var(--muted-foreground))",
            }}>
              {active ? "▶" : "○"}
            </span>
            {label}
          </Link>
        );
      })}
    </nav>
  </div>
)}

몇 가지 챙긴 것들:

  • 링크 클릭 시 메뉴 닫기onClick={() => setMenuOpen(false)}. 안 넣으면 링크 이동 후에도 메뉴가 열려있다.
  • 현재 페이지 표시 / 로 활성 링크를 구분.
  • 배경 블러backdropFilter: blur(20px) 로 헤더와 동일한 유리 효과.
  • 구분선 — 각 항목 사이 borderBottom으로 구분.

Step 5 — 로고 클릭 시도 메뉴 닫기

메뉴가 열린 상태에서 로고를 클릭하면 홈으로 이동하면서 메뉴도 닫히도록 처리했다.

<Link href="/" onClick={() => setMenuOpen(false)}>
  {/* 로고 */}
</Link>

전후 비교

변경 전변경 후
데스크톱가로 메뉴가로 메뉴 (동일)
모바일메뉴 꽉 참, 스크롤 필요햄버거 버튼 → 드롭다운
현재 페이지 표시하단 밑줄▶ 아이콘 + 색상 강조

정리

모바일 네비게이션 흐름:

헤더
├── [데스크톱] hidden md:flex → 가로 메뉴 그대로
└── [모바일]  flex md:hidden → 햄버거 버튼

버튼 클릭
├── menuOpen: false → true → 드롭다운 렌더링
└── menuOpen: true  → false → 드롭다운 제거

드롭다운 링크 클릭 → setMenuOpen(false) → 자동으로 닫힘

라이브러리 없이 SVG 아이콘과 useState 하나로 구현했다. Tailwind의 반응형 prefix(md:hidden, hidden md:flex)를 활용하면 데스크톱과 모바일을 깔끔하게 분리할 수 있다.

PM

backtodev

40대 PM, 다시 개발자로 돌아갑니다. 실패하고 배우며 성장하는 기록.

모바일에서 메뉴가 잘린다 — 햄버거 메뉴로 해결하기 | backtodev