Responsive

[Responsive] 반응형 header / header 작업 시 유의사항 및 기본 구조

ui-o 2025. 7. 29. 14:52
반응형

반응형 헤더 작업 시 유의사항

1. 반응형 레이아웃 설계

📌 브레이크포인트 정의

  • 중요 이유: 브레이크포인트가 일관되지 않으면 예기치 않은 레이아웃 깨짐이 발생.
  • : SCSS 사용하는 경우 @mixin media($device) 식으로 공통 처리하면 유지보수 용이

📌 디자인 일관성 유지

  • 각 해상도에서 메뉴 순서, 로고 위치, 버튼 형태가 다르면 사용자 혼란.
     → 예: PC에선 오른쪽에 있는 메뉴가 모바일에서 맨 아래로 가면 UX가 불편해짐

2.내비게이션 메뉴 처리 

📌 메뉴 개수에 따른 처리

  • 메뉴가 많으면 PC에서는 가로로 충분하지만, 모바일에서는 한 줄에 다 안 들어감.
    가로 스크롤, 아코디언, "더보기" 처리 등으로 설계 필요.

📌 서브 메뉴가 있는 경우

  • PC: hover로 보여주는 드롭다운
  • 모바일: 클릭/터치 시 열리는 아코디언 구조 필요
  • 주의: hover 기반 구조는 터치 디바이스에서 작동하지 않음 → JS로 toggle 필요

3. 접근성

📌 aria-label, aria-expanded 활용

  • aria-label="메뉴 열기": 스크린리더가 버튼의 기능을 설명하는 역할로 토글에 따라 다른 텍스트 제어가 필요하지 않음
  • aria-expanded="true/false": 현재 상태 전달하므로, 토글에 따라 js로 true/false 변경 필요
<button class="menu-toggle" aria-label="메뉴 열기" aria-expanded="false">☰</button>

 

  • 스크린리더 사용자(예: VoiceOver, NVDA)는 버튼에 접근했을 때 아래와 같이 들을 수 있음:
    • 처음 접근 시: "메뉴 열기, 버튼, 펼쳐지지 않음" (aria-label="메뉴 열기" + aria-expanded="false")
    • 버튼을 클릭해서 메뉴가 열리면: "메뉴 열기, 버튼, 펼쳐짐"(aria-expanded="true"로 변경되었기 때문)
기능 접근성 효과
시각적 아이콘 (☰) 의미 전달이 어려움 → aria-label로 보완
상태 정보 aria-expanded로 현재 메뉴 상태 전달
동작 키보드, 스크린리더 사용자가 메뉴를 조작할 수 있음
역할 구분 <button> 태그로 명확한 역할 전달 (링크와 구분)

📌 시맨틱 태그 사용

  • <nav>: 내비게이션 영역임을 명시
  • <button>: 햄버거 메뉴 클릭 요소는 <a> 대신 <button>을 반드시 사용해야 함
  • <ul><li><a>: 목록형 메뉴 구조는 접근성 + 키보드 탐색에 적합

4. 터치/모바일 UX 최적화

📌 터치 영역 확보

  • 모바일에서는 44px x 44px 이상 클릭 가능한 영역 확보가 필요 (애플/구글 가이드 기준)
  • 버튼이나 메뉴 간격이 좁으면 오작동 가능성이 있음

📌 스크롤 방지 처리

  • 모바일에서 메뉴가 열렸을 때 배경 스크롤이 되면 UX 혼란
  • 해결 방법
document.body.style.overflow = 'hidden'; // 메뉴 열릴 때
document.body.style.overflow = ''; // 메뉴 닫힐 때

📌 키보드 탐색

  • Tab 키로 포커스 이동이 되고, Enter 또는 Space로 메뉴를 열 수 있어야 함
  • Escape로 메뉴 닫기 기능도 UX 향상에 도움이 됨

5. 스타일 및 애니메이션 처리

📌 자연스러운 슬라이드

  • max-height vs transform 비교:
    • max-height: 콘텐츠 높이 제한으로 슬라이드처럼 보이게 할 수 있음 (간단함). 높이는 충분히 높은 임의 값 지정
    • transform: translateY(), opacity: GPU 가속 → 부드럽고 성능 좋음 (하지만 구조 복잡할 수 있음)
    • max-height는 CSS 애니메이션에 사용하기 좋지만, 콘텐츠 높이가 유동적인 경우 JavaScript로 실제 높이를 측정해 style.maxHeight = nav.scrollHeight + "px" 형태로 설정하는 방법도 있음

📌 transition 성능

  • 속성별 transition 성능 차이:
    • 좋음: transform, opacity, color
    • 나쁨: height, width, top, left (layout 재계산 발생)
  • 성능 민감할 땐 transform 기반 slide 방식 고려

6. 레이어 구조 및 위치 이슈

📌 메뉴 위치 결정

  • 모바일에서 메뉴가 헤더 아래에 뜰지, 전체 화면을 덮을지 기획단에서 확정 필요
  • position: absolute 또는 fixed 중 선택

📌 스크롤 시 고정 여부

  • 헤더를 고정할지 말지에 따라 콘텐츠 여백 설정이 달라짐
    • position: sticky → 최근 많이 쓰이는 방법
    • position: fixed → 화면 상단 고정, top offset 고려해야 함

 

7. 유지보수성과 확장성

📌 변수/믹스인 활용

  • SCSS라면 $breakpoint-mobile, $header-height, $z-index-header 등으로 통일된 값 사용
  • @mixin mobile 같은 구조를 쓰면 미디어쿼리 일관성 높음

📌 네이밍 규칙

  • BEM 방식 추천:
    • .header, .header__inner, .gnb, .gnb__list, .menu-toggle
    • 의미가 명확하고 구조 추적이 쉬움

📌 JS 최소화

  • 복잡한 이벤트 처리 대신 class 토글 중심으로 작성
  • 외부 라이브러리 없이 가벼운 vanilla JS로 구현 가능

8. 테스트 사항

📌 다양한 기기/브라우저 테스트

  • 데스크탑: Chrome, Firefox, Edge, Safari
  • 모바일: Android Chrome, iOS Safari → 필수 확인
  • 반응형 뷰 툴이 아닌 실제 기기로 확인 추천

📌 키보드/스크린리더 테스트

  • 키보드로 포커스 이동해 보며 탭 순서, 메뉴 열림/닫힘 여부 확인
  • 스크린리더(TalkBack, VoiceOver)로 햄버거 버튼의 설명이 읽히는지 확인

📌 graceful degradation

  • JS가 꺼졌을 때도 최소한 메뉴에 접근은 가능해야 함
    • 메뉴를 기본적으로 보이게 처리하고, JS로 닫히게 바꾸는 방식도 가능

유의사항

항목 설명
 슬라이드 방식 max-height로 구현 시, 콘텐츠 높이가 달라질 경우 최대값 충분히 확보 필요 (ex. 500px)
 위치 position: absolute + top: 100%을 사용해 header 바로 아래에 출력되도록 처리
 접근성 <button aria-label="메뉴 열기"> 사용 → 스크린리더도 메뉴 버튼 역할 인식 가능
구조 유지 마크업 변경 없이, CSS와 JS만으로 반응형 구현 가능
 z-index nav가 다른 콘텐츠 위에 오도록 z-index 필요
 터치 UX 메뉴 아이템 간격 충분히 확보 (padding, gap)하여 터치 쉽게 함

기본 반응형 헤더 구조

기본 마크업 예

<header class="header">
		<div class="header__inner">
			<h1 class="logo"><a href="/">MyLogo</a></h1>
			<nav class="gnb">
				<ul class="gnb__list">
					<li><a href="#">Home</a></li>
					<li><a href="#">Services</a></li>
					<li><a href="#">Contact</a></li>
				</ul>
			</nav>
			<button class="menu-toggle" aria-label="메뉴 열기" aria-expanded="false">☰</button>
		</div>
	</header>
	<script>
		const toggleBtn = document.querySelector('.menu-toggle');
		const navList = document.querySelector('.gnb');
		toggleBtn.addEventListener('click', () => {
			const isOpen = navList.classList.toggle('active');
			// classList.toggle(className) 해당클래스가 있으면 true,
            없으면 false를 반환하여 불리언 값을 반환하여 제어 가능

			toggleBtn.setAttribute('aria-expanded', isOpen)
		})
	</script>

 

 

[Responsive] header 기본 -1

...

codepen.io

 

반응형