|
|
상단에 고정되는 메인메뉴 만들기
CSS의 fixed position으로 메뉴바 상단 고정
ixed position을 이용해서 스크롤에 해도 따라가지 않고 항상 화면 상단에 고정되어 있는 메뉴바를 만들어보겠습니다.
fixed position의 특징
CSS의 position 속성은 엘리먼트가 브라우저 화면에 어떻게 배치되는가를 결정합니다. 어떤 엘리먼트의 position 속성을 fixed로 지정해줄 경우, 해당 엘리먼트는 부모 엘리먼트로 부터 완전히 독립되어 브라우저 화면(viewport) 상에서 어디든지 원하는 위치에 자유롭게 배치시킬 수 있게 됩니다. 뿐만 아니라, 브라우저 화면을 스크롤했을 때도 영향을 받지 않기 때문에, 다른 엘리먼트들이 상하좌우로 움직일 때, position 속성이 fixed로 설정되어 있는 엘리먼트는 그 자리에서 움직이지 않습니다.
이러한 fixed position의 특징은 웹에서 다양한 UI를 구현하는데 사용될 수 있는데요. 이번 포스팅에서는 그 중에서 가장 널리 사용되고 있는 상단에 고정되어 있는 메뉴바/네비게이션을 만드는 방법에 대해서 알아보겠습니다.
전형적인 메뉴바 만들기
일반적으로 메뉴바는 <header/>과 <nav/> 태그를 이용해서 마크업을 많이 합니다. 실제로는 <ul/>, <li/>, <a/> 태그를 이용해서 다른 웹페이지로 이동이 가능한 메뉴 목록을 만들어야 하지만 최대한 간단한 예제를 위해서 <span/> 태그만을 사용하였습니다.
헤더 영역에 대한 스타일도 메인 영역과 시작적으로 구분이 될 수 있을 정도로만 간단히 합니다.
header {
height: 75px;
padding: 1rem;
color: white;
background: teal;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
이렇게 작성된 전형적인 메뉴바는 스크롤바를 아래로 움직이면, 움직이는 만큼 브라우저 화면에서 사라지게 됩니다.
이러한 전형적인 메뉴바가 보여줄 컨턴츠가 적은 웹사이트에서는 다시 금방 스크롤바를 위로 움직일 수 있기 때문에 큰 문제가 되지 않겠지만, 리스트 형태로 컨텐츠를 세로로 길게 보여줘야 하는 웹사이트의 경우, 다른 메뉴로 이동하기 위해서 사용자가 매번 맨 위까지 스크롤링을 해야 한다면 상당히 불편할 것입니다.
메뉴바를 화면 상단에 고정시키기
자 그럼, fixed position을 활용해서 위에서 작성한 메뉴바를 화면 상단에 고정시켜보겠습니다. 우선 스크롤에 영향을 받지 않고 화면의 특정 지점에 고정될 수 있도록 헤더 영역의 position 속성을 fixed로 지정합니다. 그 다음, 헤더 영역을 화면의 어디에 고정시킬지를 offset 속성을 이용해서 설정해줍니다. 화면 상단에 빈틈없이 붙일 것이기 때문에 top 속성을 0으로 설정하고, 헤더 영역을 화면 양측에 꽉차게 하기 하기 위해서 left 속성과 right 속성도 0으로 설정합니다. (left 속성과 right 속성 대신에 width 속성을 100%로 설정해도 됩니다.)
header {
position: fixed;
top: 0;
/* width: 100% */
left: 0;
right: 0;
/* 생략 */
}
그리고 나서 스크롤을 해보면 메뉴바가 화면 상단에 고정되어서 아무리 아래로 스크롤해도 항상 메뉴바를 볼 수 있는 상태가 됩니다.
하지만 여기서 한가지 문제가 발생하는데요. 자세히 보면 메인 영역의 윗 부분이 헤더 영역의 뒤로 들어가서 일부 컨텐츠가 보이지 않습니다. 이것은 <header> 엘리먼트가 부모인 <body> 엘리먼트로 부터 완전히 독립되면서, <body> 엘리먼트에서 점유하고 있던 <header> 엘리먼트 공간이 사라져버렸기 때문입니다.
가려진 컨텐츠 보이게 하기
위 문제는 상단 고정 메뉴바 사용할 때 흔히 겪은 문제이며, <body> 엘리먼트의 상단에 메뉴바 높이 만큼 패딩(padding)을 주면 쉽게 해결할 수 있습니다.
body {
padding-top: 75px;
/* 생략 */
}
자, 이제 가려지는 컨텐츠 없이 상단 고정 메뉴바를 사용할 수 있게 되었습니다. :)
마치면서
본 포스팅에서 간단하게 구현해본 상단에 고정되는 메뉴바는 사실 Bootstrap과 같은 CSS 라이브러리를 통해 쉽게 접할 수 있습니다. 하지만 한번 직접 구현하고 어떻게 동작하는 건지 파악해보는 것도 CSS를 공부하고 계신다면 도움이 될 것 같습니다.
참고로 최근에는 fixed position 대신에 sticky position을 이용해서 동일한 UI를 구현하는 경우가 많은 것 같습니다. 이에 대한 내용은 별도의 포스팅에서 다루었으니 참고 바랍니다.
https://www.daleseo.com/css-position-fixed-navigation/
리액트(React)를 활용하여 화면 상단에 고정되는 메뉴를 만드는 방법입니다. 스크롤을 내리면 타이틀이 사라지면서 메뉴가 화면 상단에 고정됩니다. 같은 기능을 구현할 때 자바스크립트만 사용하면 꽤 복잡한 코딩을 해야 합니다. 간단하고 빠르게 원하는 기능을 구현할 수 있다는 점은 우리가 리액트를 사용하는 이유 중 하나겠죠.
그럼 스크롤을 내렸을 때 메뉴를 화면 상단에 고정하는 방법에 대해서 알아보겠습니다.
1. 기본 기능 – App 작성
스크롤을 내렸을 때 메뉴를 상단에 고정하는 기능을 구현하려면 스크롤 이벤트를 감지하고 메뉴의 스타일과 타이틀 스타일을 동적으로 조작해야 합니다. useEffect를 이용하면 간단하게 처리할 수 있습니다.
다음은 useEffect와 useState를 사용하여 기능을 구현하는 예제 코드입니다.
import React, { useState, useEffect } from 'react';
function App() {
const [isSticky, setSticky] = useState(false);
useEffect(() => {
const handleScroll = () => {
// 현재 스크롤 위치를 가져옴
const currentScrollPosition = window.pageYOffset;
// 스크롤 위치가 메뉴의 위치보다 크거나 같으면 메뉴를 고정
if (currentScrollPosition >= 50) {
setSticky(true);
} else {
setSticky(false);
}
};
window.addEventListener('scroll', handleScroll);
// Clean up function
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return (
<div>
<header className={isSticky ? 'sticky' : ''}>
{isSticky ? null : <h1>Title</h1>}
<nav>
<ul>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
</ul>
</nav>
</header>
<div>
{/* 나머지 페이지 콘텐츠 */}
</div>
</div>
);
}
export default App;
위 코드에서는 useState를 사용하여 isSticky 상태를 관리하고, useEffect를 사용하여 스크롤 이벤트를 수신합니다. useEffect의 두 번째 인자로 빈 배열을 전달하여 컴포넌트가 마운트될 때만 이벤트 핸들러가 작동되도록 해줍니다.
handleScroll 함수에서는 현재 스크롤 위치를 가져와 메뉴의 위치와 비교한 후, setSticky 함수를 호출하여 isSticky 상태를 변경합니다. return 구문에서는 컴포넌트가 언마운트될 때 이벤트 핸들러를 제거합니다.
마지막으로, header 요소에 sticky 클래스를 동적으로 추가하여 CSS를 통해 메뉴를 고정합니다. 이제 스크롤을 내리면 sticky 클래스가 해당 요소에 추가되는 것을 확인할 수 있을 것입니다.
위 코드는 스크롤을 내렸을 때 타이틀을 숨겨줍니다. 만약 스크롤을 내렸을 때 타이틀을 그대로 보여주고 싶다면 아래 부분처럼 수정해주면 됩니다.
...
<header className={isSticky ? 'sticky' : ''}>
<h1>Title</h1>
<nav>
...
...
위와 같이 코드를 수정하면 타이틀이 사라지지 않습니다.
2. CSS 작성
무엇인가를 화면에 고정할 때 사용할 수 있는 CSS 속성은 position: fixed입니다. 아래 CSS 속성들은 기본적인 디자인만 구현되어 있으며, 필요한 것들은 추가해서 사용할 수 있습니다.
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
position: relative;
}
.sticky {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 100;
}
header 요소에는 display: flex; 속성을 적용하여 내부 요소들을 가로 방향으로 정렬합니다. 그리고 justify-content: space-between; 속성을 적용하여 내부 요소들 사이의 간격을 균등하게 설정합니다. align-items: center; 속성을 적용하여 내부 요소들을 수직 중앙 정렬합니다. padding: 1rem; 속성을 적용하여 header 요소의 마진을 설정합니다. position: relative; 속성을 적용하여 내부 요소들이 상대적인 위치를 가지도록 설정합니다.
sticky 클래스에는 position: fixed; 속성을 적용하여 요소를 화면 상단에 고정시킵니다. top: 0; 속성을 적용하여 요소를 화면 상단에 정확히 위치시킵니다. left: 0; 속성을 적용하여 요소를 화면 왼쪽에 위치시킵니다. width: 100%; 속성을 적용하여 요소의 너비를 화면 전체로 설정합니다. background-color: #fff; 속성을 적용하여 요소의 배경색을 흰색으로 설정합니다. box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 속성을 적용하여 요소에 그림자 효과를 추가합니다. z-index: 100; 속성을 적용하여 요소의 z-index 값을 100으로 설정합니다.
위와 같이 sticky 클래스를 적용하면, 요소가 화면을 스크롤할 때 일정 위치에 고정되어 화면 상단에 고정된 메뉴를 구현할 수 있습니다.
3. 애니메이션 적용
타이틀이 사라질 때 애니메이션을 적용하여 위쪽으로 부드럽게 이동하도록 할 수도 있습니다. 리액트에서는 애니메이션을 비교적 쉽게 다룰 수 있는데, 이 코드에서 타이틀이 사라질 때 위쪽으로 올라가는 애니메이션을 적용하기 위해서는 react-transition-group 패키지를 사용하여 CSS 트랜지션을 구현해야 합니다.
먼저 react-transition-group 패키지를 설치합니다.
npm install react-transition-group
다음은 react-transition-group 패키지를 사용하여 타이틀 숨김 애니메이션을 구현한 예시입니다.
import React, { useState, useEffect } from 'react';
import { CSSTransition } from 'react-transition-group';
import './styles.css';
function App() {
const [isSticky, setSticky] = useState(false);
useEffect(() => {
const handleScroll = () => {
// 현재 스크롤 위치를 가져옴
const currentScrollPosition = window.pageYOffset;
// 스크롤 위치가 메뉴의 위치보다 크거나 같으면 메뉴를 고정
if (currentScrollPosition >= 50) {
setSticky(true);
} else {
setSticky(false);
}
};
window.addEventListener('scroll', handleScroll);
// Clean up function
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return (
<div>
<header className={isSticky ? 'sticky' : ''}>
<CSSTransition
in={!isSticky}
timeout={300}
classNames="title"
unmountOnExit
>
<h1>Title</h1>
</CSSTransition>
<nav>
<ul>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
</ul>
</nav>
</header>
<div>
{/* 나머지 페이지 콘텐츠 */}
</div>
</div>
);
}
export default App;
위 코드에서는 CSSTransition 컴포넌트를 사용하여 타이틀 숨김 애니메이션을 구현합니다. in prop을 사용하여 타이틀이 보여지는지 숨겨져 있는지를 결정하고, timeout prop을 사용하여 애니메이션 지속 시간을 설정합니다. classNames prop을 사용하여 CSS 클래스를 설정합니다. unmountOnExit prop을 사용하여 애니메이션 종료 후 DOM에서 컴포넌트를 제거합니다.
CSSTransition 컴포넌트를 사용하여 타이틀 숨김 애니메이션을 구현하도록 예시 코드를 작성하였습니다. 이제 CSS 파일에서 애니메이션을 구현합니다.
.title-enter {
opacity: 0;
transform: translateY(-100%);
}
.title-enter-active {
opacity: 1;
transform: translateY(0);
transition: opacity 300ms ease-in-out, transform 300ms ease-in-out;
}
.title-exit {
opacity: 1;
transform: translateY(0);
}
.title-exit-active {
opacity: 0;
transform: translateY(-100%);
transition: opacity 300ms ease-in-out, transform 300ms ease-in-out;
}
먼저, title-enter 스타일을 설정하여 컴포넌트가 처음 마운트될 때의 스타일을 정의합니다. opacity 속성을 0으로 설정하여 컴포넌트를 숨기고, transform 속성을 translateY(-100%)으로 설정하여 컴포넌트를 화면 위쪽으로 이동시킵니다.
다음으로, title-enter-active 스타일을 설정하여 애니메이션이 실행 중일 때의 스타일을 설정합니다. opacity 속성을 1로 설정하여 컴포넌트를 보이도록 하고, transform 속성을 translateY(0)으로 설정하여 컴포넌트를 원래 위치로 이동시킵니다. transition 속성을 사용하여 애니메이션의 지속 시간과 타이밍을 설정합니다.
타이틀이 사라질 때 적용되는 title-exit와 title-exit-active 스타일도 title-enter-* 스타일과 같은 방법으로 작성합니다.
이제 스크롤을 내리면 메뉴가 화면 상단에 고정되면서 타이틀이 애니메이션과 함께 화면 위쪽으로 사라지는 것을 확인할 수 있습니다.
4. 자바스크립트만 사용하여 작성하기
리액트를 사용하지 않고 자바스크립트만 사용해서 같은 기능을 구현할 수도 있습니다. 리액트보다 간단하게 보이지만, 직접 DOM을 조작해야 하고, 유지보수가 힘들어서 가능하다면 리액트로 작성하는 것이 좋습니다.
const header = document.querySelector('header');
const h1 = header.querySelector('h1');
const nav = header.querySelector('nav');
const ul = nav.querySelector('ul');
function handleScroll() {
// 현재 스크롤 위치를 가져옴
const currentScrollPosition = window.pageYOffset;
// 스크롤 위치가 메뉴의 위치보다 크거나 같으면 메뉴를 고정
if (currentScrollPosition >= 50) {
header.classList.add('sticky');
h1.style.display = 'none';
ul.style.marginTop = '0';
} else {
header.classList.remove('sticky');
h1.style.display = 'block';
ul.style.marginTop = '';
}
}
window.addEventListener('scroll', handleScroll);
<header>
<h1>Title</h1>
<nav>
<ul>
<li>Menu 1</li>
<li>Menu 2</li>
<li>Menu 3</li>
</ul>
</nav>
</header>
<div>
<!-- 나머지 페이지 콘텐츠 -->
</div>
위 코드에서는 querySelector 메서드를 사용하여 DOM 요소를 가져옵니다. handleScroll 함수에서는 현재 스크롤 위치를 가져온 후, 스크롤 위치에 따라 요소들의 스타일을 조작합니다.
header.classList.add(‘sticky’) 코드는 header 요소에 sticky 클래스를 추가하여 메뉴를 고정합니다. h1.style.display = ‘none’ 코드는 h1 요소의 스타일을 직접 조작함으로써 화면에서 숨깁니다. ul.style.marginTop = ‘0’ 코드는 ul 요소의 margin-top 값을 0으로 설정하여 메뉴가 화면 상단에 고정될 때 메뉴와 페이지 콘텐츠 사이의 간격을 없앱니다.
header.classList.remove(‘sticky’) 코드는 header 요소에서 sticky 클래스를 제거하여 메뉴를 원래 위치로 돌려놓습니다. h1.style.display = ‘block’ 코드는 h1 요소를 다시 보이도록 합니다. ul.style.marginTop = ” 코드는 ul 요소의 margin-top 값을 초기값으로 되돌려줍니다.
위와 같이 자바스크립트로도 같은 기능을 구현할 수 있습니다. 다만, 리액트를 사용하는 것보다는 코드가 복잡해지고 유지보수가 어려워질 수 있습니다.
html
상단에 고정되는 메인메뉴 만들기
https://www.walterz.net/2023/03/22/sticky-menu-react-javascript/
|
|
