화면을 만들다 보면 요소들이 앞뒤로 겹치는 상황이 생긴다. 팝업이 배경 위에 떠야 하고, 드롭다운 메뉴가 아래 내용을 덮어야 한다. 나는 이 겹침 순서를 조절하려고 z-index라는 값을 주기 시작했는데, 어떤 때는 잘 되고 어떤 때는 아무리 큰 값을 줘도 요소가 위로 올라오지 않았다. 그 이유를 이해하지 못한 채 값만 키우던 시절이 꽤 길었다.


이번 편은 요소의 겹침 순서를 결정하는 z-index와, 그 뒤에 숨은 쌓임 맥락을 다룬다. z-index가 언제 작동하는지, 쌓임 맥락이란 무엇이고 무엇이 그것을 만드는지, 왜 큰 값을 줘도 요소가 안 올라오는지, 그리고 이 혼란을 체계적으로 관리하는 법까지 살펴본다. 이 원리를 이해하면 겹침 순서가 예상대로 통제되지 않던 답답함에서 벗어날 수 있다.

1. 겹침 순서를 정하는 z-index

화면은 평면이지만 요소들은 앞뒤로 쌓인다. 두 요소가 같은 자리에 겹치면 하나가 다른 하나를 가리는데, 이 앞뒤 순서를 조절하는 것이 z-index다. 값이 클수록 앞으로 나와 다른 요소를 덮는다. 나는 처음에 이 값을 그저 크게 주면 무조건 위로 온다고 단순하게 생각했는데, 실제로는 그렇게 단순하지 않았다.


z-index에는 중요한 전제가 있다. 이 값은 위치가 지정된 요소에서만 작동한다는 것이다. 위치 속성이 기본 상태인 요소에는 z-index를 아무리 줘도 효과가 없다. 나는 이 사실을 모르고 z-index를 줬는데 아무 변화가 없어 한참을 헤맸다. 요소의 위치를 겹침이 가능한 상태로 바꿔야 비로소 z-index가 의미를 갖는다는 걸 그때 배웠다.


플렉스나 그리드 같은 레이아웃의 자식 요소는 예외적으로 위치 지정 없이도 z-index가 작동한다. 나는 이 예외 덕분에 레이아웃 안의 요소들 순서를 위치 속성 없이 조절하기도 한다. 다만 대부분의 경우는 위치가 지정되어야 한다는 원칙을 기본으로 기억해두는 편이 혼란이 적다. 예외는 예외로 알아두되 원칙을 먼저 세우는 것이다.


z-index를 주지 않아도 요소들은 나름의 순서로 쌓인다. 마크업에서 나중에 나온 요소가 앞선 요소를 덮는 것이 기본 규칙이다. 나는 겹침 순서가 자연스러운 경우라면 굳이 z-index를 주지 않고 마크업 순서에 맡긴다. 값을 남발하기보다, 기본 규칙으로 해결되는지를 먼저 살피는 것이 나중의 복잡함을 줄인다.


z-index 값 자체는 상대적인 의미만 갖는다. 중요한 건 절대적인 크기가 아니라 같은 맥락 안에서 다른 요소보다 크냐 작으냐다. 나는 이 상대성을 이해하고 나서, 굳이 큰 값을 쓸 필요가 없다는 걸 알았다. 겹칠 요소들 사이의 순서만 정하면 되므로, 작은 값 몇 개로도 충분히 원하는 순서를 만들 수 있다.


문제는 이 상대성이 어떤 범위 안에서 작동하느냐다. z-index는 아무 데서나 전역적으로 비교되는 게 아니라, 특정한 울타리 안에서만 서로 비교된다. 나는 이 울타리의 존재를 몰랐을 때 z-index가 도무지 예측대로 움직이지 않았다. 이 울타리가 바로 다음에 다룰 쌓임 맥락이며, 겹침 순서의 진짜 열쇠다.

2. 쌓임 맥락이라는 울타리

쌓임 맥락은 요소들의 겹침 순서가 결정되는 독립된 범위다. 하나의 쌓임 맥락 안에서 요소들은 서로 z-index를 비교하며 순서를 정하지만, 다른 쌓임 맥락에 속한 요소와는 직접 비교되지 않는다. 나는 이 개념을 이해하고 나서야, 왜 어떤 요소는 큰 값을 줘도 특정 요소 위로 올라오지 못하는지를 비로소 납득했다.


쌓임 맥락은 층층이 중첩된다. 한 쌓임 맥락 안에 또 다른 쌓임 맥락이 생기고, 그 안에 또 생기는 식이다. 중요한 건 자식 쌓임 맥락 전체가 부모 안에서 하나의 덩어리로 취급된다는 점이다. 나는 이걸 상자 안의 상자로 상상한다. 안쪽 상자 속 물건들이 아무리 높이 쌓여도, 바깥 상자들끼리의 순서를 넘어설 수는 없다.


이 중첩 구조가 겹침 순서의 핵심 원리다. 어떤 요소의 실제 앞뒤 위치는 자기 z-index만으로 정해지지 않고, 자기가 속한 쌓임 맥락이 부모 안에서 어디에 있는지에도 달려 있다. 나는 요소의 겹침을 판단할 때, 그 요소만 보는 게 아니라 어떤 쌓임 맥락에 속해 있는지를 함께 본다. 소속을 알아야 순서를 알 수 있다.


문서 전체에는 최상위 쌓임 맥락이 하나 있다. 그리고 그 안에서 여러 요소가 각자의 쌓임 맥락을 만들며 계층을 이룬다. 나는 이 계층을 나무처럼 그려본다. 각 가지가 하나의 쌓임 맥락이고, 그 안에 잎처럼 요소들이 달려 있다. 요소의 최종 위치는 이 나무에서 자기가 어느 가지에 달렸는지에 따라 결정된다.


같은 쌓임 맥락 안에서만 z-index가 서로 비교된다는 원칙은 아주 중요하다. 서로 다른 쌓임 맥락에 속한 두 요소는, z-index 값이 어떻든 각자의 쌓임 맥락의 순서를 따른다. 나는 이 원칙을 겹침 문제를 진단하는 첫 질문으로 삼는다. 두 요소가 같은 쌓임 맥락에 있는가, 아니면 다른 맥락에 있는가를 먼저 확인하는 것이다.


쌓임 맥락을 이해하면 겹침은 더 이상 신비로운 현상이 아니게 된다. 값을 무작정 키우는 대신, 요소들이 어떤 맥락에 속하고 그 맥락들이 어떻게 중첩되는지를 따지게 된다. 나는 이 사고방식으로 전환한 뒤로, 겹침 문제를 논리적으로 풀 수 있게 되었다. 원리를 알면 추측이 진단으로 바뀐다.

3. 무엇이 쌓임 맥락을 만드는가

쌓임 맥락을 이해하려면 무엇이 그것을 생성하는지 알아야 한다. 가장 익숙한 방식은 위치가 지정된 요소에 z-index를 주는 것이다. 이 조합은 새로운 쌓임 맥락을 만든다. 나는 z-index를 줄 때마다 그 요소가 새 쌓임 맥락을 여는 셈이라는 걸 의식한다. 값을 주는 행위 자체가 새 울타리를 세우는 것이기 때문이다.


그런데 쌓임 맥락은 z-index 말고도 여러 속성이 만든다. 요소를 반투명하게 만들거나, 앞서 다룬 변형을 걸거나, 필터 효과를 주는 것만으로도 새 쌓임 맥락이 생긴다. 나는 이 사실을 몰랐을 때, z-index를 전혀 건드리지 않았는데도 겹침 순서가 이상해지는 걸 겪었다. 원인은 무심코 준 반투명이나 변형이었다.


이 점이 가장 흔한 혼란의 원천이다. 겹침과 아무 상관 없어 보이는 속성이 조용히 쌓임 맥락을 만들어, 그 안의 요소들을 하나의 울타리에 가둬버리기 때문이다. 나는 반투명 효과를 준 부모 때문에, 그 안의 요소가 바깥 요소 위로 올라오지 못하는 문제를 여러 번 겪었다. 겉보기엔 무해한 속성이 겹침 순서를 좌우한 것이다.


화면에 고정된 요소나 스크롤에 붙는 요소도 쌓임 맥락을 만든다. 이런 요소들은 특성상 다른 요소들과 독립적으로 배치되어야 하므로, 자연스럽게 새 맥락을 연다. 나는 고정 요소나 스크롤 요소를 다룰 때, 이들이 자동으로 쌓임 맥락을 만든다는 점을 염두에 둔다. 이 요소들 안팎의 겹침은 별개의 울타리에서 처리된다.


쌓임 맥락을 만드는 속성들의 목록은 제법 길다. 하지만 공통점을 잡으면 외우기보다 이해할 수 있다. 대개 요소를 다른 요소와 시각적으로 독립시키는 성격의 속성들이 쌓임 맥락을 만든다. 나는 어떤 속성이 요소를 별도로 다뤄야 하게 만든다면, 그것이 쌓임 맥락을 만들 가능성이 높다고 짐작한다. 원리로 이해하면 개별 항목을 다 외우지 않아도 된다.


그래서 겹침 문제를 만나면, 나는 문제의 요소부터 위로 올라가며 조상들을 하나씩 점검한다. 어느 조상에 반투명이나 변형, 필터 같은 속성이 걸려 있는지 찾는 것이다. 대개 그중 하나가 예상 못 한 쌓임 맥락을 만들어 문제를 일으키고 있다. 원인을 찾는 이 습관이, 겹침 문제를 추측이 아니라 진단으로 해결하게 해준다.

4. 큰 값을 줘도 안 되는 이유

가장 흔한 겹침 문제는 자식에게 아주 큰 z-index를 줬는데도 요소가 특정 요소 위로 올라오지 못하는 것이다. 이건 그 자식이 속한 부모 쌓임 맥락 자체가 상대 요소보다 뒤에 있기 때문이다. 나는 이 상황을 이해하기 전까지 값을 계속 키우는 헛수고를 반복했다. 자식은 부모라는 울타리를 결코 넘을 수 없다.


이 원리를 상자에 비유하면 명확하다. 낮은 선반 위의 상자 안에 아무리 높은 물건을 넣어도, 그 물건이 높은 선반 위의 물건보다 높아질 수는 없다. 상자의 높이가 선반에 의해 이미 정해졌기 때문이다. 나는 자식의 z-index를 키우는 대신, 부모 쌓임 맥락의 순서를 봐야 한다는 걸 이 비유로 이해했다. 문제는 자식이 아니라 부모에 있다.


이 문제를 푸는 방법 중 하나는 문제의 요소를 다른 쌓임 맥락으로 옮기는 것이다. 갇힌 울타리에서 꺼내, 상대 요소와 같은 맥락이나 더 높은 맥락에 두는 것이다. 나는 팝업이나 알림처럼 항상 맨 위에 떠야 하는 요소를, 다른 요소에 갇히지 않도록 문서의 높은 위치에 두는 방식으로 이 문제를 자주 해결한다.


반대로 의도적으로 쌓임 맥락을 만들어 격리하는 방법도 있다. 컴포넌트가 자기만의 겹침 범위를 갖도록 새 쌓임 맥락을 열어주는 것이다. 이를 위한 전용 속성이 있어서, 시각적인 부작용 없이 순수하게 새 맥락만 만들 수 있다. 나는 독립적으로 관리하고 싶은 컴포넌트에 이 속성을 줘서, 내부의 겹침이 바깥에 영향을 주지 않게 격리한다.


이 격리는 큰 프로젝트에서 특히 유용하다. 컴포넌트마다 겹침 범위를 격리해두면, 한 컴포넌트의 z-index가 다른 컴포넌트와 충돌하지 않는다. 나는 재사용하는 컴포넌트를 만들 때, 내부 겹침을 격리해 어디에 놓여도 예측 가능하게 동작하도록 한다. 격리는 컴포넌트를 독립적이고 안전하게 만드는 좋은 습관이다.


결국 큰 값을 주는 것은 대개 잘못된 해법이다. 값을 키워도 안 되는 문제는 값의 크기가 아니라 쌓임 맥락의 구조에서 비롯되기 때문이다. 나는 겹침이 안 될 때 값을 키우고 싶은 충동을 참고, 먼저 쌓임 맥락의 구조를 살핀다. 구조를 바로잡으면 작은 값으로도 원하는 순서를 얻고, 구조를 무시하면 아무리 큰 값도 소용없다.

5. 겹침을 체계적으로 관리하기

z-index를 방치하면 프로젝트 곳곳에 임의의 큰 값들이 흩어지게 된다. 저마다 남보다 위에 오려고 값을 키우다 보면, 결국 아무 의미 없는 거대한 숫자들의 경쟁이 벌어진다. 나는 이런 z-index 전쟁을 여러 번 목격했고, 그 혼란을 수습하는 게 얼마나 고통스러운지 안다. 겹침도 값 관리처럼 체계가 필요하다.


내가 쓰는 방법은 겹침 층위를 미리 정해 이름 붙이는 것이다. 기본 내용, 떠 있는 메뉴, 덮개, 팝업, 알림처럼 겹침의 단계를 정하고, 각 단계에 정해진 값을 부여한다. 앞서 다룬 변수와 결합하면, 이 층위들을 이름으로 관리할 수 있다. 나는 요소마다 임의의 값을 주는 대신, 이 정해진 층위 중 하나를 고른다.


층위를 이름으로 관리하면 겹침 순서가 문서화된 규칙이 된다. 어떤 요소가 어느 층에 속하는지가 이름으로 드러나므로, 새 요소를 추가할 때도 어느 층에 둘지 명확하다. 나는 이 방식으로 팝업이 항상 메뉴 위에, 알림이 항상 팝업 위에 오도록 일관성을 유지한다. 값이 아니라 역할로 겹침을 다루면 관리가 훨씬 수월하다.


층위를 정할 때는 단계 사이에 여유를 두는 것이 좋다. 나중에 두 층 사이에 새 층을 끼워 넣어야 할 수도 있기 때문이다. 나는 각 층위 값 사이에 넉넉한 간격을 둬서, 예상 못 한 층이 생겨도 기존 값들을 다시 손대지 않고 끼워 넣을 수 있게 한다. 확장을 미리 고려한 설계가 나중의 대규모 수정을 막는다.


최신 기능 중에는 겹침 문제를 근본적으로 피하는 것도 있다. 팝업이나 대화 상자를 위한 특별한 최상위 층이 도입되어, 이런 요소들은 z-index 다툼 없이 항상 맨 위에 뜬다. 나는 팝업 성격의 요소는 이 최상위 층에 올리는 방식을 우선 고려한다. 겹침 순서를 직접 관리할 필요 자체를 없애는 것이, 관리의 가장 좋은 형태이기 때문이다.


정리하면 z-index는 위치가 지정된 요소에서 같은 쌓임 맥락 안의 순서를 정하고, 쌓임 맥락은 반투명이나 변형 같은 속성으로 조용히 생성되며, 자식은 부모 울타리를 넘지 못한다. 나는 값을 키우는 대신 구조를 진단하고, 층위를 이름으로 관리한다. 이 원리를 알면 겹침이 통제 가능한 대상이 된다. 다음 편에서는 넘치는 내용을 다루는 overflow와 스크롤 제어를 다룬다.