최신 웹브라우저 자세히 살펴보기 (4부)

Mariko Kosaka

입력이 컴포지터로 전송됩니다.

이 글은 Chrome 내부를 살펴보는 4부로 구성된 블로그 시리즈 중 마지막입니다. 처리 방식, 웹사이트를 표시할 수 있습니다. 이전 게시물에서는 렌더링 프로세스를 살펴보고 컴포지터에 관해 알아봤습니다. 이 게시물에서는 컴포지터가 사용자 입력이 들어올 때 어떻게 매끄러운 상호작용을 가능하게 하는지 살펴봅니다.

브라우저 관점에서의 입력 이벤트

'입력 이벤트'라는 음성이 들릴 때 텍스트 상자에 입력하거나 마우스 클릭만 한다고 생각할 수도 있지만 브라우저의 관점에서 입력은 사용자의 모든 동작을 의미합니다. 마우스 휠 스크롤은 이벤트, 터치 또는 마우스오버도 입력 이벤트입니다.

화면 터치와 같은 사용자 동작이 발생하면 브라우저 프로세스는 보여드리겠습니다. 그러나 브라우저 프로세스는 탭 내부의 콘텐츠는 렌더러 프로세스에 의해 처리됩니다. 따라서 브라우저 프로세스는 유형 (예: touchstart) 및 좌표를 렌더기 프로세스에 추가합니다. 렌더기 프로세스는 이벤트 대상을 찾고 연결된 이벤트 리스너를 실행하여 해당 이벤트를 적절히 처리합니다.

<ph type="x-smartling-placeholder">
</ph> 입력 이벤트 <ph type="x-smartling-placeholder">
</ph> 그림 1: 브라우저 프로세스를 통해 렌더기 프로세스로 라우팅되는 입력 이벤트

컴포지터가 입력 이벤트 수신

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> 그림 2: 페이지 레이어 위에 마우스 오버된 표시 영역

이전 게시물에서는 컴포지터가 합성을 통해 스크롤을 원활하게 처리하는 방법을 살펴보았습니다. 레이어가 있습니다 페이지에 연결된 입력 이벤트 리스너가 없는 경우 컴포지터 스레드는 새 합성 프레임을 생성할 수 있습니다. 하지만 어떤 이벤트가 리스너가 페이지에 첨부되었나요? 컴포지터 스레드는 이벤트에 어떻게 해야 할까요?

빠른 스크롤이 불가능한 영역 이해

JavaScript 실행이 기본 스레드의 작업이므로 페이지가 합성될 때 컴포지터 스레드는 는 'Non-Fast Scrollable Region'으로 연결된 이벤트 핸들러가 있는 페이지 영역을 표시합니다. 작성자: 이 정보가 있으면 컴포지터 스레드는 기본 스레드에 입력 이벤트를 보내고 알림을 받을 수 있습니다 입력 이벤트가 이 리전 외부에서 오면 컴포지터 스레드가 기본 스레드를 기다리지 않고 새 프레임 합성을 수행합니다.

<ph type="x-smartling-placeholder">
</ph> 빠르게 스크롤할 수 없는 제한된 영역 <ph type="x-smartling-placeholder">
</ph> 그림 3: 빠른 스크롤 불가능 영역에 관해 설명된 입력 다이어그램

이벤트 핸들러 작성 시 유의사항

웹 개발의 일반적인 이벤트 처리 패턴은 이벤트 위임입니다. 일정이 도움말 풍선이므로 최상위 요소에 하나의 이벤트 핸들러를 연결하고 이벤트 타겟을 기반으로 작업을 위임할 수 있습니다. 나 다음과 같은 코드를 보거나 작성했을 수 있습니다.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault();
    }
});

모든 요소에 대해 하나의 이벤트 핸들러만 작성하면 되므로 이 이벤트의 인체공학은 위임 패턴이 매력적입니다. 하지만 브라우저의 지점에서 이 코드를 보면 뷰, 이제 전체 페이지가 빠르게 스크롤할 수 없는 영역으로 표시됩니다. 다시 말해 애플리케이션이 페이지의 특정 부분으로부터의 입력에 관심이 없다면 컴포지터 스레드는 기본 스레드와 통신하고 입력 이벤트가 수신될 때마다 이를 기다려야 합니다. 따라서 컴포지터의 부드러운 스크롤 기능이 작동하지 않습니다.

<ph type="x-smartling-placeholder">
</ph> 전체 페이지 빠르게 스크롤할 수 없는 영역 <ph type="x-smartling-placeholder">
</ph> 그림 4: 전체 페이지를 덮는 빠른 스크롤 가능하지 않은 영역에 대한 설명된 입력을 보여주는 다이어그램

이 문제를 완화하려면 이벤트에 passive: true 옵션을 전달하면 됩니다. 리스너입니다. 이렇게 하면 기본 스레드에서 여전히 이벤트를 수신하려는 것을 브라우저에 알 수 있습니다. 컴포지터가 새 프레임도 합성할 수 있습니다.

document.body.addEventListener('touchstart', event => {
    if (event.target === area) {
        event.preventDefault()
    }
 }, {passive: true});

이벤트를 취소할 수 있는지 확인

<ph type="x-smartling-placeholder">
</ph> 페이지 스크롤 <ph type="x-smartling-placeholder">
</ph> 그림 5: 페이지 일부가 가로 스크롤로 고정된 웹페이지

스크롤 방향을 가로 스크롤만으로 제한하려는 페이지가 페이지에 있다고 가정해 보겠습니다.

포인터 이벤트에서 passive: true 옵션을 사용하면 페이지 스크롤이 부드러울 수 있지만 스크롤을 제한하기 위해 preventDefault하려는 시간쯤에 세로 스크롤이 시작되었을 수 있습니다. 스크롤 방향입니다. event.cancelable 메서드를 사용하여 이를 확인할 수 있습니다.

document.body.addEventListener('pointermove', event => {
    if (event.cancelable) {
        event.preventDefault(); // block the native scroll
        /*
        *  do what you want the application to do here
        */
    }
}, {passive: true});

또는 touch-action와 같은 CSS 규칙을 사용하여 이벤트 핸들러를 완전히 제거할 수도 있습니다.

#area {
  touch-action: pan-x;
}

이벤트 타겟 찾기

<ph type="x-smartling-placeholder">
</ph> 조회 테스트 <ph type="x-smartling-placeholder">
</ph> 그림 6: 페인트 레코드를 보고 xy 지점에 그려진 내용을 묻는 기본 스레드

컴포지터 스레드가 기본 스레드에 입력 이벤트를 전송할 때 가장 먼저 실행해야 할 작업은 조회 테스트를 사용하여 이벤트 타겟을 찾습니다. 히트 테스트는 렌더링에서 생성된 페인트 레코드 데이터를 사용함 이벤트가 발생한 점 좌표 아래에 무엇이 있는지 알아낼 수 있습니다.

기본 스레드로의 이벤트 전달 최소화

이전 게시물에서 일반적인 디스플레이가 초당 60회 화면을 새로 고치는 방식과 매끄러운 애니메이션을 위한 케이던스를 어떻게 유지해야 하는지에 대해 생각해야 합니다. 입력의 경우 일반적인 터치 스크린은 기기는 초당 60~120번 터치 이벤트를 전달하며, 일반적인 마우스는 1초에 100번 이벤트를 전달합니다. 둘째, 입력 이벤트의 충실도가 화면이 새로고침할 수 있는 것보다 높습니다.

touchmove와 같은 연속 이벤트가 초당 120회 기본 스레드로 전송되었다면 실제 히트 테스트와 자바스크립트 실행을 훨씬 느리게 실행할 때보다 새로고침할 수 있습니다

<ph type="x-smartling-placeholder">
</ph> 필터링되지 않은 이벤트 <ph type="x-smartling-placeholder">
</ph> 그림 7: 프레임 타임라인을 플러딩하여 페이지 버벅거림을 유발하는 이벤트

기본 스레드에 대한 과도한 호출을 최소화하기 위해 Chrome은 연속 이벤트 (예: wheel, mousewheel, mousemove, pointermove, touchmove) 및 발송 지연 다음 requestAnimationFrame 직전에 발생합니다.

<ph type="x-smartling-placeholder">
</ph> 병합된 이벤트 <ph type="x-smartling-placeholder">
</ph> 그림 8: 이전과 동일한 타임라인이지만 이벤트가 병합 및 지연됨

불연속 이벤트(예: keydown, keyup, mouseup, mousedown, touchstart, touchend) 즉시 전달됩니다

getCoalescedEvents를 사용하여 프레임 내 이벤트 가져오기

대부분의 웹 애플리케이션에서는 병합된 이벤트만으로도 우수한 사용자 환경을 충분히 제공할 수 있습니다. 그러나, 애플리케이션을 그리고 기본 언어를 기반으로 경로를 설정하는 등의 작업을 touchmove 좌표의 경우 부드러운 선을 그리기 위해 중간 좌표를 잃을 수도 있습니다. 이 경우 포인터 이벤트에서 getCoalescedEvents 메서드를 사용하여 이러한 객체에 관한 정보를 가져올 수 있습니다. 이벤트를 병합했습니다.

<ph type="x-smartling-placeholder">
</ph> getCoalescedEvents <ph type="x-smartling-placeholder">
</ph> 그림 9: 왼쪽에는 부드러운 터치 동작 경로, 오른쪽에는 제한된 경로 병합
window.addEventListener('pointermove', event => {
    const events = event.getCoalescedEvents();
    for (let event of events) {
        const x = event.pageX;
        const y = event.pageY;
        // draw a line using x and y coordinates.
    }
});

다음 단계

이 시리즈에서는 웹 브라우저의 내부 작동 방식을 다뤘습니다. 그 이유를 전혀 생각해 본 적 없는 경우 DevTools에서는 이벤트 핸들러에 {passive: true}를 추가할 것을 권장합니다. 또는 async를 작성해야 하는 이유는 무엇인가요? 속성에 대한 이해를 넓히는 데 관심이 있으시다면 이 시리즈가 브라우저에 보다 빠르고 원활한 웹 환경을 제공할 수 있습니다.

Lighthouse 사용

코드가 브라우저에 적합하게 만들고 싶지만 어디서부터 시작해야 할지 모르는 경우 Lighthouse는 모든 웹사이트 감사를 실행하고 해야 할 일과 개선이 필요한 부분을 보고할 수 있습니다. 감사 목록 검토 브라우저가 관심을 갖는 것이 무엇인지도 알 수 있습니다.

실적 측정 방법 알아보기

성능 조정은 사이트마다 다를 수 있으므로 실적을 측정하는 것이 중요합니다. 하고 사이트에 가장 적합한 것을 결정할 수 있습니다. Chrome DevTools 팀은 사이트의 실적을 측정하는 방법을 알아보세요.

사이트에 기능 정책 추가

필요한 경우 기능 정책이 웹 플랫폼 기능입니다. 이 기능은 프로젝트를 빌드할 때 안전장치가 될 수 있습니다. 켜는 중 기능 정책은 앱의 특정 동작을 보장하고 실수를 방지합니다. 예를 들어 앱이 파싱을 차단하지 않게 하려면 동기 스크립트 정책 sync-script: 'none'가 사용 설정되면 파서 차단 JavaScript가 다음을 실행합니다. 실행할 수 없습니다 이렇게 하면 어떤 코드도 파서를 차단하지 않으며 브라우저는 파서 일시중지에 대해 걱정할 필요가 없습니다.

마무리

감사합니다

웹사이트를 구축하기 시작했을 때는 코드를 작성하는 방법과 생산성 향상에 도움이 될 것입니다. 이러한 것들이 중요하지만 브라우저는 우리가 작성한 코드를 사용합니다. 최신 브라우저는 사용자에게 더 나은 웹 환경을 제공할 수 있습니다. 코드를 정리하여 브라우저에 친절하게 맞추는 것은 사용자 환경이 개선됩니다. 브라우저를 원활하게 지원하는 퀘스트에 동참해 주시길 바랍니다.

이 시리즈의 초안을 작성해 주신 모든 분께 진심으로 감사드립니다. 알렉스 러셀, 폴 아이리시, 메긴 키어니, 에릭 비델만, Mathias Bynens, 애디 오스마니, 야스다 키누코, 나스코 오스코프, 찰리 라이스(Charlie Reis)를 만나볼 수 있습니다.

이 시리즈가 마음에 드셨나요? 향후 게시물에 관한 질문이나 제안이 있으면 아래 댓글 섹션이나 @kosamari로 의견을 보내주세요. 을 참고하세요.