카테고리 없음

[Vue] Vue.js 실습(2) : 반응형 햄버거 메뉴와 카드식 테이블 구현

subindev 2026. 5. 28. 14:56

Vue 3 독학 실습: 반응형 네비게이션 및 반응형 데이터 테이블 구축

 

1. 반응형 모바일 햄버거 토글 메뉴 (src/components/Header.vue)

화면 크기가 1024px 이하(모바일/태블릿)로 줄어들었을 때, 기존 PC용 가로 나열 메뉴를 숨기고 햄버거 버튼 클릭 시 전체 화면을 덮는 슬라이드 오버레이 메뉴를 구현함.

<template>
  <header class="site-header">
    <div class="header-container">
      <div class="logo-area"><router-link to="/">MYAPP</router-link></div>

      <nav class="main-navigation">...</nav>

      <button class="hamburger-btn" :class="{ 'is-active': isMenuOpen }" @click="toggleMenu">
        <span class="line"></span><span class="line"></span><span class="line"></span>
      </button>

      <div class="mobile-menu-overlay" :class="{ 'is-open': isMenuOpen }">
        <nav class="mobile-navigation">
          <ul class="mobile-menu-list">
            <li><router-link to="/products" @click="closeMenu">PRODUCTS</router-link></li>
            </ul>
        </nav>
      </div>
    </div>
  </header>
</template>

<script setup>
import { ref } from 'vue'
const isMenuOpen = ref(false)
const toggleMenu = () => isMenuOpen.value = !isMenuOpen.value
const closeMenu = () => isMenuOpen.value = false
</script>

핵심 제어 로직 및 스타일

  • ref(false) 상태 관리: 모바일 메뉴의 열림/닫힘 상태를 불리언(true/false) 데이터로 선언하고, 버튼 클릭 시 이 값을 반전시켜 UI를 동적으로 제어함.
  • 클래스 바인딩 (:class): isMenuOpen이 true가 되면 버튼에 .is-active, 오버레이에 .is-open 클래스가 주입되어 CSS 트랜지션 애니메이션이 촉발됨.
  • CSS 삼선 변형 연출: CSS transform 속성을 활용해 클래스 활성화 시 햄버거의 가운데 선은 사라지고(opacity: 0), 위아래 선이 회전하면서 'X' 모양으로 교차하는 마이크로 인터랙션을 구현함.

 

2. 가로 해체형 모바일 카드 테이블 (src/views/HomeView.vue)

데이터량이 많은 정밀 표(Table) 구조를 모바일 스크린(768px 이하) 환경에 맞춰 과감히 해체하고, 위에서 아래로 스크롤하며 읽을 수 있는 '카드형(Card) 레이아웃'으로 변환 설계함.

<table class="modern-table">
  <thead class="pc-thead">
    <tr><th>분류</th><th>제품명</th><th>주요 특징 및 기술</th></tr>
  </thead>
  <tbody>
    <tr v-for="product in products" :key="product.id">
      <td class="cell-category" data-label="분류">
        <span class="badge-category">{{ product.category }}</span>
      </td>
      <td class="cell-name" data-label="제품명">{{ product.name }}</td>
      <td class="cell-feature" data-label="주요 특징 및 기술">{{ product.feature }}</td>
    </tr>
  </tbody>
</table>

핵심 반응형 미디어 쿼리 스타일 (@media (max-width: 768px))

  • display: block; 구조 변환: 가로 배열 특성을 지닌 기존 table, tbody, tr 요소의 고유 속성을 깨뜨리고 일반 블록 오브젝트로 전환하여 개별 행(tr)을 하나의 둥근 카드로 격상시킴.
  • data-label과 가짜 헤더: 각 td 태그에 심어둔 data-label 값을 CSS 가상 요소인 content: attr(data-label); 구조로 불러와 모바일 화면 왼쪽에 라벨 텍스트를 동적으로 자동 생성함.

 

3. 레이아웃 트러블슈팅 및 해결 방안

 

① 모바일 메뉴 진입 후 페이지 이동 시 메뉴 잔상 현상

  • 현상: 모바일 메뉴바 안에서 특정 서브 메뉴(예: PRODUCTS)를 터치하여 페이지가 성공적으로 라우팅 되었음에도 불구하고, 전체 화면을 덮고 있던 모바일 오버레이 메뉴창이 자동으로 닫히지 않고 화면을 계속 가로막고 있는 상태가 유지됨.
  • 해결: 모바일 내부의 각 <router-link> 태그에 클릭 이벤트 리스너 @click="closeMenu" 핸들러를 바인딩함. 링크를 누르는 순간 라우터의 이동 기능과 동시에 내부 반응형 데이터 변수 isMenuOpen.value를 false로 강제 리셋 시켜 메뉴 창이 인지하지 못할 속도로 깔끔하게 접히도록 유도함.

② 텍스트 밀림으로 인한 우측 정렬 찌그러짐 현상

  • 현상: 모바일 카드 뷰에서 모든 항목을 좌(라벨)·우(데이터) 정렬(justify-content: space-between) 구조로 통일하자, 문자열 문장이 매우 긴 '주요 특징 및 기술' 데이터 부분이 오른쪽 좁은 영역에 극도로 압축되면서 세로 가로폭이 기괴하게 길어지고 가독성이 완전히 파괴됨.
  • 해결: .cell-feature 클래스 한정으로 모바일 미디어 쿼리 내부에서 흐름축 방향을 세로로 전환하는 flex-direction: column;을 선언함. 정렬 기준을 왼쪽 정렬(align-items: flex-start; text-align: left;)로 초기화하고 넓이 폭을 width: 100% !important;로 가득 채워 라벨 밑 공간 전체를 온전하게 활용하는 가독성 높은 텍스트 블록 형태로 구성을 개정함.

핵심 Vue 3 및 CSS 기술 요약

기술 명칭 유형 기능 설명

attr(data-*) CSS HTML 마크업에 기록된 사용자 정의 속성 값을 CSS 가상 요소(::before) 내부 문장으로 파싱해 표현하는 기능
flex-direction CSS Flex 컨테이너 안의 자식 자재들을 가로축(row) 혹은 세로축(column) 방향으로 배치 흐름을 결정하는 가이드 라인
v-for 바인딩 Vue 3 배열 객체 내부의 고유 데이터를 순회하며 key 속성을 기반으로 동일 구조의 반응형 DOM 카드를 반복 생성하는 제어 문법
동적 클래스 (:class) Vue 3 스크립트 단의 데이터 변수 스태이트 상태 값에 연동되어 클래스명을 실시간으로 탈부착시키는 동적 제어 트리거

 

 

 


실습 화면 스크린샷