CSS의 z-index 속성 이해하기
우리는 보통 웹페이지를 2차원 공간으로 생각하고 웹 개발을 하는 경우가 많은데요. 하지만 복잡한 웹페이지를 구현할 때는 마치 3차원 공간처럼 요소를 앞뒤로 겹쳐서 배치해야 경우가 생기기 마련이죠.
이번 포스팅에서는 이렇게 웹에서 요소의 Z축 방향의 깊이를 결정하는 CSS의 z-index
속성에 대해서 배워보겠습니다.
z-index가 없을 때 요소 간 상대적 깊이
z-index
속성에 대해서 본격적으로 배우기 전에 먼저 z-index
가 없을 때 어떻게 요소(element) 간의 상대적 깊이가 결정되는지에 대해서 이해하는 것이 중요한데요.
z-index
속성을 너무 많이 사용하면 스타일 유지보수가 힘들어질 수 있기 때문에 될 수 있다면 z-index
속성을 아예 사용하지 않는 편이 낫기 때문입니다.
기본적으로 z-index
속성이 적용되지 않은 요소 간에는 HTML 문서 상에서 나중에 나오는 요소가 먼저 나오는 요소보다 위로 올라오도록 되어 있는데요.
따라서 다음과 같이 두 개의 div
요소가 겹쳐지면, 두 번째 상자가 첫 번째 상자 위에 올라오게 됩니다.
<div class="first box">1</div>
<div class="second box">2</div>
.first.box {
background: yellow;
}
.second.box {
background: tomato;
margin-top: -50px;
margin-left: 50px;
}
.box {
width: 200px;
height: 200px;
border: 2px solid;
font-size: 2rem;
text-align: center;
line-height: 200px;
}
하지만 position
속성이 static
이 아닌 relative
나 absolute
, fixed
, sticky
인 요소가 나타나기 시작하면 겹치는 순서가 바뀔 수 있는데요.
예를 들어, 첫 번째 상자의 position
속성을 relative
로 바꿔주면, 두 번째 상자가 밑으로 내려가는 것을 보게 됩니다.
.first.box {
background: yellow;
position: relative; top: 50px; left: 50px;}
.first.box {
background: yellow;
position: relative; top: 50px; left: 50px;}
여기서 만약에 제가 두 번째 상자의 position
속성도 relative
로 바꿔주면 어떻게 될까요?
그럼 상황이 역전되어 다시 두 번째 상자가 첫 번째 상자 위로 올라오게 됩니다.
.second.box {
background: tomato;
position: relative;}
정리를 해보면, z-index
가 없을 때 요소 간 상대적 깊이는 HTML 문서 상에서 요소가 나오는 순서와 각 요소의 position
속성이 static
이냐 아니냐에 따라서 결정이 되는데요.
position
속성이static
이 아닌 요소는 무조건position
속성이static
인 요소 위로 올라옵니다.position
속성이static
인 요소 간에는 HTML 문서 상에서 나중에 나오는 요소가 먼저 나오는 원소 위로 올라옵니다.position
속성이relative
나absolute
,fixed
,sticky
인 요소 간에는 HTML 문서 상에서 나중에 나오는 요소가 먼저 나오는 원소 위로 올라옵니다.
z-index가 있을 때 요소 간 상대적 깊이
z-index
속성을 사용하면 위에서 다룬 기본적인 규칙을 무시하고 HTML 문서 상에서 먼저 나온 요소를 나중에 나온 요소보다 앞으로 나오게 할 수 있는데요.
브라우저는 z-index
속성값이 낮은 요소를 먼저 그리고, z-index
속성값이 높은 요소를 나중에 그리기 때문에, 요소가 겹쳐있을 경우 z-index
속성값이 큰 요소가 z-index
속성값이 작은 요소의 일부를 가리거나 전체를 덮을 수 있습니다. (물감을 계속 덫칠한다고 생각하시면 이해가 쉬우실 것 같네요.)
예를 들어, 첫 번째 상자의 z-index
속성을 1
로 설정해주면, 두 번째 상자의 앞으로 나오는 것을 볼 수 있습니다.
.first.box {
z-index: 1; background: yellow;
position: relative;
top: 50px;
left: 50px;
}
이번에는 두 번째 상자의 z-index
속성을 2
로 설정해볼까요?
그러면 두 번째 상자가 첫 번째 상자의 앞으로 나올 것입니다.
.second.box {
z-index: 2; background: tomato;
position: relative;
}
여기서 간과하기 쉬운 부분이 바로 position
속성이 static
인 요소에는 z-index
속성이 아무 효력을 내지 못한다는 점입니다.
왜냐하면 position
속성이 static
인 요소는 z-index
속성이 auto
, 즉 0
으로 고정되어 있기 때문입니다.
예를 들어서, 제가 이 상태에서 두 번째 상자에 적용된 position: relative
를 제거하면 첫 번째 상자가 다시 앞으로 나오는 것을 볼 수 있습니다.
.second.box {
z-index: 2;
background: tomato;
/* position: relative; */}
또한 z-index
속성은 음수로도 설정해줄 수 있는데요.
이럴 경우, 해당 요소는 브라우저가 가장 먼저 그리기 때문에 심지어 position
속성이 static
인 요소보다도 더 뒤에 나타나게 됩니다.
예를 들어, 첫 번째 상자의 z-index
속성을 -1
로 설정해주면, 첫 번째 상자가 두 번째 상자 뒤로 들어가는 것을 볼 수 있습니다.
.first.box {
z-index: -1; background: yellow;
position: relative;
top: 50px;
left: 50px;
}
정리를 해보면, z-index
속성을 사용하면 position
속성이 static
이 아닌 요소의 깊이를 조절할 수 있습니다.
position
속성이static
인 요소에는z-index
속성이0
으로 고정되어 있으며 바꿀 수 없습니다.z-index
속성이 양수로 설정된 요소는position
속성이static
인 요소보다 앞으로 올라옵니다.z-index
속성이 음수로 설정된 요소는position
속성이static
인 요소보다 뒤로 내려갑니다.position
속성이static
이 아닌 요소 간에는z-index
속성값이 클 수록 앞으로 올라오고, 작으면 작을수록 뒤로 내려갑니다. (예:z-index: 1
인 요소보다z-index: 2
인 요소가 더 앞에 나오고z-index: -1
인 요소보다z-index: -2
인 요소가 더 뒤로 들어감)
stacking context: z-index가 비교되는 범위
z-index
와 관련되서 많은 분들이 오해하시는 부분이 있는데요.
바로 z-index
가 HTML 문서 전체 범위에서 비교된다고 생각하는 것이에요.
사실 z-index
는 특정 범위 내에서 비교되며, 이것을 CSS에서는 stacking context라고 부릅니다.
예를 들어, 첫 번째 상자의 z-index
를 100
이라고 설정하고 두 번째 상자의 z-index
를 2
로 설정하면 당연히 첫 번째 상자가 두 번째 상자 앞으로 올라오겠죠?
여기서 첫 번째 상자를 다른 <div>
요소로 감싸고, 그 감싸는 div
요소의 z-index
를 한번 1
로 줘 볼까요?
<div class="wrapper"> <div class="first box">1</div>
</div><div class="second box">2</div>
.first.box {
z-index: 100; background: yellow;
position: relative;
top: 50px;
left: 50px;
}
.second.box {
z-index: 2; background: tomato;
position: relative;
}
.wrapper {
z-index: 1; position: relative;
}
그러면 정말 신기하게도 두 번째 상자가 첫 번째 상자 앞으로 올라오는 것을 볼 수 있는데요.
어떻게 z-index
값이 무려 100
인 첫 번째 상자가 z-index
값이 겨우 2
인 두 번째 상자의 뒤로 내려가게 되었을까요?
비밀은 바로 stacking context에 있는데요.
다시 말해서, 이 두 상자의 z-index
값이 서로 다른 범위에서 비교되기 때문입니다.
첫 번째 상자는 z-index
값이 1
인 <div>
요소에 감싸져있기 때문에, 두 번째 상자의 z-index
값과 직접 비교되지 않습니다.
따라서 첫 번째 상자의 z-index
값이 아무리 크더라도 절대 부모인 <div class="wrapper">
요소의 외부에 있는 다른 요소의 z-index
값과 경쟁할 수가 없는 것이지요.
다음과 같이 부모 요소에 설정된 z-index
값이 자식 요소의 z-index
값 앞에 붙어있다고 상상하시면 좀 더 이해가 쉬우실 겁니다.
<!-- z-index: 1 -->
<div class="wrapper">
<!-- z-index: 1.100 -->
<div class="first box">1</div>
</div>
<!-- z-index: 2 -->
<div class="second box">2</div>
CSS의 stacking context와 관련해서 한 가지 주의할 점은 stacking context의 구조는 반드시 HTML 문서의 DOM 구조와 일치하지는 않는다는 것입니다.
예를 들어, 아래 예제를 보면 두 개의 상자가 모두 여러 개의 <div>
요소로 감싸져 있지만 첫 번째 상자가 두 번째 상자보다 위로 올라오는 것을 볼 수 있는데요.
이것은 첫 번째 상자와 두 번째 상자 간의 z-index
값에 직접적인 비교가 일어났다는 의미죠.
<div> <div> <div class="first box">1</div>
</div></div><div> <div> <div class="second box">2</div>
</div></div>
이번 경우에는 두 상자를 감싸고 있는 <div>
요소에 z-index
속성을 설정해주지 않았기 때문에, 부모 단계에서 stacking context가 형성되지 않았습니다.
따라서 부모 요소가 있든 없든 상관없이 두 상자의 z-index
값이 같은 범위 내에서 비교가 된 것입니다.
<div>
<div>
<!-- z-index: 100 -->
<div class="first box">1</div>
</div>
</div>
<div>
<div>
<!-- z-index: 2 -->
<div class="second box">2</div>
</div>
</div>
마치면서
지금까지 CSS에서 z-index
속성을 어떻게 사용하는지에 대해서 여러 가지 예시를 통해서 살펴보았는데요.
꽤 복잡한 알고리즘에 의해서 요소 간의 상대적인 깊이가 결정이 된다는 것을 배우셨을 것입니다.
z-index
에 대해서 제대로 이해하고 않고, z-index
를 납용하게 되면 여러 요소를 원하는 순서로 겹치는 것이 생각했던 것보다 상당히 골치아파질 수 있는데요.
워낙 악명이 높아서 CSS 커뮤니티에서는 이 문제를 z-index
전쟁(war)이라고 부르기도 합니다.
본 포스팅이 이러한 z-index
문제를 해결하거나 예방하는데 도움이 되었으면 좋겠습니다.