인터랙션

스크롤 내리면 사라지고 올리면 돌아오는 헤더

사용자가 본문에 집중할 때는 헤더가 슬쩍 비켜주고, 다시 위로 의도를 표현하면 부드럽게 따라 올라옵니다. useScrollDirection 훅 + translate-y 트랜지션 조합.

ReactuseScrollDirection (커스텀 훅)requestAnimationFramepassive scroll listenerTailwind CSStranslate-y transition
라이브 데모
새 탭에서 열기
데모 불러오는 중…

제작 과정

한 줄 요약

스크롤 방향을 인지해 아래로 내릴 땐 헤더가 비켜주고 위로 올릴 땐 다시 등장하는 패턴 — useScrollDirection 훅 + translate-y 트랜지션.

이럴 때 필요해요

긴 본문 페이지에서 상단 헤더가 늘 시야를 차지하면 답답하다. 토스·노션·인스타그램 웹뷰가 "아래로 내릴 땐 헤더 빠지고, 위로 올릴 땐 등장"하는 인터랙션을 쓰는 이유 — 글 읽기 모드와 네비게이션 모드를 사용자의 스크롤 의도로 구분해 주는 것.

어떻게 동작하나

  1. 방향 측정 — 방향은 위치의 시간 변화율이라 IntersectionObserver로 못 잡는다. lastYRef에 직전 scrollY를 들고 현재값과 비교해 부호를 본다 (+면 down, -면 up).
  2. rAF 스로틀 — scroll 이벤트는 프레임당 여러 번 발사되니 requestAnimationFrame + ticking 플래그로 프레임당 한 번만 측정. listener는 { passive: true }.
  3. threshold 히스테리시스 — 1px만 떨려도 방향이 바뀌면 헤더가 깜빡인다. Math.abs(delta) ≥ threshold(기본 8px) 통과해야만 갱신.
  4. topOffset 강제 노출 — 페이지 최상단에선 방향과 무관하게 헤더를 항상 노출. 막 도착했을 때 헤더가 없으면 길을 잃는다.
  5. CSS-translate-y-fulltranslate-y-0, duration-300 ease-out. top:0이라야 슬라이드가 자연스럽다.

놓치기 쉬운 것

  • display:none 토글은 금물 — 트랜지션이 끊긴다. 위치만 옮긴다.
  • sticky 컨테이너의 부모가 overflow: hidden이면 sticky가 안 잡힌다.
  • iOS Safari momentum scroll에서 방향이 끝물에 잠깐 반대로 잡히기도 — threshold를 12~16으로 키우면 거의 안 보인다.
  • threshold가 너무 크면(20+) 위로 빠르게 올렸을 때 등장이 늦어 답답하다. 8~12이 sweet spot.
  • 헤더 안에 드롭다운·메가메뉴가 있으면 헤더가 숨을 때 메뉴도 같이 닫는 처리가 필요하다.

이런 곳에 써요

  • 모바일 본문 위주 페이지 — 블로그 상세, 뉴스 기사, 긴 마케팅 랜딩
  • 콘텐츠 피드(인스타·X 웹) 상단 헤더
  • 풀스크린 미디어 페이지의 컨트롤 자동 숨김
  • 다단계 폼·온보딩의 진행률 바 — 본문 집중 모드 대응

소스 코드

· 데모 페이지에서 자동 추출
import type { Metadata } from "next";
import { StickyHeaderDemo } from "./-components/StickyHeaderDemo";

export const metadata: Metadata = {
  title: "Sticky Header — 스크롤 방향 hide/show (데모)",
  description:
    "사용자가 아래로 스크롤하면 상단 헤더를 슬쩍 숨기고, 위로 다시 스크롤하면 부드럽게 노출하는 인터랙션. useScrollDirection 훅 + translate 트랜지션 조합 데모.",
  robots: { index: false, follow: false },
};

export default function Page() {
  return <StickyHeaderDemo />;
}
51조회수

댓글