21. 반응형 햄버거메뉴

화면 폭에 따른 메뉴 UI 전환 비교 — 미디어 쿼리로 화면 폭을 감지하여 내비게이션 노출 구조를 변경한다.

8.반응형 햄버거메뉴

반응형 웹(Responsive Web) 디자인에서 화면 너비에 맞춰 내비게이션(Navigation) 메뉴의 구조를 전환하는 방법을 다룬다. 데스크톱 환경에서는 전체 메뉴 항목을 가로로 나열하고, 화면 폭이 좁은 모바일 기기에서는 햄버거 메뉴(Hamburger Menu) 아이콘 형태로 축소하여 화면 공간을 절약한다. 순수 자바스크립트(JavaScript)와 제이쿼리(jQuery) 라이브러리를 각각 활용하여 메뉴의 열림과 닫힘 상호작용을 구현한다.

1. 자바스크립트

미리보기

index.html

웹 문서의 뼈대를 구성하는 HTML 코드다. 상단 영역을 정의하는 <header> 태그 내부에 로고 링크, 햄버거 메뉴 아이콘(<i>), 그리고 목록 형태의 메뉴 항목들(<ul>)을 배치한다.

<!DOCTYPE html>
<html lang="ko">
	<head>
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Responsive Navigation Bar</title>
		
		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" />
		
		<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@600;700&display=swap" rel="stylesheet" />
		
		<link rel="stylesheet" href="style.css" />
	</head>
	<body>
		<header>
			<nav>
				<a href="#home" id="logo">Site Logo</a>
				<i class="fas fa-bars" id="ham-menu"></i>
				<ul id="nav-bar">
					<li>
						<a href="#home">Home</a>
					</li>
					<li>
						<a href="#about">About</a>
					</li>
					<li>
						<a href="#services">Services</a>
					</li>
					<li>
						<a href="#team">Team</a>
					</li>
					<li>
						<a href="#contact">Contact</a>
					</li>
				</ul>
			</nav>
		</header>
		<section id="home">
			<h1>Home Section</h1>
		</section>
		
		<script src="script.js"></script>
	</body>
</html>

외부 리소스로 폰트 어썸(Font Awesome) 아이콘 폰트와 구글 웹 폰트(Poppins)를 불러온다. 햄버거 아이콘은 <i class="fas fa-bars" id="ham-menu"></i>로 삽입하여 추후 자바스크립트 클릭 이벤트의 대상이 된다. <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 선언이 없으면 모바일 기기 브라우저에서 화면을 데스크톱 비율로 축소하여 렌더링하므로 미디어 쿼리가 올바르게 동작하지 않는다.

style.css

레이아웃 배치와 반응형 스타일을 정의하는 CSS 코드다. 플렉스박스(Flexbox)를 통해 요소를 정렬하고 미디어 쿼리(Media Query)로 화면 너비 구간에 따라 스타일을 분기한다.

* {
	padding: 0;
	margin: 0;
	box-sizing: border-box;
}
html {
	font-size: 62.5%;
}
*:not(i) {
	font-family: 'Poppins', sans-serif;
}
header {
	position: fixed;
	width: 100%;
	background-color: #2c8eec;
	padding: 3rem 5rem;
}
nav {
	display: flex;
	justify-content: space-between;
	align-items: center;
}
nav ul {
	list-style: none;
	display: flex;
	gap: 2rem;
}
nav a {
	font-size: 1.8rem;
	text-decoration: none;
}
nav a#logo {
	color: #000000;
	font-weight: 700;
}
nav ul a {
	color: #ffffff;
	font-weight: 600;
}
nav ul a:hover {
	border-bottom: 2px solid #ffffff;
}
section#home {
	height: 100vh;
	display: grid;
	place-items: center;
}
h1 {
	font-size: 4rem;
}
#ham-menu {
	display: none;
}
nav ul.active {
	left: 0;
}
@media only screen and (max-width: 991px) {
	html {
		font-size: 56.25%;
	}
	header {
		padding: 2.2rem 5rem;
	}
}
@media only screen and (max-width: 767px) {
	html {
		font-size: 50%;
	}
	#ham-menu {
		display: block;
		color: #ffffff;
	}
	nav a#logo,
	#ham-menu {
		font-size: 3.2rem;
	}
	nav ul {
		background-color: black;
		position: fixed;
		left: -100vw;
		top: 73.6px;
		width: 100vw;
		height: calc(100vh - 73.6px);
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: space-around;
		transition: 1s;
		gap: 0;
	}
}
@media only screen and (max-width: 575px) {
	html {
		font-size: 46.87%;
	}
	header {
		padding: 2rem 3rem;
	}
	nav ul {
		top: 65.18px;
		height: calc(100vh - 65.18px);
	}
}

주요 스타일 속성과 작동 원리는 다음과 같다.

  • html { font-size: 62.5%; }: 기본 브라우저 폰트 크기(16px)를 10px로 맞추어 1rem = 10px 단위로 계산하기 쉽게 만든다.
  • *:not(i) { font-family: 'Poppins', sans-serif; }: 폰트 어썸 아이콘(<i>)에 구글 웹폰트가 덮어씌워져 아이콘 그래픽이 손상되는 문제를 방지한다.
  • #ham-menu { display: none; }: 넓은 데스크톱 화면에서는 햄버거 아이콘을 숨긴다.
  • @media only screen and (max-width: 767px): 미디어 쿼리(Media Query)는 화면 해상도나 뷰포트 조건에 따라 다른 스타일을 적용하는 문법이다. 마치 신체 치수에 맞춰 옷을 달리 입히는 재단 기준과 같다. 화면 폭이 767px 이하로 줄어들면 햄버거 아이콘을 노출하고, 메뉴 목록(nav ul)을 화면 바깥(left: -100vw)으로 이동시켜 숨긴다.
  • nav ul.active { left: 0; } 및 transition: 1s: 메뉴에 active 클래스가 추가되면 화면 왼쪽에서 1초 동안 슬라이드되며 나타난다.

script.js

사용자 클릭 동작에 반응하여 클래스를 추가하거나 제거하는 자바스크립트 코드다.

let hamMenuIcon = document.getElementById('ham-menu');
let navBar = document.getElementById('nav-bar');
let navLinks = navBar.querySelectorAll('li');

hamMenuIcon.addEventListener('click', () => {
	navBar.classList.toggle('active');
	hamMenuIcon.classList.toggle('fa-times');
});
navLinks.forEach((navLinks) => {
	navLinks.addEventListener('click', () => {
		navBar.classList.remove('active');
		hamMenuIcon.classList.toggle('fa-times');
	});
});

클래스 리스트(ClassList) 객체의 토글(toggle)과 리무브(remove) 메서드를 활용한다. 햄버거 메뉴(Hamburger Menu)는 평소에는 숨겨져 있다가 버튼을 누르면 서랍처럼 열리는 UI 요소다. 아이콘을 누르면 navBar에 active 클래스가 켜지고 꺼지며, 아이콘 역시 삼선 모양(fa-bars)에서 닫기 ‘X’ 모양(fa-times)으로 변경된다. 메뉴 항목(navLinks)을 클릭했을 때도 열려 있던 메뉴가 닫히도록 active 클래스를 제거한다.

2. 제이쿼리로 구현하기

동일한 상호작용 로직을 제이쿼리(jQuery) 라이브러리를 사용하여 간결하게 작성하는 구성이다.

index.html

제이쿼리 라이브러리 CDN(Content Delivery Network) 스크립트와 제이쿼리 코드가 담긴 파일을 HTML 문서 하단에 연결한다.

...생략
  
  <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
  <script src="jquery.js"></script>
</body>

반드시 jquery.js보다 제이쿼리 코어 라이브러리 스크립트가 먼저 로드되어야 한다. 순서가 바뀌면 $ 식별자를 찾지 못해 ReferenceError가 발생한다.

jquery.js

제이쿼리 메서드로 DOM 요소를 선택하고 이벤트를 처리하는 코드다.

let hamMenuIcon = $('#ham-menu');
let navBar = $('#nav-bar');
let navLinks = $('li');

hamMenuIcon.click(function () {
	navBar.toggleClass('active');
	hamMenuIcon.toggleClass('fa-times');
});
navLinks.click(function () {
	navBar.removeClass('active');
	hamMenuIcon.toggleClass('fa-times');
});

순수 자바스크립트의 document.getElementById() 대신 제이쿼리 선택자 $()를 사용하여 코드를 간소화한다. 순수 자바스크립트에서는 여러 개의 <li> 요소에 이벤트를 걸기 위해 forEach 반복문이 필요했으나, 제이쿼리는 $('li')처럼 선택한 모든 요소 집합에 대해 .click() 이벤트 핸들러를 한 번에 바인딩(Binding)한다.

댓글 남기기