게시일: 2026년 3월 27일
요소 범위 뷰 전환을 사용하면 여러 뷰 전환을 동시에 실행하고, 진행 중인 뷰 전환을 다른 뷰 전환 내에 중첩하고, 문서 범위 뷰 전환에서 발생할 수 있는 z-index 문제를 해결할 수 있습니다. 이 모든 작업은 페이지의 나머지 부분을 대화형으로 유지하면서 이루어집니다. 이 가이드를 읽고 사용 방법을 알아보세요.
범위가 더 좁은 뷰 전환의 필요성
document.startViewTransition()를 사용하거나 document.startViewTransition()의 교차 문서 대응 항목을 통해 동일 문서 뷰 전환을 시작하면 브라우저가 결과 뷰 전환을 문서로 범위 지정합니다.
업데이트 콜백이 실행되고 브라우저가 필요한 모든 요소를 스냅샷하면 결과 ::view-transition 오버레이와 의사 요소 트리가 :root 요소(다음 예의 html)에 연결됩니다.
html
├─ ::view-transition
│ └─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
├─ head
└─ body
└─ …
::view-transition 레이어가 전환 루트 위에 렌더링되므로 예기치 않은 상황이 발생할 수 있습니다. 예를 들어 뷰 전환에 참여하는 요소가 갑자기 다른 비참여 요소와 겹치거나 뷰 전환 중에 요소가 더 이상 상위 래퍼에 의해 잘리지 않을 수 있습니다.
실시간 데모
데모 녹화
::view-transition에서 pointer-events을 다시 사용 설정하거나 중첩된 뷰 전환 그룹을 사용하면 문서 범위 뷰 전환으로 인해 발생하는 일부 부작용을 해결할 수 있습니다. 하지만 이러한 방법으로 모든 문제를 해결할 수는 없습니다.
예를 들어 position: fixed가 있는 요소나 팝오버는 전환이 활성 상태인 동안 문서 범위 뷰 전환에 의해 가려집니다. 이를 z-index 문제라고도 합니다.
다음 데모에서 팝오버를 전환한 다음 셔플 버튼을 선택하여 문서 범위 뷰 전환을 시작합니다. 중첩된 뷰 전환 그룹은 클리핑 문제를 해결하지만 레이어링 문제는 그대로 남아 있습니다.
실시간 데모
데모 녹화
한 가지 해결 방법은 view-transition-name을 제공하여 뷰 전환의 일부로 popover를 캡처하는 것입니다. 단일 인스턴스에서는 작동할 수 있지만 유지관리하기가 번거롭고 스냅샷 프로세스에 불필요한 부담을 줍니다.
요소 범위 뷰 전환
요소 범위 뷰 전환을 사용하면 DOM의 하위 트리에서 뷰 전환을 시작할 수 있습니다. document.startViewTransition()를 호출하는 대신 임의의 요소에서 element.startViewTransition()를 호출하여 뷰 전환을 해당 요소로 범위 지정합니다.
다음 스니펫에서 브라우저는 <ul> 요소에서 요소 범위 뷰 전환을 시작합니다.
document.querySelector('ul').startViewTransition({
callback: () => {
// … code that manipulates the contents of <ul>
},
})
element.startViewTransition()를 호출하는 요소(예: <ul>)를 전환 루트 또는 범위라고 합니다.
브라우저가 뷰 전환의 범위를 요소로 지정하면 DOM의 나머지 부분에서 격리됩니다.
- 브라우저는 스냅샷을 찍을 요소를 범위의 하위 트리 내에서만 찾습니다.
- 스냅샷 생성 프로세스 중(
update콜백이 실행되는 동안) 범위 렌더링만 중지됩니다. - 결과
::view-transition의사 트리는 전환 루트에 삽입됩니다.
예를 들어 <ul>를 사용하면 뷰 전환이 활성 상태일 때 DOM 트리는 다음과 같습니다.
html
├─ head
└─ body
├─ ul
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(root)
│ │ ├─ ::view-transition-group-children(root)
│ │ │ └─ …
│ │ └─ ::view-transition-image-pair(root)
│ │ ├─ ::view-transition-old(root)
│ │ └─ ::view-transition-new(root)
│ ├─ li
│ ├─ li
│ └─ li
├─ button#showpopover
├─ button#reorder
└─ div#popover
└─ p
::view-transition 의사 요소는 전환 루트와 크기와 모양이 동일하며 전환 루트 위에만 렌더링됩니다. 따라서 전환 루트 외부에 있는 요소의 레이어링 순서가 적용됩니다.
예를 들어 <ul> 요소 위에 표시되는 팝오버가 있고 <ul> 요소에서 요소 범위 뷰 전환을 시작하면 팝오버가 뷰 전환의 의사 트리로 가려지지 않습니다.
다음 데모에서 사용해 보세요. 버튼이 두 개 있습니다. 첫 번째 버튼은 팝오버를 전환하고 두 번째 버튼은 요소 범위 뷰 전환을 사용하여 목록 항목을 재정렬합니다.
실시간 데모
데모 녹화
요소 범위 뷰 전환이 사용되므로 전환이 활성 상태인 동안 팝오버가 <ul> 요소 위에 표시된 상태로 유지됩니다.
또한 <ul> 요소 외부의 요소(예: 버튼)는 범위에 포함되지 않으므로 상호작용이 가능한 상태로 유지됩니다.
자체 참여 범위 및 중첩된 뷰 전환 그룹
오버플로를 클리핑하는 요소 (즉, overflow이 hidden, scroll 또는 clip로 설정된 경우)에서 요소 범위 뷰 전환을 시작하면 뷰 전환의 콘텐츠가 시각적으로 클리핑된 상태로 유지됩니다.
요소 범위 뷰 전환은 다음을 자동으로 처리하기 때문입니다.
- 범위에
view-transition-name: root이 자동으로 적용되어 자체 참여가 가능합니다. - 범위가 자동으로 적용되어 중첩된 뷰 전환 그룹이 사용 설정됩니다.
view-transition-group: contain - 결과
::view-transition-group-children(root)의사 요소는 범위 루트가 오버플로를 클리핑하는 경우overflow: clip을 사용하여 콘텐츠를 자동으로 클리핑하므로 의사 요소가 전환 루트에서 시각적으로 번지는 것을 방지합니다.
따라서 요소 범위 뷰 전환에 사용하는 CSS를 캡처하려는 요소에만 집중할 수 있습니다. 예를 들어 목록 데모에서 CSS는 목록 항목에 이름만 추가합니다.
ul li {
view-transition-name: match-element;
view-transition-class: album;
}
다음 데모에서 사용해 보세요. 이를 통해 자체 참여를 재정의할 수 있습니다. 범위가 자체 참여인 경우 (기본 동작) 모든 것이 예상대로 작동합니다. 범위가 자체 참여하지 않는 경우 테두리가 즉시 변경되고 전환 중에 콘텐츠가 래퍼에서 번집니다.
실시간 데모
데모 녹화
참고로 자체 참여가 있는 이 데모의 의사 트리는 다음과 같습니다.
html
├─ head
└─ body
├─ ul
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(root)
│ │ ├─ ::view-transition-group-children(root)
│ │ │ ├─ ::view-transition-group(item1)
│ │ │ │ └─ ::view-transition-image-pair(item1)
│ │ │ │ ├─ ::view-transition-old(item1)
│ │ │ │ └─ ::view-transition-new(item1)
│ │ │ ├─ ::view-transition-group(item2)
│ │ │ │ └─ …
│ │ │ …
│ │ └─ ::view-transition-image-pair(root)
│ │ ├─ ::view-transition-old(root)
│ │ └─ ::view-transition-new(root)
│ ├─ li
│ ├─ li
│ └─ li
└─ button#reorder
전환 루트인 <ul> 요소가 콘텐츠를 세로로 클리핑하므로 ::view-transition-group-children(root)도 자동으로 클립을 적용합니다.
동시 요소 범위 뷰 전환
요소 범위 뷰 전환은 격리된 상태로 실행되므로 범위가 다른 경우 여러 요소 범위 뷰 전환을 동시에 실행할 수 있습니다.
다음 데모에는 각 목록에 하나씩 두 개의 재정렬 버튼이 있습니다. 각 버튼은 해당 목록에서만 요소 범위 뷰 전환을 시작합니다. 두 목록의 DOM 트리가 겹치지 않으므로 두 요소 범위 뷰 전환이 격리된 상태로 동시에 실행될 수 있습니다.
실시간 데모
데모 녹화
또한 이러한 격리된 특성으로 인해 여러 범위에서 view-transition-name 값을 재사용할 수 있습니다. 이름이 범위 내에서 고유한 한 충돌은 없습니다.
중첩된 요소 범위 뷰 전환 및 view-transition-name 포함
여러 요소 범위 뷰 전환의 DOM 트리가 겹치면 view-transition-name 값 충돌이 발생할 위험이 있습니다. 이러한 이유로 브라우저는 이 위험을 완화하기 위해 활성 요소 범위 뷰 전환에 view-transition-scope: all를 자동으로 할당합니다.
anchor-scope이 anchor-name 값을 범위 지정하는 방식과 마찬가지로 view-transition-scope 속성은 view-transition-name 값이 요소의 하위 트리에 범위 지정되도록 합니다. 이 속성은 범위를 지정할 이름 목록인 none 또는 모든 값의 범위를 지정하는 all를 허용합니다.
view-transition-scope는 이름이 번지는 것을 방지할 뿐만 아니라 요소와 콘텐츠가 외부의 동시 뷰 전환에 의해 캡처되는 것도 방지합니다. 스냅샷 프로세스가 스냅샷을 만들 요소를 찾기 위해 하위 트리를 탐색할 때 view-transition-scope: all가 적용된 요소 (및 전체 하위 트리)는 무시됩니다. 이는 이러한 요소가 이미 다른 요소 범위 뷰 전환에 참여하고 있다고 가정합니다.
다음 데모는 이전 데모의 변형입니다. 목록 콘텐츠를 셔플하는 두 개의 버튼 외에도 목록을 바꾸는 바꾸기 버튼이 있습니다. #lists-wrapper에서 .reversed 클래스를 전환하면 스왑이 처리됩니다.
실시간 데모
데모 녹화
view-transition-scope: all는 셔플 전환 중에 자동으로 적용되므로 셔플 전환이 아직 진행 중인 동안 동시 외부 스왑 전환을 시작할 수 있습니다.
view-transition-scope: all는 요소가 외부 전환에서 스냅샷으로 저장되는 것을 방지하므로 데모에서는 <ul> 요소를 래핑하는 요소에 view-transition-name 값도 추가합니다.
#list1-wrapper, #list2-wrapper {
view-transition-name: attr(id type(<custom-ident>));
}
두 번째 목록에서 셔플을 시작한 다음 두 목록을 모두 바꾼 후 이 데모의 의사 트리는 다음과 같습니다.
html
├─ head
└─ body
└─ #lists-wrapper.reversed (SCOPE)
├─ ::view-transition
│ └─ ::view-transition-group(lists-wrapper)
│ ├─ ::view-transition-group-children(lists-wrapper)
│ │ ├─ ::view-transition-group(list1-wrapper)
│ │ │ └─ ::view-transition-image-pair(list1-wrapper)
│ │ │ ├─ ::view-transition-old(list1-wrapper)
│ │ │ └─ ::view-transition-new(list1-wrapper)
│ │ └─ ::view-transition-group(list2-wrapper)
│ │ └─ ::view-transition-image-pair(list2-wrapper)
│ │ ├─ ::view-transition-old(list2-wrapper)
│ │ └─ ::view-transition-new(list2-wrapper)
│ └─ ::view-transition-image-pair(lists-wrapper)
│ ├─ ::view-transition-old(lists-wrapper)
│ └─ ::view-transition-new(lists-wrapper)
├─ div#list1-wrapper
│ ├─ ul
│ │ ├─ li#item1
│ │ ├─ li#item2
│ │ └─ li#item3
│ └─ button.reorder
└─ div#list2-wrapper
├─ ul (SCOPE)
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(list)
│ │ ├─ ::view-transition-group-children(list )
│ │ │ ├─ ::view-transition-group(item4)
│ │ │ │ └─ ::view-transition-image-pair(item4)
│ │ │ │ ├─ ::view-transition-old(item4)
│ │ │ │ └─ ::view-transition-new(item4)
│ │ │ ├─ ::view-transition-group(item5)
│ │ │ │ └─ …
│ │ │ …
│ │ └─ ::view-transition-image-pair(list)
│ │ ├─ ::view-transition-old(list)
│ │ └─ ::view-transition-new(list)
│ ├─ li#item4
│ ├─ li#item5
│ └─ li#item6
└─ button.reorder
자세히 알아보기
요소 범위 뷰 전환에 대해 자세히 알아보려면 설명, css-view-transitions-2 사양, 열린 사양 수정사항 목록을 참고하세요.