웹사이트에서 상단 메뉴를 구현할 때, PC에서는 hover로 메뉴가 열리고, 모바일에서는 클릭으로 메뉴가 열리는 방식을 자주 사용합니다.
Vue 3 + <script setup> + TailwindCSS 환경에서 반응형 디자인이 아닌 PC와 동일한 디자인을 적용할 때 겪은 시행착오와 해결 방법을 정리합니다.
문제 상황
- 모바일에서 동작을 위해 상위 메뉴 클릭시 모바일 여부와 메뉴 오픈 여부를 체크하여 하위메뉴를 보여주도록 적용한 뒤, PC에서는 hover로 하위 메뉴가 열리지만, 마우스를 아래로 내리는 순간 메뉴가 사라짐
- 모바일에서는 클릭으로 하위 메뉴를 열어야 하는데, viewport가 고정되어 있어 window.innerWidth로는 모바일인지 구분이 안 됨
- v-show로 메뉴를 열어도 class="hidden"이 같이 적용돼서 메뉴가 안 보임
문제 원인과 해결 방법
1. PC에서 hover 메뉴가 바로 닫히는 문제
원인
상단 메뉴에만 @mouseleave가 적용돼 있어, 사용자가 마우스를 아래로 이동하면 하위 메뉴를 클릭하기 전에 사라집니다.
해결
상단 메뉴와 하위 메뉴를 같은 div로 감싸고, 이 래퍼에 @mouseenter, @mouseleave 이벤트를 줘야 합니다.
<div
class="relative"
@mouseenter="isHoverMenu = true"
@mouseleave="isHoverMenu = false"
>
<!-- 상단 메뉴 -->
<div class="flex"> ... </div>
<!-- 하위 메뉴 -->
<div v-show="isHoverMenu" class="absolute top-full"> ... </div>
</div>
2. 모바일 디바이스 구분이 안 되는 문제
원인
<meta name="viewport" content="width=1024"> 설정 때문에 window.innerWidth가 항상 1024로 고정됨
해결
navigator.userAgent를 기반으로 디바이스가 모바일인지 판단해야 함
function isMobileDevice(): boolean {
return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
}
onMounted(() => {
isMobile.value = isMobileDevice();
});
3. v-show로 메뉴를 열어도 안 보이는 문제
원인
v-show는 내부적으로 display: none을 조절하는데, Tailwind의 class="hidden"이 함께 있으면 무조건 숨겨짐
해결
v-show 대신 조건부 :class로 hidden/flex를 제어해야 함
<div :class="[ isOpen ? 'flex' : 'hidden', 'absolute top-full ...' ]">
모바일 클릭, PC hover 둘 다 지원하는 조건
하위 메뉴는 다음 조건으로 보여줄 수 있습니다:
:class="[
'absolute top-full ...',
(!isMobile && isHoverMenu) || (isMobile && isMenuOpen) ? 'flex' : 'hidden'
]"
✨ 마무리
Vue + Tailwind로 반응형 메뉴를 만들 때는 PC와 모바일의 동작 방식이 완전히 다르기 때문에 분리해서 처리해야 안정적인 UX를 구현할 수 있습니다.
- 디바이스 구분은 userAgent 기반으로
- hover 이벤트는 상단/하위 메뉴 전체 래퍼에서 처리
- Tailwind의 hidden과 v-show는 동시에 쓰지 않기
구현이 복잡하게 느껴진다면 상단 메뉴와 하위 메뉴를 컴포넌트로 분리해서 각 환경에 맞게 관리하는 것도 좋은 방법입니다.