CSS 강좌 / position의 relative·absolute·fixed·sticky 차이
position: relative는 원래 공간을 남긴 채 요소를 이동하고, absolute와 fixed는 일반 흐름에서 빠져 별도의 기준으로 배치됩니다. sticky는 처음에는 흐름 안에 있다가 스크롤 중 지정한 경계에 붙되 부모 영역의 제한을 받습니다. 값을 고를 때는 위치뿐 아니라 주변 요소의 공간과 스크롤 동작을 함께 판단해야 합니다.
static을 기준으로 다섯 값 비교하기
| 값 | 일반 흐름의 원래 공간 | 위치와 스크롤 기준 |
|---|---|---|
| static | 유지 | 일반 흐름. top·left 같은 오프셋이 적용되지 않음 |
| relative | 유지 | 원래 배치 위치에서 오프셋만큼 이동 |
| absolute | 차지하지 않음 | 보통 가장 가까운 positioned 조상의 포함 블록 |
| fixed | 차지하지 않음 | 보통 뷰포트. 일부 조상 속성은 기준을 바꿀 수 있음 |
| sticky | 유지 | 가까운 스크롤 메커니즘 조상과 포함 블록의 경계 |
일반 흐름(normal flow)은 요소가 문서의 배치 규칙에 따라 공간을 차지하는 상태입니다. absolute로 옮긴 요소를 뒤따르는 본문이 피해 주리라고 기대하면 안 됩니다. 요소가 흐름에서 빠지면 다음 요소는 그 자리의 공간을 사용하며, 겹치지 않게 하려면 레이아웃 구조나 필요한 padding을 따로 마련해야 합니다.
네 오프셋 top·right·bottom·left는 위치 지정 방식에 따라 의미가 달라집니다. relative에서 top: 16px은 원래 위치보다 아래로 옮기지만, absolute에서는 포함 블록 위쪽을 기준으로 거리를 지정합니다. 같은 숫자여도 좌표 기준이 다르므로 position부터 확인하세요. 정의와 예외는 MDN의 position 설명에서 확인할 수 있습니다.
relative는 이동해도 원래 공간을 남김
relative는 다른 요소의 배치를 다시 당겨 채우지 않습니다. 카드 한 개를 조금 아래로 옮겨도 다음 카드의 원래 시작 위치는 유지됩니다. 그래서 아래 요소와 겹치거나 위에 빈 공간이 보일 수 있습니다. 문서 전체의 간격을 바꾸려는 목적이라면 relative의 top보다 margin, padding, gap이 적절한지 먼저 검토하세요.
relative의 다른 용도는 absolute 자식의 위치 기준을 만드는 것입니다. 부모에 position: relative만 쓰고 오프셋을 지정하지 않으면 부모 자체는 이동하지 않으면서 자식 배지의 기준이 됩니다. position을 지정한다고 곧바로 모든 요소가 겹치거나 문서 흐름에서 빠지는 것은 아닙니다.
absolute의 기준 부모와 부모 높이 확인하기
absolute 요소는 보통 position이 static이 아닌 가장 가까운 조상을 기준으로 배치됩니다. 카드에 relative를 주고 자식에 absolute·top·right를 주면 카드 안의 배지가 됩니다. 해당 조상이 없으면 더 바깥의 기준을 사용하므로 카드가 아니라 페이지 쪽으로 배지가 이동할 수 있습니다. transform 같은 속성도 포함 블록을 만드는 경우가 있어 position 선언만 찾고 끝내면 안 됩니다.
absolute 자식만 있는 부모는 그 자식의 높이를 일반 흐름의 콘텐츠 높이로 더하지 않습니다. 부모가 높이 0처럼 보이거나 테두리가 접히는 문제는 자식이 보이더라도 발생할 수 있습니다. 내용이 자연스럽게 부모 높이를 정해야 한다면 자식을 흐름에 두고, 겹쳐 놓을 작은 배지만 absolute로 만드는 편이 관리하기 쉽습니다.
전체 예제로 공간 유지와 배지 비교하기
아래 코드를 position-flow.html로 저장합니다. 세 구역은 앞 요소·대상·뒤 요소를 동일한 HTML 순서로 구성했습니다. 카드 배지의 12px은 카드의 기준 영역에 대한 거리이며 본문과 겹치지 않도록 위쪽 padding을 확보했습니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>position-flow.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.flow { border: 2px dashed #789; margin-bottom: 40px; margin-right: 24px; }
.flow > div { height: 56px; padding: 12px; background: #e8f5de; }
.flow .target { background: #dceefa; }
.relative .target { position: relative; top: 16px; left: 20px; }
.absolute { position: relative; }
.absolute .target { position: absolute; top: 16px; left: 20px; }
.card { position: relative; padding: 64px 20px 20px; background: #dceefa; max-width: 600px; }
.badge { position: absolute; top: 12px; right: 12px; padding: 4px 12px; background: #ffe4b5; }
</style>
</head>
<body>
<h1>원래 공간이 남는가?</h1>
<h2>static</h2><div class="flow static"><div>앞 요소</div><div class="target">대상</div><div class="after">뒤 요소</div></div>
<h2>relative</h2><div class="flow relative"><div>앞 요소</div><div class="target">대상</div><div class="after">뒤 요소</div></div>
<h2>absolute</h2><div class="flow absolute"><div>앞 요소</div><div class="target">대상</div><div class="after">뒤 요소</div></div>
<h2>카드 안의 배지</h2><div class="card"><span class="badge">새 소식</span><p>배지는 카드의 오른쪽 위에 놓이고 본문은 위쪽 padding으로 배지와 분리합니다.</p></div>
</body>
</html>
- static과 relative 구역에서 뒤 요소의 위치를 비교합니다. relative 대상이 아래로 16px 이동해도 뒤 요소를 위한 배치 공간은 원래 대상 위치를 기준으로 유지됩니다. top만 0으로 바꾸어 확인한 뒤 16px로 복원합니다.
- absolute 구역은 대상이 흐름에서 빠져 뒤 요소가 앞 요소 바로 다음에 놓입니다. .absolute .target의 position을 static으로 바꾸면 부모 높이가 대상 높이만큼 늘어납니다. 비교 후 absolute로 복원합니다.
- 카드 배지에서 .card의 position: relative만 지워 봅니다. 배지가 카드의 오른쪽 위를 기준으로 삼지 못하는 것을 확인한 뒤 즉시 복원합니다. 다른 조건은 바꾸지 않습니다.
- 배지의 글자를 길게 늘려 본문과의 간격을 확인합니다. absolute는 본문을 자동으로 밀어내지 않으므로 배지가 여러 줄이 될 수 있다면 고정 padding만으로 충분한지 검토해야 합니다. 실습 후 원래 글자로 돌립니다.
fixed와 sticky의 스크롤 차이 이해하기
fixed는 보통 뷰포트에 고정되어 문서를 스크롤해도 화면상의 위치가 유지됩니다. 화면 오른쪽 아래의 맨 위로 버튼처럼 작은 조작 요소에 사용할 수 있습니다. 흐름의 공간을 차지하지 않으므로 고정 바가 있다면 본문 끝과 포커스 대상이 가려지지 않게 여유 공간을 확보해야 합니다.
조상에 transform이 적용되면 fixed의 포함 블록이 뷰포트에서 그 조상으로 바뀔 수 있습니다. fixed가 화면을 따라오지 않고 특정 상자 안에서 움직인다면 조상의 transform·filter 등 포함 블록을 바꾸는 속성을 확인하세요. 이번 기본 예제에는 이런 조상 속성을 넣지 않아 뷰포트 고정을 관찰합니다.
sticky는 해당 축에 top 같은 non-auto 경계값이 있어야 붙는 효과를 관찰할 수 있습니다. 제목은 처음부터 화면 위에 고정된 것이 아니라 자신의 흐름 위치에 있다가 스크롤로 위 경계에 도달하면 붙습니다. 부모의 끝에 도달하면 그 제한을 따라 움직여 다음 구역 위에 계속 떠 있지 않습니다.
sticky는 가까운 스크롤 메커니즘 조상의 영향을 받습니다. overflow: auto·scroll뿐 아니라 hidden인 조상도 기준에 영향을 줄 수 있습니다. 실제로 스크롤하는 영역이 예상한 조상과 다르거나, 컨테이너와 제목 높이가 비슷해 이동 여유가 없으면 붙는 동작이 보이지 않을 수 있습니다. overflow를 무조건 제거하기보다 어떤 요소가 스크롤 기준인지 먼저 확인하세요.
전체 예제로 고정 시작과 부모 경계 관찰하기
다음 코드를 position-scroll.html로 저장합니다. 제목 앞에 최소 320px의 도입 영역이 있으므로 첫 화면에서 제목은 화면 위에 붙어 있지 않습니다. 장 안에 충분한 높이를 두고 뒤에 다른 구역을 두어 붙는 시작과 해제 양쪽을 관찰합니다.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>position-scroll.html</title>
<style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
body { margin: 0; padding-bottom: 80px; }
.intro { min-height: 320px; padding: 24px; background: #eef2f6; }
.chapter { min-height: 760px; background: #e8f5de; }
.sticky { position: sticky; top: 0; margin: 0; padding: 16px; background: #dceefa; border-bottom: 2px solid #789; }
.chapter p { padding: 16px; }
.tail { min-height: 900px; padding: 24px; background: #fff2df; }
.fixed { position: fixed; right: 16px; bottom: 16px; padding: 12px; background: #fff; border: 2px solid #145f8f; }
</style>
</head>
<body>
<div class="intro" id="top"><h1>스크롤하면서 붙는 제목</h1><p>아래 장의 제목은 처음에는 일반 흐름에 놓입니다. 스크롤하여 화면 위에 도달하면 붙고, 장의 끝에서는 부모 영역을 벗어나지 못합니다.</p></div>
<section class="chapter"><h2 class="sticky">이 장의 제목</h2><p>장 안의 설명입니다. 충분한 높이를 두어 제목이 붙어 있는 시간을 관찰합니다.</p></section>
<section class="tail"><h2>다른 구역</h2><p>앞 장의 제목이 이 구역 위에 계속 남는지 확인하세요.</p></section>
<a class="fixed" href="#top">맨 위로</a>
</body>
</html>
- 스크롤하기 전에 제목이 도입 영역 아래에 있는지 확인합니다. 천천히 내려 제목이 화면 위에 닿으면 그 자리에 붙습니다. 오른쪽 아래의 맨 위로 링크는 처음부터 같은 화면 위치에 있습니다.
- 장을 지나 다른 구역으로 내려갑니다. sticky 제목은 부모 장의 끝에 밀려 올라가지만 fixed 링크는 계속 보입니다. 링크를 눌러 도입 영역으로 돌아갑니다.
- .sticky의 top만 auto로 바꾸면 세로축의 붙는 경계가 없어 일반 흐름처럼 스크롤됩니다. 확인 후 0으로 복원합니다.
- .chapter에 overflow: hidden만 추가하여 가까운 스크롤 메커니즘 조상이 바뀌었을 때 창 스크롤의 sticky 결과를 확인합니다. 비교 후 추가한 선언을 제거합니다.
위치가 예상과 다를 때 점검 순서
- 공간이 남는지부터 확인합니다. relative·sticky인지, absolute·fixed인지에 따라 주변 요소의 배치가 달라집니다.
- 개발자 도구에서 실제 position과 top·right·bottom·left 적용값을 확인합니다. 더 우선하는 규칙과 미디어 쿼리도 확인하세요.
- absolute는 기준 조상, fixed는 포함 블록을 바꾸는 조상 속성을 확인합니다. sticky는 경계값·스크롤 조상·부모 높이와 이동 여유를 확인합니다.
- 좌표가 맞는데 가려진다면 위치 문제와 겹침 순서·overflow 잘림을 구분합니다. 큰 z-index가 모든 가림을 해결하는 것은 아닙니다.










