웹 페이지를 만들다 보면 꼭 한 번은 이런 순간이 옵니다.
"이 버튼을 카드 오른쪽 위에 붙이고 싶은데 왜 저 멀리 날아가지?" "헤더를 위에 고정했더니 본문을 가려 버리네?" "분명 z-index: 9999를 줬는데도 왜 뒤에 있지?"
이 질문들의 중심에는 거의 항상 position이 있습니다. CSS에서 배치는 단순히 "옮긴다"의 문제가 아닙니다. 어떤 기준을 보고 움직이는가, 원래 흐름에 남아 있는가, 겹치면 누가 앞에 오는가를 함께 이해해야 합니다.
이번 장에서는 static, relative, absolute, fixed, sticky를 차근차근 익히고, 마지막에 z-index까지 연결해 보겠습니다. 이 장을 끝내면 요소가 갑자기 말을 안 듣는 이유가 훨씬 또렷하게 보일 것입니다.

먼저 머릿속에 넣어 둘 한 가지: 기본 흐름
CSS에서 대부분의 요소는 아무 설정이 없으면 문서의 기본 흐름 안에서 배치됩니다. 위에서 아래로, 앞에서 뒤로, 차례대로 쌓인다고 생각하면 됩니다. 지난 장에서 display와 흐름을 배웠다면, 이번 장은 그 흐름을 부분적으로 깨거나, 기준점을 바꾸는 방법이라고 보면 됩니다.
쉽게 말해, 기본 흐름은 줄 서기입니다. 모두 차례대로 서 있는데, position은 누군가는 제자리에서 살짝 비키고, 누군가는 줄 밖으로 나가고, 누군가는 화면 구석에 딱 붙어 버리게 만듭니다.1
position은 무엇을 바꾸는 속성일까
position은 요소가 어디를 기준으로, 어떤 방식으로 놓일지 정하는 속성입니다. 이때 자주 함께 쓰는 것이 top, right, bottom, left입니다. 다만 이 값들은 아무 때나 듣지 않습니다. position 값에 따라 반응이 달라집니다.12
이 장에서 다룰 다섯 가지는 다음과 같습니다.
static: 기본값relative: 원래 자리에서 살짝 이동absolute: 흐름에서 빠져 기준점에 맞춰 배치fixed: 화면에 고정sticky: 스크롤하다가 달라붙기
이제 하나씩 만나 보겠습니다.
static: 아무 일도 하지 않는 기본 상태
모든 요소의 기본 position은 static입니다. 이 상태에서는 요소가 일반적인 문서 흐름에 따라 놓입니다. 중요한 점은 top, right, bottom, left가 사실상 영향을 주지 않는다는 것입니다.1
.box {
position: static;
top: 20px;
left: 20px;
}위 코드는 얼핏 보면 박스를 옮길 것 같지만, 실제로는 거의 아무 일도 일어나지 않습니다. static은 "브라우저야, 네가 평소 하던 대로 해"에 가깝습니다.
초보자가 자주 하는 실수도 여기서 나옵니다. top: 30px를 줬는데 안 움직이면, 가장 먼저 position이 static인지 확인해 보세요. 범인은 자주 그곳에 있습니다.
relative: 원래 자리는 남겨 두고 살짝 이동
relative는 요소를 원래 있던 자리 기준으로 움직입니다. 중요한 특징은 두 가지입니다.
첫째, 요소는 여전히 기본 흐름 안에 있습니다. 둘째, 화면에서 보이는 위치만 이동하고 원래 차지하던 자리는 그대로 남아 있습니다.12
.note {
position: relative;
top: 10px;
left: 20px;
}이 코드는 .note를 아래로 10px, 오른쪽으로 20px 이동시킵니다. 그런데 다른 요소들은 "저 요소가 원래 있던 자리"를 그대로 비워 둡니다. 그래서 상대 이동을 많이 주면 빈자리가 생긴 것처럼 보일 수 있습니다.
이 성질 때문에 relative는 두 가지 상황에서 정말 많이 씁니다. 하나는 요소를 조금만 다듬듯 움직일 때이고, 다른 하나는 절대 위치의 기준점을 만들 때입니다. 두 번째가 특히 중요합니다.
작은 이동 예제
<p>첫 번째 문장</p>
<p class="tag">중요</p>
<p>세 번째 문장</p>.tag {
position: relative;
top: 8px;
left: 12px;
background: gold;
}이 경우 중요 문장은 살짝 밀려나 보이지만, 원래 자리는 남아 있습니다. 그래서 주변 문장이 완전히 새로 정렬되지는 않습니다.
absolute: 흐름에서 빠져나와 기준점을 찾는다
absolute는 CSS 입문자에게 가장 많이 충격을 주는 값입니다. 이유는 간단합니다. 이 요소는 기본 흐름에서 빠져나오기 때문입니다. 즉, 다른 요소들은 이 요소가 없는 것처럼 배치됩니다.1
그리고 absolute는 자기 멋대로 떠다니는 것이 아니라, 가장 가까운 positioned 조상을 기준으로 위치를 잡습니다. 여기서 positioned 조상은 static이 아닌 relative, absolute, fixed, sticky 중 하나를 가진 조상입니다.1
.parent {
position: relative;
}
.badge {
position: absolute;
top: 0;
right: 0;
}이 코드에서 .badge는 .parent의 오른쪽 위에 붙습니다. 왜냐하면 .parent가 기준점이 되었기 때문입니다.
부모에 position: relative를 자주 주는 이유
초보자에게 가장 중요한 공식 하나를 꼽으라면 이겁니다.
부모를 기준으로 자식을 절대 배치하고 싶다면, 부모에 position: relative를 준다.
이 규칙을 기억하면 카드의 배지, 이미지 위의 아이콘, 모서리 버튼, 툴팁 같은 것들이 훨씬 쉬워집니다.
실전 예제: 카드 오른쪽 위에 배지 붙이기
<div class="card">
<span class="badge">NEW</span>
<h3>CSS 입문 강의</h3>
<p>position을 배우면 배치가 훨씬 편해집니다.</p>
</div>.card {
position: relative;
width: 320px;
padding: 24px;
border: 1px solid #cbd5e1;
border-radius: 16px;
background: white;
}
.badge {
position: absolute;
top: 12px;
right: 12px;
padding: 4px 8px;
background: tomato;
color: white;
border-radius: 999px;
font-size: 12px;
}이 예제에서 배지는 카드 내용 흐름을 밀어내지 않습니다. 그냥 카드 위에 얹혀 있는 것처럼 배치됩니다. 이 맛에 absolute를 씁니다.
왜 요소가 갑자기 화면 구석으로 날아갈까
부모에 기준점이 없기 때문입니다.
.parent {
/* position이 없음, 즉 static */
}
.child {
position: absolute;
top: 0;
right: 0;
}이 경우 .child는 가까운 positioned 조상을 찾지 못합니다. 그러면 더 바깥쪽 기준을 따라가며, 결국 페이지나 초기 기준 블록을 기준으로 배치될 수 있습니다.1
그래서 "분명 카드 안에 넣었는데 왜 페이지 오른쪽 위로 가지?"라는 일이 생깁니다. CSS가 장난치는 것이 아니라, 기준점을 못 찾은 것입니다.
fixed: 화면에 딱 붙어 있는 요소
fixed는 흐름에서 빠져나오며, 요소를 뷰포트, 즉 현재 보이는 브라우저 화면을 기준으로 배치합니다. 스크롤해도 자리가 바뀌지 않습니다.1
.help-button {
position: fixed;
right: 20px;
bottom: 20px;
}이 코드는 도움말 버튼을 화면 오른쪽 아래에 붙여 둡니다. 페이지를 내려도 버튼은 계속 그 자리에 있습니다.
자주 쓰는 예시는 다음과 같습니다.
상단 고정 헤더
우하단 채팅 버튼
맨 위로 가기 버튼
쿠키 안내 배너
실전 예제: 상단 고정 헤더
<header class="site-header">CSS 기초</header>
<main class="content">
<p>긴 본문...</p>
</main>.site-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 16px;
background: #111827;
color: white;
}
.content {
padding-top: 72px;
}여기서 핵심은 .content에 padding-top을 준 부분입니다. 고정 헤더는 흐름에서 빠졌기 때문에, 본문은 헤더가 없는 줄 알고 위로 올라옵니다. 그래서 여백을 직접 만들어 줘야 본문이 헤더에 가려지지 않습니다.
fixed를 쓸 때는 늘 한 번 더 생각해야 합니다. "이 요소가 다른 내용을 가리지 않을까?"라는 질문입니다.1
sticky: 평소엔 흐름에 있다가, 스크롤하면 붙는다
sticky는 이름 그대로 끈끈합니다. 처음에는 relative처럼 흐름 안에 있다가, 스크롤이 특정 지점에 도달하면 fixed처럼 붙어 보입니다. 다만 완전히 자유로운 고정은 아니고, 자신의 스크롤 컨테이너 안에서만 그렇게 동작합니다.13
.section-title {
position: sticky;
top: 0;
}이 코드는 제목이 평소에는 자연스럽게 있다가, 스크롤해서 화면 맨 위에 닿는 순간 그 자리에 붙게 만듭니다.
sticky가 안 되는 가장 흔한 이유
sticky는 생각보다 까다롭습니다. 특히 아래 두 가지를 꼭 기억하세요.
top,bottom,left,right중 적어도 하나를 지정해야 합니다.1가까운 조상 중 스크롤 기준이 바뀌는 요소가 있으면, 그 요소를 기준으로 동작합니다.1
즉, "왜 화면에 안 붙지?" 싶으면 top: 0이 있는지, 그리고 부모 쪽 overflow가 어떻게 되어 있는지 먼저 확인하면 됩니다.
실전 예제: 섹션 제목 고정
<div class="article-box">
<h2 class="sticky-title">HTML 기초</h2>
<p>아주 긴 내용...</p>
</div>.article-box {
height: 240px;
overflow: auto;
border: 1px solid #cbd5e1;
}
.sticky-title {
position: sticky;
top: 0;
margin: 0;
padding: 12px;
background: white;
border-bottom: 1px solid #e5e7eb;
}이제 .article-box 안을 스크롤하면 제목이 위에 붙어 있습니다. 마치 "여기 지금 HTML 섹션 읽는 중입니다" 하고 계속 알려 주는 느낌입니다.
다섯 가지를 한 번에 비교해 보기
같은 요소라도 position 값이 바뀌면 사고방식이 달라집니다.
static: 흐름대로 둔다.relative: 흐름 안에 두되, 보이는 위치만 조금 조정한다.absolute: 흐름에서 빼고, 가까운 기준점 안에서 자유롭게 놓는다.fixed: 화면 자체에 붙인다.sticky: 흐름 안에 있다가, 스크롤 상황에 따라 붙는다.
이 다섯 가지를 외우는 것보다 더 중요한 건 기준점과 흐름입니다.
"이 요소는 흐름 안에 있나?" "무엇을 기준으로 움직이나?"
이 두 질문만 습관처럼 던져도 배치 문제의 절반은 풀립니다.
top, right, bottom, left는 어떻게 읽어야 할까
이 네 속성은 "어느 방향으로 이동한다"보다 어느 가장자리에서 얼마나 떨어질지 정한다고 이해하는 편이 더 정확합니다.1
예를 들어:
.box {
position: absolute;
top: 20px;
right: 10px;
}이 코드는 "위에서 20px, 오른쪽에서 10px 떨어진 곳에 놓아라"라는 뜻입니다.
특히 top: 0; right: 0;은 아주 자주 등장합니다. "오른쪽 위 모서리에 딱 붙여라"라는 의미이기 때문입니다.
이제 겹침 문제: z-index
배치를 하다 보면 요소가 서로 겹칩니다. 이때 필요한 것이 z-index입니다. z-index는 요소가 앞에 올지, 뒤에 올지 정하는 속성입니다. 숫자가 클수록 더 앞에 보입니다.45
.box-a {
position: absolute;
z-index: 1;
}
.box-b {
position: absolute;
z-index: 2;
}이 경우 .box-b가 .box-a보다 앞에 보입니다.
초보자용으로 단순하게 말하면, z-index는 화면을 층층이 쌓인 투명 필름처럼 다루는 방법입니다. 숫자가 클수록 더 위에 놓입니다.5
z-index는 언제 동작할까
처음 배울 때는 position이 있는 요소에서 먼저 떠올리면 거의 맞습니다. relative, absolute, fixed, sticky와 함께 많이 쓰이기 때문입니다. 다만 이것이 전부는 아닙니다. flex item이나 grid item에는 position: static이어도 z-index가 적용될 수 있습니다.4
즉, 초보자 입장에서는 이렇게 기억하면 가장 안전합니다.
보통은
position이 있는 요소에서z-index를 쓴다.그런데 Flexbox나 Grid 안의 직계 자식은 예외적으로
z-index가 동작할 수 있다.
그래서 실전에서는 아래처럼 쓰는 경우가 많습니다.
.box {
position: relative;
z-index: 10;
}이 패턴은 여전히 가장 이해하기 쉽고 예측도 쉽습니다.
z-index가 없으면 누가 앞에 올까
겹치는 요소 둘 다 z-index를 따로 지정하지 않았다면, 보통 HTML에서 나중에 나온 요소가 위에 보이는 경우가 많습니다.45
이 규칙은 디버깅할 때 꽤 유용합니다. "숫자도 안 줬는데 왜 저게 위에 있지?" 싶으면 마크업 순서를 먼저 확인해 보세요.
음수 z-index도 있다
z-index는 음수도 가능합니다. 그러면 요소를 더 뒤로 보낼 수 있습니다.5
.background-shape {
position: absolute;
z-index: -1;
}장식용 배경 도형을 본문 뒤로 보내고 싶을 때 이런 패턴을 쓸 수 있습니다. 다만 너무 뒤로 보내면 아예 안 보이거나, 부모 배경 뒤로 숨는 것처럼 느껴질 수도 있으니 조심해서 써야 합니다.
그런데 왜 z-index: 9999가 안 먹을까
여기서 많은 사람이 CSS를 의심합니다. 하지만 진짜 원인은 대개 stacking context, 즉 겹침의 묶음 규칙입니다.4
쉽게 비유해 보겠습니다.
큰 서랍장이 두 개 있다고 생각해 봅시다. 왼쪽 서랍장 전체는 오른쪽 서랍장 뒤에 놓여 있습니다. 그런데 왼쪽 서랍장 안의 물건 하나에 "나는 9999번으로 제일 앞이야!"라고 써 붙여도, 서랍장 전체가 뒤에 있으면 오른쪽 서랍장 앞까지 튀어나오지 못합니다.
CSS도 비슷합니다. 어떤 부모 요소가 자기만의 겹침 문맥을 만들면, 그 안의 자식들은 그 부모 묶음 안에서만 경쟁합니다.4
이해용 예제
<div class="parent-a">
<div class="child-a">A의 자식</div>
</div>
<div class="box-b">B</div>.parent-a {
position: relative;
z-index: 1;
}
.child-a {
position: relative;
z-index: 999;
}
.box-b {
position: relative;
z-index: 2;
}여기서는 .child-a의 숫자가 999여도 .box-b보다 앞에 못 올 수 있습니다. 왜냐하면 .child-a는 .parent-a라는 묶음 안에 있고, 그 묶음 전체의 순서가 .box-b보다 뒤이기 때문입니다.4
이 지점이 바로 z-index가 무섭게 느껴지는 이유입니다. 숫자만 비교하는 것이 아니라, 어느 묶음에 속해 있는지도 함께 봐야 합니다.
초보자를 위한 z-index 사용 습관
처음에는 거대한 숫자를 던지기보다, 질서 있게 쓰는 습관이 훨씬 좋습니다.
기본 콘텐츠:
z-index: 1고정 헤더:
z-index: 10드롭다운:
z-index: 20모달:
z-index: 100
핵심은 숫자의 절대값이 아니라 프로젝트 안에서의 상대적 순서입니다. 999999를 던지는 습관은 잠깐은 시원하지만, 나중에는 더 큰 혼란을 부르기 쉽습니다.
position과 z-index를 함께 쓰는 대표 패턴
이제 실무에서 자주 만나는 장면으로 묶어 보겠습니다.
패턴 1: 카드 모서리 배지
부모 카드에 position: relative, 배지에 position: absolute를 줍니다. 필요하면 z-index로 배지를 카드 내용보다 앞에 둡니다.
.card {
position: relative;
}
.badge {
position: absolute;
top: 8px;
right: 8px;
z-index: 2;
}패턴 2: 항상 보이는 상단 헤더
헤더에 position: fixed와 적절한 z-index를 줍니다. 그리고 본문에 상단 여백을 만들어 헤더와 겹치지 않게 합니다.
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 10;
}
main {
padding-top: 64px;
}패턴 3: 스크롤 중 붙는 제목
제목에 position: sticky와 top: 0을 줍니다. 내용과 겹치지 않도록 배경색을 주는 것도 중요합니다.
.title {
position: sticky;
top: 0;
background: white;
z-index: 1;
}배경색이 없으면 아래 내용이 비쳐 보여 "붙긴 붙었는데 좀 지저분한" 결과가 나올 수 있습니다.
position으로 전체 레이아웃을 짜려고 하면 왜 힘들까
position은 아주 강력하지만, 만능 레이아웃 도구는 아닙니다.
페이지 전체를 absolute로만 배치하려 하면 화면 크기가 바뀔 때 금방 무너지기 쉽습니다. 요소들이 서로의 공간을 고려하지 않기 때문입니다. 그래서 큰 구조는 보통 기본 흐름, Flexbox, Grid 같은 레이아웃 도구로 만들고, position은 그 위에서 특정 요소를 정확히 얹는 역할로 쓰는 편이 안정적입니다.
쉽게 말해, 집의 뼈대는 display와 레이아웃 도구가 만들고, position은 액자, 스티커, 간판, 고정 버튼처럼 세밀한 배치를 담당합니다.
레이아웃이 무너질 때 확인할 체크리스트
배치가 이상하면 감으로 고치기보다 순서대로 확인하는 편이 훨씬 빠릅니다.
이 요소의
position값이 무엇인가?top,left같은 오프셋이 실제로 적용되는 상태인가?absolute요소라면 기준이 되는 부모에position이 있는가?fixed나sticky요소가 다른 내용을 가리고 있지 않은가?z-index가 안 먹는다면 다른 stacking context 안에 갇혀 있지 않은가?Flexbox나 Grid 안의 직계 자식인지도 확인했는가?
sticky가 안 된다면top값과 부모의overflow를 확인했는가?
이 체크리스트만 잘 써도 "왜 안 되지?"가 "아, 여기였네"로 바뀌는 속도가 꽤 빨라집니다.
연습 예제: 배치를 한 번에 복습해 보기
아래 예제는 카드 안 배지, 고정 버튼, sticky 제목을 한 화면에 모아 둔 작은 연습장입니다.
<div class="demo-page">
<button class="help">도움말</button>
<section class="panel">
<h2 class="panel-title">배치 연습</h2>
<div class="product-card">
<span class="sale-badge">SALE</span>
<img src="https://via.placeholder.com/320x180" alt="예시 상품 이미지">
<h3>무지 재밌는 CSS 노트</h3>
<p>position을 이해하면 배치가 덜 무섭습니다.</p>
</div>
<p>스크롤용 텍스트...</p>
<p>스크롤용 텍스트...</p>
<p>스크롤용 텍스트...</p>
<p>스크롤용 텍스트...</p>
<p>스크롤용 텍스트...</p>
</section>
</div>body {
margin: 0;
font-family: sans-serif;
}
.help {
position: fixed;
right: 20px;
bottom: 20px;
padding: 12px 16px;
border: none;
border-radius: 999px;
background: #2563eb;
color: white;
z-index: 20;
}
.panel {
max-width: 720px;
height: 320px;
margin: 40px auto;
padding: 0 16px 24px;
overflow: auto;
border: 1px solid #cbd5e1;
border-radius: 16px;
}
.panel-title {
position: sticky;
top: 0;
margin: 0 -16px 16px;
padding: 16px;
background: white;
border-bottom: 1px solid #e5e7eb;
z-index: 5;
}
.product-card {
position: relative;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 16px;
background: #f8fafc;
}
.product-card img {
width: 100%;
border-radius: 12px;
}
.sale-badge {
position: absolute;
top: 12px;
right: 12px;
padding: 6px 10px;
background: crimson;
color: white;
border-radius: 999px;
z-index: 2;
}이 예제에서 확인할 수 있는 포인트는 세 가지입니다.
첫째, SALE 배지는 카드 안에서 absolute로 움직입니다. 카드에 relative가 없으면 제멋대로 튀어 버릴 수 있습니다.
둘째, 배치 연습 제목은 패널 안을 스크롤할 때 위에 붙습니다. sticky와 top: 0의 조합입니다.
셋째, 도움말 버튼은 화면 오른쪽 아래에 계속 붙어 있습니다. fixed의 대표 장면입니다.
오늘 배운 내용을 한 문장씩 정리하면
static은 기본 흐름입니다.
relative는 원래 자리를 남겨 둔 채 살짝 움직입니다.
absolute는 흐름에서 빠지고, 가장 가까운 positioned 조상을 기준으로 배치됩니다.
fixed는 화면에 붙습니다.
sticky는 스크롤하다가 붙습니다.
z-index는 겹칠 때 앞뒤 순서를 정하지만, 숫자만이 아니라 어느 묶음 안에 있는지도 함께 봐야 합니다.
배치가 어렵게 느껴질 때는 속성 이름을 외우려 하지 말고, 이렇게 질문해 보세요.
"이 요소는 지금 흐름 안에 있나?" "무엇을 기준으로 움직이나?" "겹친다면 누구의 층이 더 앞인가?"
이 세 질문에 답할 수 있으면 position은 더 이상 무서운 마법이 아니라, 꽤 믿음직한 도구가 됩니다.
다음 장에서는 이 배치 감각을 바탕으로, 실무에서 가장 많이 쓰는 레이아웃 도구 중 하나인 Flexbox를 만나 보겠습니다. 줄 세우기 실력이 생기면, 이제는 줄 정리 실력까지 붙게 됩니다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.