본문 바로가기
HTML

z-index에 오류가 있을 때

by 프로랑 2023. 10. 20.
728x90

실제로 z-index를 쓰다 보면 종종 마음대로 동작하지 않을 때가 있는데요.

이럴 때 도움이 되는 쌓임 맥락(Stacking Context)이라는 개념에 대해 알아보겠습니다.

처음에는 조금 복잡하게 느껴질 수도 있으니까, 일단 쌓임 맥락의 큰 개념만이라도 잡고 넘어가는 걸 목표로 해 봅시다.

z-index: 9999로도 해결이 안 되는 이유

빨강, 초록, 파랑 사각형 세 개를 배치했는데, 원하는 대로 배치가 안 되는 경우를 한번 예를 들어 보겠습니다. 아래 코드를 천천히 읽어 보고 어떤 게 앞에 보이고 어떤게 뒤에 보일지 한번 예상해 보세요. 일부러 여백을 좀 두었으니 스크롤을 천천히 내리면서 생각해 봅시다. 종이랑 펜을 가지고 생각한 모습을 그려 보셔도 좋습니다.

<div class="red">
  <div class="green"></div>
</div>
<div class="blue"></div>
.red {
  background-color: #e46e80;
  position: absolute;
  width: 100px;
  height: 100px;
  top: 100px;
  left: 100px;
  z-index: 1;
}

.green {
  background-color: #32b9c1;
  position: absolute;
  width: 50px;
  height: 50px;
  top: 25px;
  left: 25px;
  z-index: 3;
}

.blue {
  background-color: #5195ee;
  position: absolute;
  width: 100px;
  height: 100px;
  top: 150px;
  left: 150px;
  z-index: 2;
}

.red, .green, .blue는 모두 absolute 포지션이고, z-index가 1, 3, 2니까 초록, 파랑, 빨강 순서로 앞에 보일 거 같습니다. 하지만 이 코드의 실행 결과는 의외인데요.

z-index: 2인 .blue가 z-index: 3인 .green 보다 앞 쪽에 보입니다. 분명 z-index 값이 크면 앞 쪽에 보인다고 배웠는데, 이러니까 이상합니다. 심지어 z-index: 3을 z-index: 9999로 바꿔도 안 됩니다. 이렇게 되는 이유는 z-index 값은 단순히 서로 비교만 하는 게 아니라, 쌓임 맥락(Stacking Context) 안에서 비교하기 때문인데요.

 

쌓임 맥락

쌓임 맥락을 간단히 설명하자면, z-index를 묶어서 생각하는 범위라고 할 수 있는데요. 앞에서 코드로 살펴본 예시를 가지고 다시 생각해보죠.

<body> 태그 아래에 이렇게 .red, .green 그리고 .blue가 배치되어 있습니다. 그리고 각각 z-index 값이 있죠. 여기서 .green 태그의 위치를 잘 보세요. .red 태그의 자식인데요. 마침 조건이 맞아서 .red에는 쌓임 맥락이 만들어진 상태입니다.

.red 태그에서 쌓임 맥락이 만들어졌기 때문에 .red의 모든 자손 태그들은 (빗금 친 영역) 마치 .red와 마찬가지로 z-index: 1로 묶어서 생각할 수 있습니다. 그래서 항상 .blue 뒤쪽에 배치되는 거죠.

심지어 .green에서 z-index: 9999라고 하더라도, 바깥에서 보면 쌓임 맥락 때문에 z-index: 1과 마찬가지로 처리해서, 아무런 효과가 없는 것입니다. 왜 이런 식으로 동작하는 걸까요?

쌓임 맥락을 쓰면 좋은 점이 하나 있는데요, 쌓임 맥락 안에서는 바깥을 신경 쓰지 않고 z-index 값을 쓸 수 있다는 점입니다. 이게 간단한 코드에서는 와닿지 않는데, HTML 태그가 수백 개, 수천 개, 수만 개 있다고 생각해보시면 이것들의 z-index를 고려해서 CSS 코드를 쓰는 게 만만치 않겠죠? 쌓임 맥락만 분명하다면 바깥은 신경 쓰지 않고 코드를 쉽게 쓸 수 있습니다. 덕분에 내비게이션 같은 걸 만들 때도 z-index 숫자를 무한정 크게 하지 않아도 깔끔하게 값을 쓸 수 있죠.

 

쌓임 맥락 만들기

쌓임 맥락이라는 개념 자체는 복잡하지 않지만 쌓임 맥락이 만들어지는 조건은 굉장히 복잡합니다. 몇 가지만 소개해드리자면 이렇습니다:

  • 문서의 루트 요소(<html>)
  • position이 absolute이거나 relative이고, z-index가 auto가 아닌 경우
  • position이 fixed이거나 sticky인 경우
  • 플렉스박스(우리가 다음 챕터에서 배울 개념)나 그리드(우리가 다음 다음 챕터에서 배울 내용)의 자식 중 z-index가 auto가 아닌 경우
  • opacity가 1보다 작은 요소

앞서 봤던 예시에서는 두 번째 조건인 "position이 absolute이거나 relative이고, z-index가 auto가 아닌 경우"에 해당했기 때문에 쌓임 맥락이 만들어진 겁니다.

솔직히 말씀드리면 저도 이 내용을 외우고 있지는 않습니다. 정말 많은 조건이 있는데, 혹시 궁금하신 분들은 MDN 문서의 쌓임 맥락 페이지를 참고해 보세요. 이 조건들을 모두 외우고 쓰면 좋겠지만 쉬운 일이 아닙니다. 사실 이런 조건들은 웹 브라우저를 만드는 사람들이 꼼꼼하게 챙기면 되고, 우리는 좀 더 실용적으로 접근해 보겠습니다.

쌓임 맥락은 언제 쓸까요? 아까처럼 의도와 다르게 내가 원하는 대로 z-index가 동작하지 않거나, z-index 값이 너무 많아지고 복잡해져서 코드가 헷갈릴 때 필요하겠죠.

z-index가 원하는 대로 동작하지 않을 때

거의 90%의 확률로 쌓임 맥락이 만들어져서 그럴 겁니다. 이럴 때 가장 손쉬운 방법은 해당 요소를 쌓임 맥락 바깥으로 옮기는 겁니다. 예를 들어서 앞에서 본 코드를 예로 들자면, .green의 <div>를 상위 태그로 옮기고 CSS도 약간 수정해 주는 거죠.

<div class="red"></div>
<div class="green"></div>
<div class="blue"></div>
.green {
  background-color: #32b9c1;
  position: absolute;
  width: 50px;
  height: 50px;
  top: 125px;
  left: 125px;
  z-index: 3;
}

z-index 값이 너무 많아지고 복잡해질 때

적절하게 쌓임 맥락을 만들어주면 됩니다. 위에 소개한 조건들 중에서 두 번째 조건(relative 포지션)을 이용하면 간단하게 쌓임 맥락을 만들 수 있는데요. 예를 들어서 .container라는 <div> 안에 쌓임 맥락을 만들고 싶으면 아래처럼 position 속성과 z-index를 추가해 줍니다.

<div class="container">
  ...
</div>
.container {
  position: relative;
  z-index: 0;
}

position: relative는 원래 위치를 기준으로 요소를 배치하는 거였죠? 이때 따로 위치를 정하지 않으면 그냥 원래 위치에 있습니다. 그리고 z-index의 기본 값은 auto인데, 0으로 지정한다고 해서 요소들 사이에 쌓이는 순서가 바뀌지는 않습니다. (auto 와 0의 가장 큰 차이는 쌓임 맥락을 만드느냐 아니냐의 차이입니다.)

이 코드를 활용하면 기존 배치를 바꾸지 않으면서도 손쉽게 쌓임 맥락을 만들 수 있을 겁니다.

예를 들어서 내비게이션 바를 만든다고 해 볼게요. <header> 태그와 <main> 태그로 일단 영역을 크게 두 개로 나누고, <main>에 쌓임 맥락을 만들어 놓으면 <header> 태그는 무슨 일이 있어도 <main> 태그 안에 있는 것들 보다 앞쪽에 보일 겁니다.

<header>
  ...내비게이션
</header>
<main>
  ...본문 내용
</main>
header {
  position: sticky;
  top: 0;
  z-index: 1;
}

main {
  position: relative;
  z-index: 0;
}
 
728x90

'HTML' 카테고리의 다른 글

HTML 포지셔닝 정리하기  (0) 2023.10.20
메타 태그 소개  (0) 2023.10.20
HTML head 와 시맨틱 태그  (0) 2023.10.20
HTML link와 script 정리  (0) 2023.10.20
HTML 폼 정리  (0) 2023.10.20