본문 바로가기

TIL

TIL - Z-index 스태킹 컨텍스트 문제 트러블슈팅

스태킹 컨텍스트 정의

스태킹 컨텍스트(Stacking Context)는 HTML 요소의 3차원 개념적 레이어링을 정의하는 방식입니다. 이는 요소들이 z축을 따라 어떻게 쌓이는지를 결정합니다. 새로운 스태킹 컨텍스트는 다음과 같은 조건에서 생성됩니다:

  • root 요소 (HTML)
  • position: absolute 또는 relative이며 z-index가 auto가 아닌 요소
  • position: fixed 또는 sticky인 요소
  • flexbox 컨테이너의 자식 중 z-index가 auto가 아닌 요소
  • opacity가 1보다 작은 요소
  • transform, filter, perspective, clip-path, mask 등의 특정 CSS 속성을 가진 요소

스태킹 컨텍스트는 부모-자식 관계를 형성하며, 각 컨텍스트 내에서 z-index 값에 따라 요소의 쌓임 순서가 결정됩니다.

 

문제 상황

QASection 내의 AnswerItem 컴포넌트에서 DetailMenu를 클릭했을 때, 오버레이가 메뉴 위에 나타나는 문제가 발생했습니다. 반면, CommentItem 컴포넌트에서는 같은 구조의 DetailMenu가 정상적으로 작동했습니다.

초기 구조 비교

// AnswerItem
<div className="relative">
  {isMenuOpen && (
    <div className="fixed inset-0 bg-black bg-opacity-50 z-[1000]" onClick={() => setIsMenuOpen(false)} />
  )}
  <div className="flex flex-col gap-4 p-4 pb-6 rounded-lg bg-whiteT-3 backdrop-blur">
    {/* 내용 */}
  </div>
</div>
// CommentItem 
<div className="bg-whiteT-3 rounded-lg p-4">
  {isMenuOpen && <div className="fixed inset-0 bg-black bg-opacity-50 z-50" onClick={() => setIsMenuOpen(false)} />}
  {/* 내용 */}
</div>

 

 

시도한 방법들

  1. z-index 조정:
    • 시도: DetailMenu의 z-index를 높여보았습니다.
    • 결과: 효과가 없었습니다.
  2. 네거티브 z-index 사용:
    • 시도: isMenuOpen일 때 다른 요소들에 -z-index를 적용해보았습니다.
    • 결과: 부분적으로 작동했지만, 전체 레이아웃에 영향을 주어 바람직하지 않았습니다.
    • 분석: 이 시점에서 문제가 z-index 관련 스태킹 컨텍스트 차이라는 것을 인지하기 시작했습니다.
  3. 내부 요소를 fixed 포지션으로 변경:
    • 시도: DetailMenu를 포함한 내부 요소를 position: fixed로 설정해보았습니다.
    • 결과: 레이아웃이 깨지고 예상치 못한 위치에 요소들이 나타났습니다.
  4. DetailMenu 내부에서 오버레이와 메뉴의 위치 일치:
    • 시도: DetailMenu 컴포넌트 내부에서 오버레이와 메뉴의 위치를 일치시켰습니다.
    • 결과: 메뉴는 올바르게 표시되었지만, 오버레이가 전체 화면에 적용되지 않았습니다.
    • 문제점: inset-0과 fixed 포지션을 사용했음에도 오버레이가 전체 화면에 전파되지 않았습니다.

원인 분석

  1. 스태킹 컨텍스트 차이:
    • 문제의 핵심은 최상위 div의 포지션 설정 차이였습니다.
    • AnswerItem: 최상위 div가 relative로 설정되어 있어 새로운 스태킹 컨텍스트를 생성했습니다.
    • CommentItem: 최상위 div가 기본 static 포지션이어서 별도의 스태킹 컨텍스트가 생성되지 않았습니다.
  2. 스태킹 컨텍스트의 이해:
    • 초기에는 div가 많이 중첩된 것이 문제의 원인이라고 생각했습니다.
    • 트러블슈팅 과정에서 포지션 설정(예: relative, absolute, fixed)이 새로운 스태킹 컨텍스트를 생성한다는 것을 학습했습니다.
  3. 오버레이 전파 문제:
    • DetailMenu 내부에서 오버레이 위치를 조정했을 때, 전체 화면에 오버레이가 적용되지 않았습니다.
    • 이는 부모 요소의 스태킹 컨텍스트가 오버레이의 범위를 제한했기 때문입니다.

 

최종 해결 방법

  1. 컴포넌트 구조 통일:
    • AnswerItem의 구조를 CommentItem과 동일하게 변경했습니다.
    • 최상위 div의 relative 포지션을 제거하여 불필요한 스태킹 컨텍스트 생성을 방지했습니다.
  2. 오버레이 위치 조정:
    • 오버레이를 컴포넌트의 최상위 레벨로 이동시켜 전체 화면에 적용되도록 했습니다.
  3. z-index 값 조정:
    • 오버레이와 메뉴의 z-index 값을 적절히 조정하여 올바른 순서로 표시되도록 했습니다.

 

 

학습 포인트

  1. 스태킹 컨텍스트의 중요성: 포지션 설정(relative, absolute, fixed 등)이 새로운 스태킹 컨텍스트를 생성할 수 있음을 이해
  2. 구조적 일관성의 가치: 유사한 기능을 하는 컴포넌트는 가능한 동일한 구조를 가져야 함
  3. CSS 포지셔닝의 영향: 다양한 포지셔닝이 레이아웃과 스태킹 컨텍스트에 미치는 영향을 이해하는 것이 중요함
  4. 오버레이 구현 시 주의점: 전체 화면 오버레이 구현 시 부모 요소의 스태킹 컨텍스트를 고려해야 함
  5. 스태킹 컨텍스트의 계층 구조 이해: 부모-자식 관계에 따른 스태킹 컨텍스트의 계층 구조가 z-index 동작에 미치는 영향을 파악하는 것이 중요함