안녕하세요 이코노트허브 입니다.
긴 글을 읽다 보면 스크롤을 한참 내렸는데, 목차나 "맨 위로" 버튼이 화면 위쪽에 그대로 남아있어서 클릭 한 번에 원하는 곳으로 이동했던 경험 있으실 겁니다. 이런 효과를 position: sticky 속성 하나로 자바스크립트 없이 만들 수 있습니다.
[sticky, fixed와 뭐가 다를까요?]
고정 위치를 만드는 방법은 크게 두 가지가 있어서 헷갈리기 쉽습니다.
- position: fixed: 화면(뷰포트) 기준으로 완전히 고정됩니다. 부모 요소가 어디에 있든 상관없이 항상 같은 위치에 붙어있습니다.
- position: sticky: 평소엔 원래 자리에 있다가, 스크롤이 특정 지점을 지나면 그때부터 고정됩니다. 부모 요소의 범위 안에서만 고정된다는 점이 핵심입니다.
목차처럼 "본문 옆에서 같이 스크롤되다가, 화면 위쪽에 닿으면 그 자리에 멈추는" 효과를 원한다면 sticky가 훨씬 자연스럽습니다.
[기본 문법]
.toc {
position: sticky;
top: 20px;
}
top: 20px는 "화면 위쪽에서 20px 떨어진 지점에 닿으면 고정하라"는 뜻입니다. 스크롤을 내리다가 목차가 화면 위쪽 20px 지점에 도달하면, 그 위치에서 딱 멈춰서 계속 보이게 됩니다.
[실전 예제: 본문 옆 목차 고정하기]
<div class="post-layout">
<div class="toc">
<h3>목차</h3>
<ul>
<li><a href="#section1">1. 시작하기</a></li>
<li><a href="#section2">2. 실전 예제</a></li>
<li><a href="#section3">3. 마무리</a></li>
</ul>
</div>
<div class="post-content">
본문 내용...
</div>
</div>
.post-layout {
display: flex;
gap: 24px;
align-items: flex-start;
}
.toc {
position: sticky;
top: 20px;
width: 220px;
flex-shrink: 0;
}
.post-content {
flex: 1;
}
여기서 align-items: flex-start를 부모(.post-layout)에 꼭 넣어야 합니다. 이게 없으면 flex 자식 요소들이 서로의 높이에 맞춰 늘어나면서, sticky가 원하는 대로 작동하지 않는 경우가 많습니다. flex-shrink: 0은 목차가 본문 내용에 밀려 좁아지지 않도록 막아줍니다.
[sticky가 작동하지 않을 때 체크리스트]
sticky는 특정 조건에서 조용히 작동을 멈추는 경우가 있어서, 초보자가 가장 자주 막히는 속성이기도 합니다.
- top 값을 안 준 경우:
position: sticky;만 쓰고top,bottom중 아무것도 지정하지 않으면 고정되지 않습니다. 반드시 기준값을 함께 줘야 합니다. - 부모 요소에 overflow: hidden(또는 auto)이 걸려있는 경우: 부모 요소가 스크롤 영역을 자체적으로 가지고 있으면, sticky가 그 부모 안에서만 작동하려다 오작동하는 경우가 많습니다. 부모의
overflow속성을 확인해보세요. - 부모 요소의 높이가 자식보다 작은 경우: sticky 요소는 부모의 세로 범위 안에서만 고정됩니다. 목차가 본문(부모)보다 세로로 더 길면 고정 효과가 이상하게 보일 수 있습니다.
[모바일에서는 끄는 게 나을 수도 있어요]
스마트폰처럼 화면이 좁을 땐, 목차를 옆에 나란히 둘 공간 자체가 없습니다. 지난번 배운 미디어 쿼리로 모바일에서는 sticky를 해제하는 것을 추천합니다.
@media (max-width: 768px) {
.toc {
position: static;
}
}
position: static은 원래대로(고정 없이) 되돌리는 기본값입니다.
[초보자가 자주 하는 실수 2가지]
- fixed와 sticky를 섞어 쓰다 레이아웃이 깨지는 경우: fixed는 원래 자리를 완전히 벗어나 버려서, 다른 요소들이 그 공간을 채우려고 밀려옵니다. 반면 sticky는 원래 자리의 공간을 그대로 유지합니다. 목차처럼 "자기 자리는 지키면서" 고정하고 싶다면 sticky가 맞습니다.
- z-index 없이 다른 요소에 가려지는 경우: sticky 요소가 스크롤 중 다른 콘텐츠(이미지, 광고 등)에 가려 보이지 않을 수 있습니다.
z-index: 10;정도를 함께 지정해두면 항상 위에 표시됩니다.
핵심 요약
position: fixed는 화면 기준 완전 고정,position: sticky는 부모 범위 안에서 스크롤에 따라 고정됩니다.- sticky를 쓰려면
top값을 반드시 함께 지정해야 합니다. - 부모 요소의
overflow나 높이 때문에 작동하지 않을 수 있으니, 안 되면 이 두 가지부터 확인하세요. - 모바일에서는 공간이 부족하므로 미디어 쿼리로 sticky를 해제하는 것이 좋습니다.
이미 블로그에 목차 기능을 만들어두셨다면, sticky 속성만 추가해도 스크롤 내내 목차가 따라다니는 훨씬 편리한 경험을 만들 수 있습니다.
