requestAnimationFrame와 passive:true로 스크롤 이벤트 최적화하기
성능 최적화 관련해서 공부하다보면 꼭 있는 내용이 requestAnimationFrame
이다.
사용해 본 경험이 없어 사실상 죽은 지식이였는데, 이번 기회에 사용해보려고 공부를 해보니까 주로 스크롤 이벤트 최적화에 많이 사용한다고 한다.
스크롤 이벤트 최적화를 위해선 EventTarget.addEventListener()
의 pasive 옵션과 requestAnimationFrame
에 대한 이해가 필요하다.
passive
EventTarget.addEventListener()
함수의 세 번째 parameter인 options 객체에는 passive
라는 옵션이 존재한다.
이는 이벤트 핸들러 내부에서 절대 preventDefault()
를 호출하지 않을 것을 나타내는 boolean 값이다.
EventTarget.addEventListener()
를 통해 등록된 이벤트는 컴포지터 스레드가 받는다. 이벤트가 발생하면 컴포지터 스레드는 메인스레드에 이벤트를 넘기고 reflow 또는 repaint가 발생하고 렌더링 파이프라인에 따라 리렌더링 되는 것이 일반적인 과정이다.
하지만 passive
값을 true
로 할 경우 컴포지터 스레드에서 이벤트를 메인스레드에 넘기고 처리를 기다리지 않고 바로 Composite하여 새로운 프레임을 바로 합성하게 된다.
만약 핸들러 내부에 e.preventDefault()
가 존재하여 이를 수행할 경우 메인스레드에서 해당 이벤트 발생시의 기본 동작을 막고 이벤트 핸들러를 수행해야 한다. 하지만 passive
값을 true
로 할 경우 e.preventDefault()
가 핸들러 내부에 존재하지 않는다는 것이 보장되기 때문에 메인스레드의 처리를 기다리지 않고 바로 새로운 프레임을 합성할 수 있게 된다.
명시하지 않아도 최신 브라우저에선 문서 레벨 노드인 Window
Document
Document.body
의 touchstart
이벤트와 touchmove
에선 passive
의 기본 값을 true로 적용하고 있다.
지원하는 브라우저인지 여부는 다음과 같이 확인할 수 있다.(mdn 출처)
1 | /* 기능 감지 */ |
스크롤 이벤트 최적화하기
스크롤 이벤트를 최적화하는 방법에 대해 알아보자
최적화 x
아래는 최적화 없이 작성한 코드이다.
1 | window.addEventListener("scroll", () => console.log("scrolled")); |
위와 같은 이벤트 핸들러를 등록할 시 console.log가 계속 찍히게 된다.
console.log말고 reflow를 발생시키는 callback을 등록했을 경우 브라우저에는 더 많은 과부하가 발생하게 된다.
최적화 적용하기
아래는 최적화를 적용하여 작성한 코드이다.
1 | function optimizeHandler(callback) { |
callback함수를 받아 최적화된 함수를 반환하는 optimizeHandler 함수를 작성하였다.
위의 코드는 다음과 같은 방식으로 작동한다.
- 클로저를 이용할 수 있도록 반환되는 함수 바깥에 tick을 선언한다.
- 반환되는 함수 내부에선 tick이 true일경우 별도의 작업을 하지 않고 얼리리턴한다.
- 반환되는 함수 내부에서 tick이 false일경우 requestAnimation을 호출하여 callback을 수행하고 tick을 true로 바꾼다.