z-index와 쌓임 맥락 정리 - 왜 z-index가 위로 안올라갈까?

최원빈·2022년 10월 18일
19

웹을 만들 때, 보편적으로 상단-하단바를 분리하고, 가운데 메인 콘텐츠 영역을 두곤 한다.

<Router나 Provider등등...>
  <TopNav/>
  <MainSection/>
  <Footer/>
</Router나 Provider등등...>

경우에 따라 모바일 반응형 환경에서 FooterTopNav 혹은 Headerposition: fixed 를 주는 경우가 꽤 있는데, 이 경우에 마주친 문제이다.

position이 있는 요소들이 붙어있는 상황에서 아무리 z-index를 크게 줘도 내가 원하는 요소가 가장 위로 올라가지 않는 문제였다.

왜 자꾸 위로 올라오니....?

저 구조를 표현하면 아래와 같았다.

<App>
  <TopNav/>
  <MainSection>
    <Contents/>
    <BottomSaleTab/>  ---> 수업 신청하기 & 모집마감
  </MainSection>    
  <Footer/>           ---> Logo & 이용약관 등
</App>

짐작컨데, BottomSaleTab엔 position: fixed가 확실하고,
Footer에는 뭔가 position이 할당되어 위로 올라오는 것 같다.

z-index를 안 넣었겠지, Footer를 더 낮게 맞춰보자.

BottomSaleTab {
  position: fixed;
  z-index: 999;
}

Footer {
  position: relative;
  z-index: 0;
}

근데 이게 웬? 이렇게 바꿔봐도 위치가 그대로다.
뭔가 뭔가 문제가 있다...


z-index

먼저 요소끼리의 위아래 위치를 정하는 z-index 프로퍼티에 대해 알아보자.

z-index 값은 position 프로퍼티가 기본값인 static이 아닌 다른 값인
relative, absolute, fixed, sticky 중 하나를 할당받았을 때 사용할 수 있는 값이다.

relative는 본인의 위치에서 고정적인 값만큼 이동시킬 때,
absolute는 고정 위치를 가진 부모를 기준으로 고정적인 값만큼 이동시킬 때,
fixed는 viewport기준으로 고정시킬 때,
sticky는 위치를 유지하다가 스크롤되어도 viewport에 고정시킬 때 사용하고

z-index는 이들끼리 화면에 올라올 순서를 결정한다.

누가 위로 올라올 지 정해야 할 때

내가 아는건 이 정도였는데, 문제를 해결하기 위해선 공식문서를 뒤져볼 필요가 있었다.

z-index (MDN)
위치 지정 요소(position이 static 외의 다른 값인 요소)의 박스에 대해, > z-index 속성은 다음 항목을 지정합니다.

  • 현재 쌓임 맥락에서 자신의 위치
  • 자신만의 쌓임 맥락 생성 여부.

그냥 z-index가 높으면 위로 올라오는 게 아니었나 보다...ㅋㅋㅋ
반복적으로 나오는 쌓임 맥락이라는 말부터 이해해보자.


쌓임 맥락

우리가 바라보는 웹은 x, y축뿐만 아니라 z축도 가진 3차원 입체를 z축 방향으로 바라봐 2차원 화면으로 보이는 구조이다.
브라우저는 화면을 쌓는 규칙이 정의되어있고, 한 가지 파트로 전부 쌓이는 것이 아니기에 맥락을 이뤄 쌓임 맥락(Stacking Context)이라고 부른다.

당연하지만, 우리는 z축을 기준으로 화면을 보고있기에 viewport에는 x, y축으로 화면이 배치되고, z축이 높을수록 볼 수 있게 앞쪽에 쌓인다.


Z축이 높을수록 보는 방향에 가까움

기본 쌓임 맥락

무의식적으로 알게 되는 사실들이지만, z-index가 지정되지 않은 상황에서는 기본적으로 아래 규칙에 맞게 화면을 쌓는다.

  1. 배경(html, body, 최상단 태그 등)이 맨 뒤, 자식 요소는 앞으로.
  2. 자식 요소들은 먼저 생성된 순서(코드상 위에서부터 선언된 순서)대로 뒤에 쌓이고, 차례차례 앞으로.
  3. position을 할당받은 요소들은 z-index가 없더라도 할당받지 않은 다른 요소들보다 앞으로 온다.
    3-1. position을 할당받은 요소끼리도 먼저 생성된 순서대로 뒤에 쌓이고, 차례차례 앞으로.

MDN문서의 예제. DIV #1 ~ 5순으로 그러졌다.

그렇다곤 하는데, 사실 position을 쓰지 않는다면 부모-자식 관계가 아닌, 형제 요소끼리 겹칠 일은 흔하지 않다. transform정도는 써줘야 할 것 같은데..

그럼 기본 쌓임 맥락을 제외한 쌓임 맥락이 무엇인지 알아보자.

쌓임 맥락 생성 조건

아래 조건을 하나라도 만족하면, 브라우저는 새로운 쌓임 맥락을 생성한다.

매우 다양한 조건

새로운 쌓임 맥락을 생성한다는 것부터 약간 모호하지만,
한가지씩 예시를 들어보면 이해할 수 있을 듯하다.

1. 문서의 루트 요소 ( <html> )

<html> 태그는 새로운 쌓임 맥락을 생성한다.
기본적으로 모든 웹페이지는 한 개 이상의 쌓임 맥락이 있는 셈이다.

중요한 점은, 문서의 가장 바깥인 html태그의 쌓임 맥락보다 뒤에 쌓이는 요소는 없다.
추후에 증명할 내용이니, 다른 쌓임 맥락을 구성하면서 차차 알아보자.

2. position이 relative, absolute 이며, z-index가 auto가 아닌 요소

여기서부터 재밌다.
z-index의 기본값은 auto이며, 정수 값을 할당해주면 새로운 쌓임 맥락이 생성된다.

각 쌓임 맥락은 각자 내부적으로 위에서 언급한 기본 쌓임 맥락을 적용해 내부 요소를 그린다.

<div>
  <div class='red relative z-10'></div>
  <div class='green relative z-30'></div>
  <div class='blue relative z-5'></div>
</div>

잘 올라가는 초록 박스

눈에 보이는 걸 더 입체적으로 보고싶다면, 개발자도구의 Layer기능을 사용해 확인할 수 있다.
그 중에서도, Edge 브라우저의 3D보기는 z-index확인용으로는 단연 톱이라고 생각한다.

Edge브라우저의 3D View

여기서 각각의 쌓임 맥락이란?
현재 쌓임 맥락을 표현하면 이러하다

  • html
    • red(10)
    • green(30)
    • blue(5)

여기서, z-index가 30보다 높은 요소를 red안에서 생성하면 어떻게될까?

<div>
  <div class='red relative z-10'>
    <div class='purple relative z-50'></div>
  </div>
  <div class='green relative z-30'></div>
  <div class='blue relative z-5'></div>
</div>

z-index가 50인 purple은 30인 green보다 위로 올라갈까??

어라?

당연히 purple이 가장 위로 올라오겠지? 했으나 그렇지 않다.
각각의 쌓임 맥락이라는 말은 여기서 나오는 것이다.

  • html
    • red(10)
      • purple(50)
    • green(30)
    • blue(5)

html의 쌓임 맥락에는 blue -> red[purple] -> green 순으로 쌓여있고,
red의 쌓임 맥락에 purple이 쌓여있는 것이다.
그럼 purple 옆에 z-index가 1인 요소를 만들면 blue보다 밑으로 내려갈까? 당연히 그렇지 않다

다시 현재 쌓임 맥락을 표현하면 아래와 같은 상태인것이다.

  • html
    • red(10)
      • orange(1)
      • purple(50)
    • green(30)
    • blue(5)

쌓임 맥락이 이미 밑으로 쌓인 요소는 일반적으로는 부모의 쌓임 맥락을 벗어날 수 없다.
z-index를 999로 주어도 올라가지 않고, 음수 값을 주어도 내려가지 않은 것은 이러한 이유이다.

z-index는 부모의 쌓임 맥락에 따른 상대적인 값이다.
부모의 쌓임 맥락에 따라서 현재 요소의 쌓임 맥락 내에서의 위치를 정한다.

그럼 여기서 문제,
red의 z-index가 30이 되어서 형제 요소와 같아지면 orange와 purple은 어떻게될까?

<div>
  <div class='red relative z-10'>  // 여기서 z-30이 된다면??
    <div class='orange relative z-1'></div>
    <div class='purple relative z-50'></div>
  </div>
  <div class='green relative z-30'></div>
  <div class='blue relative z-5'></div>
</div>
  • html
    • red(30) ???
      • orange(1)
      • purple(50)
    • green(30) ???
    • blue(5)

둘은 같은 z-index 값을 할당받았으나,
position이 relative이면서 z-index값이 auto가 아니므로 각자의 쌓임 맥락을 생성한다.
여기서 브라우저는 기본 쌓임 맥락 순서에 따라 먼저 생성한 요소를 뒤에, 나중에 생성한 요소를 앞에 배치한다.

그렇기에 red의 z-index가 같아져도, 아무런 변화도 일어나지 않는다.

같은 30이어도, 급(?)의 차이가 있다

위에서 말했던 html 태그 뒤에는 요소가 위치할 수 없다는 점도 왜인지 알 수 있다.

  • html
    • red(30)
      • orange(1)
      • purple(50)
    • green(-999)
    • blue(5)

최초의 쌓임 맥락은 html태그이다. 이후의 쌓임 맥락들은 어떻게 생성되든 html의 자식 맥락들이고,
z-index는 부모의 쌓임 맥락에 따른 상대적인 값이기 때문에 너무나 당연히 더 뒤에 존재할 수 없다.

사실 이 점은 사용할 일도 쓸모도 없겠지만 각 쌓임 맥락이 이와 같이 부모의 맥락을 벗어나지 못하게 동작한다는 점은 내 이해에 도움을 주었다.

3. position이 fixed 또는 sticky인 요소

내 문제가 발생한 부분이다.
바로 위에서 설명한 다른 position 값과 다른 점은, z-index값이 없어도 쌓임 맥락을 생성한다는 점이다.

착각하면 안되는 것은, 쌓임 맥락을 생성할 뿐이지 쌓임 맥락을 벗어나거나 하는 것이 아니다.

<div>
  <div class='red relative z-10'>
    <div class='purple fixed z-50'></div>
  </div>
  <div class='green relative z-30'></div>
  <div class='blue relative z-5'></div>
</div>


purple은 red 위, green 밑!

쌓임 맥락상의 높이는 relative나 absolute였을때와 달라진 게 없다.

fixed와 sticky의 특징 상, 문서 배치 상의 위치가 일반적인 문서의 흐름을 완전히 벗어나기에 쌓임 맥락에서도 벗어날 것처럼 생겼으나, 실제는 그렇지 않다는 것을 알 수 있다.

그럼 여기서 두번째 문제,

<div>
  <div class='red relative'>  --> z-index가 없다?
    <div class='purple fixed z-50'></div>
  </div>
  <div class='green relative z-30'></div>
  <div class='blue relative z-5'></div>
</div>

purple을 감싸던 red의 z-index가 사라지면 어떻게 될까?

맞춘다면 앞선 파트의 여러가지 점들을 이해하고 넘어간거고, 틀렸다면 다시 위로 올라가도록 하자.

정답은 "가장 위로 올라온다" 이다.

쌓임 맥락 구조를 보면 아래와 같다.

  • html
    • red
      • purple(50)
    • green(30)
    • blue(5)

purple의 부모인 red 박스가 relative지만, z-index값이 없어서 쌓임 맥락을 생성하지 않는다.

그럼 루트 쌓임 맥락의 자식으로는 z-index 50의 purple이 들어오게 된다.

html 구조상의 자식-형제-사촌 관계가 중요한 것이 아니다.
purple은 green보다 분명히 더 자식 요소지만, 같은 level의 쌓임 맥락에서 비교된다.
쌓임 맥락이 생성된 순간부터, 요소의 앞뒤는 순서는 쌓임 맥락만이 결정한다.

한 가지 더 변형문제,
fixed인 purple의 z-index가 지정되지 않았다면 어떻게 될까?

<div>
  <div class='red relative'>         --> z-index가 없다?
    <div class='purple fixed'></div> --> 얘도?
  </div>
  <div class='green relative z-30'></div>
  <div class='blue relative z-5'></div>
</div>
  • html
    • red
      • purple(auto)
    • green(30)
    • blue(5)

position값이 static이 아닌 요소에다가 z-index 값을 정의하지 않으면 auto값을 default로 할당받는다.

auto로 할당받는 값은 0과 같다.

  • html
    • purple(auto = 0)
    • green(30)
    • blue(5)

purple은 쌓임 맥락에서 blue보다 높이가 낮기 때문에, 밑으로 깔리게 된다.

몇 가지 더 추가적으로,

blue의 z-index가 0이면?

  • z-index값이 같은 요소는 기본 쌓임 맥락 기준을 따라 먼저(코드상으로 상단부에서) 만들어진 전과 같이 purple이 더 밑으로 깔린다.

blue의 z-index가 0이고, purple보다 코드상으로 위에 있으면?

  • auto값은 0과 같기에 blue가 purple보다 밑으로 깔린다.

auto가 0과 같다면 기본값을 0으로 주면 되는거 아니냐?

  • 0을 주면 relative와 absolute는 자신의 쌓임 맥락을 새로 구축하게 된다. auto는 생성하지 않는데, 이를 구분하기 위함이다.

그러므로 z-index는 절대로 아무때나 쓰면 안되는 프로퍼티인 셈이다.

내가 만들던 프로그램도 absolute와 relative에 덕지덕지 달려있던 z-index들을 전부 날려버리고 문제를 해결할 수 있었다.
z-index를 안 쓰면 absolute와 relative를 아무리 써도 쌓임 맥락이 생기지 않아서 문제가 발생하지 않는다.

물론 쌓임 맥락을 생성하는 언급하지 않은 다른 조건들(transform, opacity 등)이 들어가면 불가피하게 맥락을 확인하고 해야겠지만... 슬쩍 보면 보통은 부모급 컴포넌트에는 잘 들어가지 않는 속성들이다.

뭐 이러나 저러나 당연히 만들다보면 쌓임 맥락이 꼬이게 될 수도 있다.
그래도 앞으론 이번 통찰과 Edge의 개발자 도구가 있으니깐. 눈으로 보고 해결할 수 있다.

역시 남이 만든 코드를 보수하는건 상당히 피곤한 일이다


참고
https://webdesign.tutsplus.com/articles/what-you-may-not-know-about-the-z-index-property--webdesign-16892
https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context

profile
FrontEnd Developer

4개의 댓글

comment-user-thumbnail
2023년 8월 7일

정리 너무 잘하셨네요! 도움이 되었습니다. 감사합니다.

1개의 답글
comment-user-thumbnail
2024년 5월 14일

진짜 최고입니다. 2, 3시간동안 실패하다가 해결했습니다. 정말 감사합니다 ㅠㅠㅠㅠ

답글 달기