Chrome DevTools의 최상위 레이어 지원

Alina Varkki
Alina Varkki

Chrome DevTools에 최상위 레이어 요소에 대한 지원이 추가되므로 개발자가 최상위 레이어 요소를 사용하는 코드를 더 쉽게 디버그할 수 있습니다.

이 문서에서는 최상위 레이어 요소가 무엇인지, DevTools가 최상위 레이어 콘텐츠를 시각화하여 최상위 레이어 요소가 포함된 DOM 구조를 이해하고 디버그하는 데 도움을 주는 방법, DevTools 최상위 레이어 지원이 구현되는 방법을 설명합니다.

최상위 레이어와 상단 레이어 요소는 무엇인가요?

<dialog>를 모달로 열면 내부적으로 정확히 어떤 일이 발생하나요? 🤔

최상위 레이어에 배치됩니다. 상단 레이어 콘텐츠는 다른 모든 콘텐츠 위에 렌더링됩니다. 예를 들어 모달 대화상자는 다른 모든 DOM 콘텐츠 위에 나타나야 하므로, 브라우저가 이 요소를 '상단 레이어'에서 자동으로 렌더링합니다. 따라서 작성자가 수동으로 Z-인덱스와 경쟁하지 않아도 됩니다. 상단 레이어 요소는 Z-색인이 가장 높은 경우에도 요소 위에 표시됩니다.

최상위 레이어는 '가장 높은 스태킹 레이어'로 설명할 수 있습니다. 각 문서에는 연결된 단일 표시 영역 및 하나의 상단 레이어도 있습니다. 여러 요소가 동시에 상단 레이어 내에 있을 수 있습니다. 이러한 상황이 발생하면 두 레이어가 서로 겹치고 마지막 항목이 위에 겹치게 됩니다. 즉, 모든 최상위 레이어 요소가 상단 레이어의 후입선출 (LIFO) 스택에 배치됩니다.

<dialog> 요소는 브라우저가 최상위 레이어에 렌더링하는 유일한 요소가 아닙니다. 현재 최상위 레이어 요소는 팝오버, 모달 대화상자, 전체 화면 모드의 요소입니다.

다음 대화상자 구현을 검토합니다.

<main>
  <button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>

다음은 배경화면에 스타일이 적용된 대화상자가 몇 개 있는 데모입니다 (아래에 배경 설명 참고).

배경화면이란 무엇인가요?

다행히 상단 레이어 요소 아래의 콘텐츠를 맞춤설정할 수 있는 방법이 있습니다.

상단 레이어의 모든 요소에는 배경화면이라고 하는 CSS 의사 요소가 있습니다.

배경화면은 표시 영역 크기의 상자로, 맨 위 레이어 요소 바로 아래에 렌더링됩니다. ::backdrop 유사 요소를 사용하면 요소가 맨 위 레이어에서 맨 위에 있는 경우 그 아래에 있는 모든 항목을 가리거나 스타일을 지정하거나 완전히 숨길 수 있습니다.

여러 요소를 모달로 만들면 브라우저에서는 이러한 요소 바로 아래와 다른 전체 화면 요소 위에 배경을 그립니다.

배경화면 스타일을 지정하는 방법은 다음과 같습니다.

/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
    background: rgba(255,0,0,.25);
}

첫 번째 배경화면만 표시하는 방법

모든 상단 레이어 요소에는 상단 레이어 스택에 속한 배경화면이 있습니다. 이러한 배경화면은 서로 겹치도록 설계되어 있습니다. 따라서 배경화면의 불투명도가 100%가 아닌 경우 그 아래의 배경화면이 표시됩니다.

상단 레이어 스택의 첫 번째 배경화면만 표시해야 하는 경우 상단 레이어 스택의 항목 식별자를 추적하여 표시할 수 있습니다.

추가된 요소가 상단 레이어의 첫 번째 요소가 아닌 경우 해당 요소가 최상위 레이어에 배치될 때 호출되는 함수는 hiddenBackdrop 클래스를 ::backdrop에 적용합니다. 이 클래스는 요소가 상단 레이어에서 삭제되면 삭제됩니다.

이 데모 예의 코드를 확인합니다.

DevTools의 최상위 레이어 지원 디자인

DevTools에서 최상위 레이어에 대한 지원을 통해 개발자는 최상위 레이어의 개념을 이해하고 최상위 레이어 콘텐츠가 어떻게 변경되는지 시각화할 수 있습니다. 이러한 기능을 통해 개발자는 다음을 식별할 수 있습니다.

  • 모든 시점의 상단 레이어에 있는 요소 및 요소의 순서
  • 어느 시점에서든 스택 상단에 있는 요소입니다.

또한 DevTools 최상위 레이어 지원을 통해 상단 레이어 스택에서 배경화면 의사 요소의 위치를 시각화할 수 있습니다. 트리 요소는 아니지만 최상위 레이어의 작동 방식에 중요한 역할을 하며 개발자에게 유용할 수 있습니다.

최상위 레이어 지원 기능을 사용하면 다음을 수행할 수 있습니다.

  1. 언제든지 상단 레이어 스택에 있는 요소를 관찰합니다. 상단 레이어 표현 스택은 요소가 상단 레이어에서 추가되거나 제거됨에 따라 동적으로 변경됩니다.
  2. 상단 레이어 스택에서 요소 위치를 확인합니다.
  3. 트리에 있는 최상위 레이어 요소 또는 요소의 배경 가상 요소에서 상단 레이어 표현 컨테이너의 요소 또는 배경화면 유사 요소로 이동한 다음 다시 그 뒤로 이동합니다.

이러한 기능의 사용 방법을 살펴보겠습니다.

상단 레이어 컨테이너

상단 레이어 요소를 시각화하기 위해 DevTools가 요소 트리에 상단 레이어 컨테이너를 추가합니다. 닫는 </html> 태그 뒤에 있습니다.

이 컨테이너를 사용하면 언제든지 상단 레이어 스택에 있는 요소를 관찰할 수 있습니다. 상단 레이어 컨테이너는 상단 레이어 요소 및 배경화면으로 연결되는 링크의 목록입니다. 상단 레이어 표현 스택은 요소가 상단 레이어에서 추가되거나 제거됨에 따라 동적으로 변경됩니다.

요소 트리 또는 상단 레이어 컨테이너 내에서 상단 레이어 요소를 찾으려면 상단 레이어 컨테이너의 상단 레이어 요소 표현에서 요소 트리의 동일한 요소로 연결되는 링크를 클릭하고 다시 돌아오면 됩니다.

상단 레이어 컨테이너 요소에서 상단 레이어 트리 요소로 이동하려면 상단 레이어 컨테이너의 요소 옆에 있는 표시 버튼을 클릭합니다.

상단 레이어 컨테이너 링크에서 요소로 이동합니다.

상단 레이어 트리 요소에서 상단 레이어 컨테이너의 링크로 이동하려면 요소 옆에 있는 상단 레이어 배지를 클릭합니다.

요소에서 상단 레이어 컨테이너 링크로 건너뜁니다.

최상위 레이어 배지를 비롯한 모든 배지를 사용 중지할 수 있습니다. 배지를 사용 중지하려면 배지를 마우스 오른쪽 버튼으로 클릭하고 배지 설정을 선택한 다음 숨기려는 배지 옆의 체크표시를 지웁니다.

배지 사용 중지

상단 레이어 스택의 요소 순서

상단 레이어 컨테이너는 스택에 나타나는 요소를 역순으로 표시합니다. 스택 요소의 상단은 상단 레이어 컨테이너의 요소 목록에서 마지막 요소입니다. 즉, 상단 레이어 컨테이너 목록의 마지막 요소가 현재 문서에서 상호작용할 수 있는 요소입니다.

트리 요소 옆에 있는 배지는 요소가 최상위 레이어에 속하고 스택에서 요소의 위치 번호를 포함하는지 여부를 나타냅니다.

이 스크린샷에서 상단 레이어 스택은 두 요소로 구성되며 두 번째 요소는 스택 상단에 있습니다. 두 번째 요소를 삭제하면 첫 번째 요소가 맨 위로 이동합니다.

스택에 있는 요소의 순서입니다.

상단 레이어 컨테이너의 배경화면

위에서 언급했듯이 모든 상단 레이어 요소에는 백드롭이라는 CSS 의사 요소가 있습니다. 이 요소의 스타일을 지정할 수 있으므로 요소를 검사하고 표현을 확인하는 것도 유용합니다.

요소 트리에서 백드롭 요소는 속해 있는 요소의 닫는 태그 앞에 있습니다. 그러나 상단 레이어 컨테이너에서는 백드롭 링크가 속한 최상위 레이어 요소 바로 위에 백드롭 링크가 나열됩니다.

배경화면 스택 위치

DOM 트리 변경사항

DevTools에서 개별 DOM 트리 요소를 만들고 관리하는 클래스인 ElementsTreeElement는 최상위 레이어 컨테이너를 구현하기에 충분하지 않았습니다.

최상위 레이어 컨테이너를 트리의 노드로 표시하기 위해 DevTools 트리 요소 노드를 생성하는 새 클래스를 추가했습니다. 이전에는 DevTools 요소 트리를 만드는 클래스가 DOMNode(backendNodeId 및 기타 백엔드 관련 속성이 있는 클래스)로 TreeElement마다 초기화되었습니다. 그러면 backendNodeId가 백엔드에 할당됩니다.

최상위 레이어 요소로 연결되는 링크 목록이 있는 상단 레이어 컨테이너 노드는 일반 트리 요소 노드 역할을 해야 했습니다. 그러나 이 노드는 '실제' DOM 노드가 아니며 백엔드는 최상위 레이어 컨테이너 노드를 만들 필요가 없습니다.

최상위 레이어를 나타내는 프런트엔드 노드를 만들기 위해 DOMNode 없이 생성되는 새로운 유형의 프런트엔드 노드를 추가했습니다. 이 최상위 레이어 컨테이너 요소는 DOMNode가 없는 첫 번째 프런트엔드 노드입니다. 즉, 프런트엔드에만 존재하고 백엔드는 이를 '인식'하지 않습니다. 다른 노드와 동일한 동작을 하기 위해 프런트엔드 노드의 동작을 담당하는 UI.TreeOutline.TreeElement 클래스를 확장하는 새로운 TopLayerContainer 클래스를 만들었습니다.

원하는 배치를 달성하기 위해 요소를 렌더링하는 클래스는 TopLayerContainer<html> 태그의 다음 동위 요소로 연결합니다.

새로운 상단 레이어 배지는 요소가 상단 레이어에 있으며 TopLayerContainer 요소에서 이 요소의 바로가기 링크 역할을 함을 나타냅니다.

초기 디자인

처음에는 요소에 대한 링크 목록을 만드는 대신 최상위 레이어 요소를 상단 레이어 컨테이너에 복제할 계획입니다. 이 솔루션은 DevTools에서 요소의 하위 요소를 가져오는 작업이 작동하는 방식 때문에 구현하지 않았습니다. 각 요소에는 하위 요소를 가져오는 데 사용되는 상위 포인터가 있으며 여러 포인터를 보유할 수 없습니다. 따라서 트리의 여러 위치에 있는 모든 하위 요소가 올바르게 펼쳐지고 포함되는 노드는 보유할 수 없습니다. 일반적으로 시스템은 중복 하위 트리를 염두에 두고 빌드되지 않았습니다.

우리가 도달한 침해는 노드를 복제하는 대신 프런트엔드 DOM 노드에 대한 링크를 생성하는 것이었습니다. DevTools의 요소 링크를 만드는 클래스는 ShortcutTreeElement로, UI.TreeOutline.TreeElement를 확장합니다. ShortcutTreeElement의 동작은 다른 DevTools DOM 트리 요소와 동일하지만 백엔드에 상응하는 노드가 없고 ElementsTreeElement에 연결되는 버튼이 있습니다. 최상위 레이어 노드의 각 ShortcutTreeElement에는 하위 ShortcutTreeElement가 있습니다. 이 하위 요소는 DevTools DOM 트리의 ::backdrop 의사 요소의 표현으로 연결됩니다.

초기 디자인:

초기 디자인

Chrome DevTools 프로토콜 (CDP) 변경사항

최상위 레이어 지원을 구현하려면 Chrome DevTools 프로토콜 (CDP)을 변경해야 합니다. CDP는 DevTools와 Chromium 간의 통신 프로토콜 역할을 합니다.

다음을 추가해야 합니다.

  • 언제든지 프런트엔드에서 호출할 명령어입니다.
  • 백엔드 측에서 프런트엔드에서 트리거할 이벤트입니다.

CDP: DOM.getTopLayerElements 명령어

현재 최상위 레이어 요소를 표시하려면 최상위 레이어에 있는 요소의 노드 ID 목록을 반환하는 새로운 실험용 CDP 명령어가 필요합니다. DevTools는 DevTools가 열리거나 최상위 레이어 요소가 변경될 때마다 이 명령어를 호출합니다. 명령어는 다음과 같습니다.

  # Returns NodeIds of the current top layer elements.
  # Top layer renders closest to the user within a viewport, therefore, its elements always
  # appear on top of all other content.
  experimental command getTopLayerElements
    returns
      # NodeIds of the top layer elements.
      array of NodeId nodeIds

CDP: 이벤트 DOM.topLayerElementsUpdated

상단 레이어 요소의 최신 목록을 가져오려면 상단 레이어 요소를 변경할 때마다 실험용 CDP 이벤트를 트리거해야 합니다. 이 이벤트는 프런트엔드에 변경사항을 알리고 프런트엔드는 DOM.getTopLayerElements 명령어를 호출하고 새 요소 목록을 수신합니다.

이벤트는 다음과 같습니다.

  # Called by the change of the top layer elements.
  experimental event topLayerElementsUpdated

CDP 고려사항

상단 레이어의 CDP 지원을 구현하는 방법에는 여러 옵션이 있었습니다. 우리가 고려한 또 다른 옵션은 프런트 엔드에 맨 위 레이어 요소의 추가 또는 삭제에 관해 알리는 것이 아니라 최상위 레이어 요소의 목록을 반환하는 이벤트를 만드는 것입니다.

또는 명령어 대신 두 개의 이벤트(topLayerElementAddedtopLayerElementRemoved)를 만들 수 있습니다. 이 경우 요소를 수신하게 되며 프런트 엔드의 맨 위 레이어 요소의 배열을 관리해야 합니다.

현재 프런트엔드 이벤트는 getTopLayerElements 명령어를 호출하여 업데이트된 요소 목록을 가져옵니다. 이벤트가 트리거될 때마다 변경의 원인이 된 요소 목록이나 특정 요소를 전송하려는 경우 명령어를 호출하는 한 단계를 피할 수 있습니다. 그러나 이 경우 프런트엔드는 푸시되는 요소를 제어할 수 없게 됩니다.

우리는 이러한 방식으로 구현했습니다. 프런트엔드가 최상위 레이어 노드를 요청할 시기를 결정하는 것이 더 좋다고 생각하기 때문입니다. 예를 들어, UI에서 맨 위 레이어가 축소되었거나 사용자가 요소 트리가 없는 DevTools 패널을 사용 중인 경우 트리 더 깊은 곳에 있을 수 있는 추가 노드를 가져올 필요가 없습니다.