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 문제를 해결하거나 예방하는데 도움이 되었으면 좋겠습니다.
This work is licensed under
CC BY 4.0