2025-09-22 00:50
-
z-index는 단순히 숫자를 부여하는 것이 아니라 ‘쌓임 맥락(Stacking Context)‘이라는 독립된 층 안에서 작동하는 원리.
-
z-index가 효과를 발휘하려면 반드시position속성이static이 아니어야 하며, 부모 요소의 쌓임 맥락에 종속. -
새로운 쌓임 맥락은
position과z-index,opacity,transform등의 특정 속성 조합으로 생성되며,isolation: isolate로 명시적으로 생성 가능.
CSS Z-index 완벽 정복 핸드북 쌓임 맥락의 비밀
웹 페이지를 만들다 보면 요소들이 서로 겹치는 상황이 비일비재하다. 이때 어떤 요소를 위로, 어떤 요소를 아래로 보낼지 결정해야 하는데, 많은 개발자가 z-index: 9999; 같은 마법의 숫자에 의존하곤 한다. 하지만 이 방법은 종종 예상치 못한 결과를 낳으며 우리를 좌절시킨다. z-index가 왜 마음대로 작동하지 않는 것처럼 보일까? 그 이유는 z-index의 핵심 원리인 **‘쌓임 맥락(Stacking Context)‘**을 이해하지 못했기 때문이다.
이 핸드북은 z-index가 단순히 숫자를 비교하는 속성이 아님을 명확히 알려준다. z-index가 탄생한 배경부터, 그 작동의 근간이 되는 쌓임 맥락의 개념, 그리고 실제 현업에서 마주하는 문제들을 해결하는 실용적인 팁과 고급 테크닉까지, z-index에 대한 모든 것을 총망라했다. 이 글을 끝까지 읽는다면 더 이상 z-index 때문에 골머리를 앓는 일은 없을 것이다. 🧑💻
1. 만들어진 이유 Z축의 등장 배경
초기 웹은 문서(Document)를 보여주는 것이 주된 목적이었다. 텍스트와 이미지가 위에서 아래로, 왼쪽에서 오른쪽으로 흐르는 2차원적인 세상이었다. 하지만 웹 디자인이 발전하면서 디자이너들은 더 풍부하고 동적인 레이아웃을 원하기 시작했다. 모달 창, 드롭다운 메뉴, 툴팁처럼 기존 콘텐츠 위에 새로운 정보를 띄워야 하는 요구가 생겨난 것이다.
이러한 요구를 해결하기 위해 CSS에 position 속성이 도입되었다. position: absolute;나 position: fixed;를 사용하면 요소를 문서의 자연스러운 흐름에서 벗어나 원하는 위치에 자유롭게 배치할 수 있게 되었다.
그런데 문제가 발생했다. 여러 요소를 자유롭게 배치하다 보니 서로 겹치는 일이 빈번해진 것이다. 마치 책상 위에 여러 장의 서류를 아무렇게나 던져놓은 것처럼, 어떤 서류가 맨 위에 와야 할지 순서를 정할 규칙이 필요했다.
여기서 바로 Z축(Z-axis) 개념이 등장한다. 우리가 화면을 바라볼 때, 가로축을 X축, 세로축을 Y축이라고 한다면, 화면에서 사용자 쪽으로 돌출되는 깊이 축을 Z축이라고 할 수 있다. z-index는 바로 이 Z축의 순서, 즉 요소가 쌓이는 순서(stacking order)를 제어하기 위해 만들어진 속성이다. z-index를 통해 개발자는 어떤 요소가 다른 요소보다 ‘앞으로’ 나올지 명시적으로 지정할 수 있게 되었다.
비유로 이해하기 🏙️
z-index를 도시의 건물 높이라고 생각해보자. 기본적으로 모든 건물(요소)은 땅(HTML) 위에 지어진다. z-index 값이 높을수록 더 높은 건물처럼 다른 건물들을 가리고 위에서 보이게 된다. 하지만 여기서 중요한 점은, 모든 건물이 같은 땅 위에 있는 것은 아니라는 것이다. 이 ‘땅’의 개념이 바로 다음에 설명할 ‘쌓임 맥락’이다.
2. 핵심 개념 쌓임 맥락 Stacking Context
z-index가 왜 예상대로 동작하지 않는지 이해하려면, 쌓임 맥락(Stacking Context)이라는 개념을 반드시 알아야 한다. 이것이 z-index의 모든 것을 지배하는 가장 중요한 규칙이다.
쌓임 맥락이란, HTML 요소들이 쌓이는 순서를 결정하는 독립적인 ‘작은 세계’ 또는 ‘레이어 그룹’이다. 하나의 쌓임 맥락 안에 있는 요소들은 그 안에서만 z-index 값을 서로 비교하며 순서가 정해진다. 외부의 다른 쌓임 맥락에 있는 요소들과는 직접적으로 z-index를 비교하지 않는다.
비유로 이해하기 🃏
카드 덱 여러 개를 상상해보자. 각 카드 덱이 하나의 ‘쌓임 맥락’이다.
A 덱: 스페이드 킹(z-index: 13), 스페이드 2(z-index: 2)
B 덱: 하트 5(z-index: 5), 하트 3(z-index: 3)
A 덱 안에서는 스페이드 킹이 스페이드 2보다 항상 위에 있다. B 덱 안에서는 하트 5가 하트 3보다 항상 위에 있다.
여기서 중요한 점은, A 덱 전체가 B 덱 위에 놓인다면, A 덱에서 가장 낮은 카드(스페이드 2)조차 B 덱에서 가장 높은 카드(하트 5)보다 위에 보인다는 것이다. 즉,
z-index: 2인 스페이드 2가z-index: 5인 하트 5를 가리게 된다.이처럼, 요소의 쌓임 순서는 자신이 속한 쌓임 맥락(카드 덱)의 순서에 먼저 종속되고, 그 다음에야 자신의
z-index값이 의미를 가진다.z-index: 9999를 부여해도 소용없었던 이유는, 그 요소가 속한 ‘덱’ 자체가 다른 ‘덱’의 아래에 깔려 있었기 때문이다.
쌓임 맥락은 언제 생성되는가?
모든 요소가 쌓임 맥락을 만드는 것은 아니다. 특정 CSS 속성과 값이 적용될 때 새로운 쌓임 맥락이 형성된다.
-
문서의 루트 요소 (
<html>): 웹 페이지 자체가 하나의 거대한 기본 쌓임 맥락이다. -
position이static이 아니면서z-index가auto가 아닌 요소: 가장 흔한 경우다.position: relative | absolute | fixed | sticky와 함께z-index에 숫자 값을 부여하면 새로운 쌓임 맥락이 생긴다. -
opacity가 1보다 작은 요소: 투명도가 적용된 요소는 그래픽적으로 분리된 레이어로 처리되므로 새로운 쌓임 맥락을 만든다. -
transform,filter,clip-path,mask등 특정 속성이none이 아닌 요소: GPU 가속을 위해 별도의 레이어로 렌더링되는 경우가 많아 새로운 쌓임 맥락을 형성한다. -
isolation: isolate가 적용된 요소: 이 속성은 명시적으로 새로운 쌓임 맥락을 생성하기 위해 만들어졌다. (심화 내용에서 자세히 다룸) -
기타:
mix-blend-mode,will-change등 다른 여러 속성도 쌓임 맥락을 생성할 수 있다.
이 조건들을 기억하는 것이 중요하다. 부모 요소에 자신도 모르게 opacity: 0.99가 적용되어 있다면, 그 자식 요소는 아무리 높은 z-index를 가져도 부모의 쌓임 맥락을 벗어날 수 없다.
3. 구조와 작동 원리
이제 쌓임 맥락을 이해했으니, 하나의 쌓임 맥락 안에서 요소들이 어떤 순서로 렌더링되는지 자세히 살펴보자.
z-index 속성 값
-
auto(기본값): 새로운 쌓임 맥락을 만들지 않는다. 쌓임 순서는 부모와 동일한 레벨에서 결정된다. -
<integer>(정수): 양수, 음수, 0을 사용할 수 있다. 이 값을 부여하면 (position이 static이 아닐 경우) 새로운 쌓임 맥락을 생성하며, 해당 맥락 내에서 쌓임 순서를 결정한다. 숫자가 클수록 위로 올라온다.
작동 조건: Position은 필수!
가장 기본적이고 중요한 규칙이다. z-index는 position 속성이 static(기본값)이 아닌 요소에만 적용된다. position이 relative, absolute, fixed, sticky 중 하나여야 한다.
CSS
.box {
/* position 속성이 없으므로 아래 z-index는 무시된다. */
z-index: 100;
}
.positioned-box {
/* position이 static이 아니므로 z-index가 작동한다. */
position: relative;
z-index: 100;
}
쌓임 순서의 7가지 규칙 (The Stacking Order)
하나의 쌓임 맥락 안에서 요소들은 다음과 같은 정해진 순서대로 그려진다(paint). 이 순서는 절대적이다.
-
쌓임 맥락의 루트 요소 (1층)
- 현재 쌓임 맥락을 형성하는 요소 그 자체. 가장 아래에 깔린다.
-
z-index가 음수인 요소 (지하층)z-index값이 음수인 자식 요소들. 숫자가 작을수록 더 아래에 쌓인다 (예:z-index: -1이z-index: -2보다 위에 있다).
-
position이 없는 블록 요소 (지상층: 건물 골조)position: static인 일반적인 블록 요소들 (div,p등). HTML 코드에 나타난 순서대로 쌓인다.
-
position이 없는 부유 요소(float) (지상층: 창문)float속성이 적용된 요소들.
-
position이 없는 인라인 요소 (지상층: 인테리어)- 일반적인 텍스트나
span태그 같은 인라인 요소들.
- 일반적인 텍스트나
-
z-index가auto또는0인 요소 (고층부: 낮은 층)position이 지정되었지만z-index가auto이거나0인 요소들. HTML 코드 순서대로 쌓인다.
-
z-index가 양수인 요소 (고층부: 높은 층)z-index값이 양수인 자식 요소들. 숫자가 클수록 더 위에 쌓인다 (예:z-index: 2가z-index: 1보다 위에 있다).
이 순서를 이해하면 왜 position: relative만 적용했는데도 다른 요소들 위로 뜨는지, z-index를 주지 않았는데도 순서가 바뀌는지 등을 명확하게 설명할 수 있다.
4. 사용법 실전 예제와 팁
이론을 알았으니 이제 실전이다. 흔히 겪는 문제와 해결책을 통해 z-index를 제대로 활용하는 법을 알아보자.
기본 사용법: 겹치는 요소 순서 정하기
가장 기본적인 시나리오. 두 개의 상자가 겹쳐 있을 때 순서를 바꿔보자.
HTML
<div class="parent">
<div class="box red">Red Box (z-index: 1)</div>
<div class="box blue">Blue Box (z-index: 2)</div>
</div>
CSS
.parent {
position: relative;
height: 200px;
}
.box {
position: absolute;
width: 150px;
height: 150px;
}
.red {
background-color: red;
top: 20px;
left: 20px;
z-index: 1; /* z-index가 낮음 */
}
.blue {
background-color: blue;
top: 50px;
left: 50px;
z-index: 2; /* z-index가 높음 -> 위로 올라옴 */
}
이 경우, .parent 요소 안에서 .red와 .blue는 같은 쌓임 맥락에 속한다. 따라서 z-index 값이 더 높은 파란색 상자가 빨간색 상자 위로 올라온다.
흔한 실수와 해결책 🧐
실수 1: position 속성 없이 z-index 사용
CSS
.modal-backdrop {
/* position이 없어서 z-index가 작동하지 않음! */
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
}
-
문제점:
position이static인 요소에z-index를 적용하면 아무 일도 일어나지 않는다. -
해결책:
z-index를 적용할 요소에position: relative;,position: absolute;등을 반드시 추가한다.
실수 2: 쌓임 맥락 무시하기 (The “z-index: 9999” Problem)
가장 빈번하게 개발자들을 괴롭히는 문제다.
HTML
<header style="position: relative; z-index: 10;">
</header>
<main>
<div class="some-wrapper" style="opacity: 0.99;">
<div class="modal" style="position: fixed; z-index: 9999;">
나는 왜 헤더 밑에 가려질까? 😭
</div>
</div>
</main>
-
문제점: 모달 창(
.modal)에 엄청나게 높은z-index: 9999를 주었지만,z-index: 10인 헤더(header)보다 아래에 표시된다. -
원인: 모달을 감싸고 있는
.some-wrapper에opacity: 0.99가 적용되어 있다. 이로 인해.some-wrapper는 새로운 쌓임 맥락을 생성한다. 모달의z-index: 9999는 이 새로운 쌓임 맥락 안에서만 유효하다. 한편,header는 루트 쌓임 맥락에서z-index: 10을 가진다. 웹 페이지는 두 개의 ‘카드 덱’(.header덱과.some-wrapper덱)을 비교하는데,.some-wrapper는z-index가auto(0으로 취급)이므로z-index: 10인 헤더보다 아래에 위치한다. 따라서 그 안의 내용물인 모달도 헤더 아래에 깔리게 된다. -
해결책:
-
구조 변경: 모달 요소를 쌓임 맥락을 생성하는 부모(
.some-wrapper) 바깥으로, 보통은<body>바로 아래로 옮긴다. 이것이 가장 근본적이고 권장되는 해결책이다. -
부모의 쌓임 맥락 제거:
.some-wrapper에서 쌓임 맥락을 생성하는 원인(opacity: 0.99)을 제거한다. 하지만 이는 디자인 요구사항에 어긋날 수 있다. -
부모에
z-index부여:.some-wrapper에도position과 높은z-index를 부여하여 헤더 위로 올린다. 하지만 이는 다른 레이아웃 문제를 야기할 수 있다.
-
모범 사례 (Best Practices)
-
z-index값을 최소화하고 중앙에서 관리하라.-
z-index: 9999같은 큰 숫자를 남발하지 않는다. 큰 숫자는 다른 개발자가 더 큰 숫자를 쓰게 만드는 ‘z-index 전쟁’을 유발한다. -
애플리케이션의 레이어 계층을 미리 설계하고, 용도에 따라
z-index값을 변수로 관리하는 것이 좋다.
SCSS
/* SCSS/SASS 변수 사용 예시 */ $z-index-backdrop: 10; $z-index-dropdown: 20; $z-index-modal: 100; $z-index-tooltip: 110; .modal { z-index: $z-index-modal; } -
-
꼭 필요한 경우에만 쌓임 맥락을 생성하라.
- 불필요한
opacity,transform등의 사용이 의도치 않은 쌓임 맥락을 만들지 않는지 확인한다.
- 불필요한
-
isolation속성을 적극 활용하라.- 다른 부작용 없이 오직 쌓임 맥락 생성을 위해
isolation: isolate를 사용하는 것이 명시적이고 안전하다. (아래 심화 내용 참고)
- 다른 부작용 없이 오직 쌓임 맥락 생성을 위해
5. 심화 내용 고급 테크닉과 관련 속성
isolation: isolate - 명시적인 쌓임 맥락 생성
CSS isolation 속성은 요소가 다른 요소들과 섞이는(blending) 것을 제어한다. isolation: isolate; 값은 다른 CSS 속성의 부작용 없이, 오직 새로운 쌓임 맥락을 만들기 위한 목적으로 사용할 수 있다.
CSS
.card-component {
isolation: isolate; /* 이 요소는 이제 독립적인 쌓임 맥락을 가짐 */
background-color: white;
}
.card-component .badge {
position: absolute;
top: -10px;
left: -10px;
z-index: -1; /* 부모인 .card-component의 배경(white) 밑으로 들어감 */
}
위 예제에서 .badge는 z-index: -1을 가지고 있다. 만약 .card-component에 쌓임 맥락이 없다면, .badge는 페이지 전체의 배경 밑으로 사라져 버릴 것이다. 하지만 isolation: isolate가 .card-component를 새로운 쌓임 맥락으로 만들었기 때문에, z-index: -1은 이 새로운 맥락의 루트(카드 배경)를 기준으로 계산된다. 즉, 카드 배경 바로 밑에 위치하게 되어 의도한 디자인을 구현할 수 있다.
opacity나 transform을 이용해 쌓임 맥락을 만드는 ‘꼼수’ 대신 isolation을 사용하면 코드의 의도가 명확해지고 유지보수가 쉬워진다.
z-index와 렌더링 성능
브라우저는 요소를 렌더링할 때 레이어(Layer) 단위로 작업을 최적화한다. 새로운 쌓임 맥락을 생성하는 요소는 종종 별도의 그래픽 레이어로 분리된다.
-
장점: 특정 요소가 변경될 때 전체 페이지를 다시 그리는(repaint) 대신 해당 레이어만 다시 그리면 되므로 성능에 이점을 줄 수 있다 (예: 애니메이션).
-
단점: 너무 많은 레이어를 생성하면 메모리 사용량이 증가하고, 레이어를 합성(composite)하는 과정에서 오히려 성능이 저하될 수 있다.
대부분의 경우 z-index로 인한 성능 문제는 미미하지만, 수백, 수천 개의 요소에 동적으로 쌓임 맥락을 생성하는 복잡한 애플리케이션에서는 성능 영향을 고려할 필요가 있다.
6. 결론 쌓임 맥락을 지배하는 자가 레이아웃을 지배한다
z-index는 결코 어렵거나 변덕스러운 속성이 아니다. 그저 우리가 그 이면에 있는 **‘쌓임 맥락’**이라는 규칙을 이해하지 못했을 뿐이다.
이 핸드북을 통해 우리는 다음의 핵심 사항들을 배웠다.
-
z-index는position: static이 아닌 요소에만 작동한다. -
z-index의 진짜 힘은 ‘쌓임 맥락’이라는 독립된 세계 안에서 발휘된다. -
한 요소의 최종 쌓임 순서는 그 요소가 속한 쌓임 맥락의 순서에 의해 결정된다.
-
쌓임 맥락은
position/z-index뿐만 아니라opacity,transform,isolation등 다양한 속성으로 생성될 수 있다.
이제부터 z-index가 말썽을 부릴 때, 무작정 숫자를 높이는 대신 한 걸음 물러서서 개발자 도구를 열어보자. 어떤 요소가 새로운 쌓임 맥락을 만들고 있는지, 내가 제어하려는 요소가 어떤 쌓임 맥락에 갇혀 있는지 차분히 분석하면 문제는 생각보다 쉽게 풀릴 것이다. 쌓임 맥락을 이해하고 제어하는 능력은 복잡한 웹 UI를 자신감 있게 구축하는 데 필수적인 무기가 되어줄 것이다.