CSS Stacking Context — z-index: 99999를 줬는데 왜 안 올라올까
CSS 쌓임 맥락의 생성 조건, z-index의 실제 작동 원리, 그리고 레이아웃 버그를 디버깅하는 실전 전략을 정리한다.
z-index: 99999를 줬는데 요소가 올라오지 않는다. 값을 999999로 올려도 마찬가지다. 이 상황에서 대부분 숫자를 더 키우거나, !important를 붙이거나, HTML 구조를 뒤집기 시작한다. 하지만 문제의 원인은 숫자의 크기가 아니다. 쌓임 맥락(Stacking Context)을 이해하지 못했기 때문이다.
쌓임 맥락이란
브라우저는 HTML 요소들을 2차원 평면에 배치하는 것처럼 보이지만, 실제로는 z축이라는 깊이 차원도 관리한다. 쌓임 맥락은 이 z축 위에서 요소들이 어떤 순서로 겹치는지를 결정하는 독립적인 레이어 그룹이다.
비유하면, 쌓임 맥락은 봉투와 같다. 봉투 안에 여러 장의 종이(자식 요소)가 들어 있고, 종이들 사이의 순서는 봉투 내부에서만 유효하다. 아무리 봉투 안의 종이에 "나는 맨 위야!"라고 적어놔도, 봉투 자체가 다른 봉투 아래에 있으면 절대 위로 올라올 수 없다.
.parent-a {
position: relative;
z-index: 1;
}
.parent-b {
position: relative;
z-index: 2;
}
.child-of-a {
position: absolute;
z-index: 99999;
}.child-of-a의 z-index가 99999여도, .parent-a의 z-index가 1이므로 .parent-b(z-index: 2) 아래에 머문다. z-index는 같은 쌓임 맥락 안에서만 비교된다.
[💡 잠깐! 이 용어는?] 쌓임 맥락(Stacking Context): z축 위에서 요소들의 렌더링 순서를 결정하는 독립적인 그룹. 쌓임 맥락이 생성되면 그 안의 모든 자식 요소는 해당 맥락 내에서만 z-index로 순서가 매겨진다.
쌓임 맥락을 생성하는 조건들
z-index를 명시적으로 설정했을 때만 생기는 게 아니다. 의외로 많은 CSS 속성이 암묵적으로 쌓임 맥락을 만든다. 이게 버그의 주범이다.
/* 1. 기본 — position + z-index */
.context-1 {
position: relative;
z-index: 1;
}
/* 2. opacity가 1 미만이면 생성 */
.context-2 {
opacity: 0.99;
}
/* 3. transform 값이 있으면 생성 */
.context-3 {
transform: translateZ(0);
}
/* 4. 의도적 생성 전용 속성 */
.context-4 {
isolation: isolate;
}
/* 5. will-change도 생성 */
.context-5 {
will-change: transform;
}
/* 6. filter도 생성 */
.context-6 {
filter: blur(0);
}전체 조건 목록
| 조건 | 설명 |
|---|---|
position: relative/absolute + z-index 값 지정 | 가장 기본적인 생성 방식 |
position: fixed 또는 sticky | z-index 없어도 생성 |
opacity < 1 | opacity: 0.99만으로도 생성 |
transform 값 존재 | transform: none이 아닌 모든 값 |
filter 값 존재 | filter: blur(0)도 포함 |
will-change | 특정 속성을 명시하면 생성 |
isolation: isolate | 의도적 생성 전용 속성 |
contain: layout 또는 paint | 컨테인먼트 관련 |
Flex/Grid 자식 + z-index | position 없이도 작동 |
mix-blend-mode 값 존재 | normal이 아닌 값 |
clip-path, mask | 클리핑/마스킹 관련 |
주의할 점은 Flex/Grid 자식 요소다. 일반적으로 z-index가 작동하려면 position이 static이 아니어야 하지만, flex 또는 grid 컨테이너의 자식이라면 position: relative 없이도 z-index가 동작하고 쌓임 맥락이 생성된다.
[💡 잠깐! 이 용어는?]
isolation: isolate: 쌓임 맥락을 의도적으로 생성하기 위한 CSS 속성. 다른 부수 효과 없이 오직 쌓임 맥락만 만든다. opacity: 0.99나 transform: translateZ(0) 같은 핵(hack) 대신 사용하는 정식 방법.
쌓임 순서 규칙
같은 쌓임 맥락 안에서 요소들은 다음 순서로 쌓인다(아래부터 위로):
1. 쌓임 맥락의 배경과 테두리
2. z-index가 음수인 자식
3. in-flow, non-positioned 블록 요소
4. non-positioned 플로팅 요소
5. in-flow, non-positioned 인라인 요소
6. z-index: 0 또는 z-index: auto인 positioned 요소
7. z-index가 양수인 자식"왜 z-index: 0을 준 요소가 z-index를 안 준 요소보다 위에 있지?"라고 헷갈릴 수 있다. positioned 요소(z-index: auto 포함)는 non-positioned 요소보다 항상 위에 쌓인다.
실전 디버깅 전략
1단계: 쌓임 맥락 트리 파악하기
브라우저 DevTools에서 해당 요소의 부모 체인을 추적하며, 어떤 요소가 쌓임 맥락을 생성하고 있는지 확인한다.
.modal-overlay {
position: fixed;
z-index: 1000;
}
.header {
position: sticky;
top: 0;
z-index: 100;
}
.sidebar {
transform: translateX(0);
/* ← 이 한 줄이 쌓임 맥락을 만든다 */
}이 예시에서 .sidebar는 transform 때문에 쌓임 맥락이 생성된다. .sidebar 안의 드롭다운에 아무리 높은 z-index를 줘도, .sidebar의 쌓임 맥락 밖으로 나갈 수 없다.
2단계: isolation: isolate 활용하기
의도적으로 쌓임 맥락의 경계를 만들 때는 isolation: isolate를 사용한다. 마치 칸막이를 세워서 "여기서부터는 별도 구역이다"라고 선언하는 것과 같다.
.card {
isolation: isolate;
}
.card__badge {
position: absolute;
z-index: 1;
}
.card__image {
position: relative;
z-index: 0;
}.card에 isolation: isolate를 주면, .card__badge의 z-index는 카드 내부에서만 유효하다. 다른 카드나 외부 요소의 z-index와 충돌하지 않는다.
[💡 잠깐! 이 용어는?] will-change: 브라우저에 "이 속성이 곧 변할 것이니 미리 최적화해두라"고 힌트를 주는 CSS 속성. 성능 최적화 목적이지만, 부작용으로 쌓임 맥락을 생성한다. 남용하면 오히려 메모리 사용량이 증가한다.
z-index 디버깅 체크리스트
| 증상 | 원인 | 해결책 |
|---|---|---|
| z-index를 올려도 안 올라옴 | 부모의 쌓임 맥락이 낮음 | 부모의 z-index 확인/조정 |
| opacity 애니메이션 후 레이어 꼬임 | opacity < 1이 새 맥락 생성 | animation 종료 후 opacity 제거 |
| transform 적용 후 드롭다운 잘림 | transform이 새 맥락 생성 | isolation: isolate로 구조 분리 |
| 모달이 헤더 아래에 깔림 | 모달과 헤더가 다른 맥락 | 같은 쌓임 맥락에서 비교되도록 구조 변경 |
| Flex 자식의 z-index가 의도대로 안 됨 | Flex 자식은 position 없이 맥락 생성 | 명시적으로 position: relative 추가 |
가장 확실한 해결 전략은 "z-index 숫자를 키우는 것"이 아니라, 쌓임 맥락의 구조를 제어하는 것이다.
마무리
z-index 문제의 99%는 숫자가 작아서가 아니라, 쌓임 맥락의 구조를 파악하지 못해서 발생한다.
- 봉투 안의 종이가 아무리 높은 번호표를 가져도, 봉투 자체의 순서를 바꾸지 않으면 소용없다
isolation: isolate를 적극 활용해 의도적으로 쌓임 맥락의 경계를 관리하자- DevTools에서 어떤 속성이 암묵적으로 쌓임 맥락을 생성하는지 추적하는 습관을 들이면, z-index 전쟁에서 벗어날 수 있다
참고:
- MDN Stacking Context: https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Positioned_layout/Stacking_context
- Josh W. Comeau: https://www.joshwcomeau.com/css/stacking-contexts/
같은 카테고리 · CSS
비슷한 주제의 최신 글
태그가 겹치는 글
공통 태그가 많을수록 위에 보인다