본문 바로가기
토이 프로젝트/포트폴리오 웹사이트

헤더 스크롤 이벤트 및 스로틀링

by 강물둘기 2023. 9. 27.

 

헤더 버튼 클릭시 해당 요소로 이동

포트폴리오에서 헤더를 만들었는데, 헤더의 특정 버튼을 누르면 해당 요소로 자연스럽게 스크롤을 이동하는 효과를 주고 싶었다. 

 처음 생각한 방법은 해당 컴포넌트에 ref를 지정해서 해당 컴포넌트의 y축을 계산하고, 계산된 y축 좌표로 스크롤을 이동시키는 것이었다. 

 

 여기서 문제가 된것이 컴포넌트에 ref를 지정하려고 했는데, 

1. 컴포넌트 자체에 ref를 지정할수 없다.

2. props로 하위 컴포넌트로 ref를 넘길 수 없다.

라는 문제를 만나게 되었다...

 

 두번째 문제를 구글링 해 보니  forwardRef를 사용하면 컴포넌트의 두번째 인자로 ref를 넘길 수 있다고 한다. 그래서 열심히 forwardRef를 사용하여 ref를 넘기고, 스크롤 이벤트를 구현하려고 구글링을 하다가 더 좋은 방법(아래 블로그)을 발견했다. 

 

https://inthedev.tistory.com/27

 

[React] useRef 이용해서 스크롤 특정 위치로 이동시키기

useRef 이용해서 스크롤 특정 위치로 이동시키기 바닐라 자바스크립트에서는 querySelector를 사용하여 DOM에 접근하지만 React에서는 useRef로 DOM에 접근한다. 특정 컴포넌트에 동작을 수행하고 싶다면

inthedev.tistory.com

 

 이벤트가 필요한 컴포넌트(나에게는 App 컴포넌트)에 ref를 선언하고, 빈 div 태그로 컴포넌트를 한 번 감싸서 ref를 지정하는 것이다. 

이러면 굳이 forwardRef로 ref를 넘길 필요없이 바로 ref를 적용할 수 있어서 좀 더 편한 것 같다. ref같은 경우에도 확장성을 고려해서 컴포넌트당 하나씩 ref로 만드는 것이 아니라 배열 형태로 만들어 ref의 current에 할당한다.

 

그리고 스크롤 이벤트 핸들러 같은경우 다음과 같은 형태로 만들었다.

 

버튼이 컴포넌트와 똑같은 이름으로 되어있기 때문에 누른 버튼의 innerText의 번호로 스크롤이 이동하도록 scrollIntoView라는 메서드를 사용했다. (상위 div에 이벤트를 등록해서 이벤트 버블링을 활용하였다.)

 

https://developer.mozilla.org/ko/docs/Web/API/Element/scrollIntoView

 

element.scrollIntoView - Web API | MDN

Element (en-US) 인터페이스의 scrollIntoView() 메소드는 scrollIntoView()가 호출 된 요소가 사용자에게 표시되도록 요소의 상위 컨테이너를 스크롤합니다.

developer.mozilla.org

element.scrollIntoView는 Web API로 해당 엘리먼트로 스크롤을 이동시킨다. 옵션을 줄 수 있는데, 그중에 behavior를 smooth로 줘서 부드러운 이동효과를 적용했다.

 

 

스크롤 이벤트와 스로틀링

헤더가 처음에는 투명한 색이었다가 일정 스크롤을 내리게 되면 색을 부여하는 이벤트를 만들어보기로 했다. 

이벤트 자체는 쉽게 구현했다. 스크롤 할 때 window.scrollY 로 Y축 좌표을 받아서 Y축 좌표가 300px이상이되면 헤더의 색을 transparent에서 특정 색상으로 바꾸어 주는 것이다.

 

문제가 되었던 건 스크롤 이벤트의 경우 Y축이 변경될 때마다 호출이 되어서 스크롤을 하면 엄청난 숫자의 이벤트들이 발생하는 것이었다. (실제로 console.log를 찍어보니 조금만 이동해도 로그가 와바바바박 찍혔다...)

 

이런 과도한 이벤트발생을 제어하기 위해서 스로틀링을 적용해보기로 했다. (책에서 보기만 하고 실제로는 처음 구현해보았다.)

 

스로틀링 구현에 requestAnimationFrame을 활용해보기로 했다.

https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame

 

Window: requestAnimationFrame() method - Web API | MDN

화면에 애니메이션을 업데이트할 준비가 될 때마다 이 메서드를 호출해야 합니다. 이는 브라우저가 다음 리페인트를 수행하기 전에 애니메이션 함수를 호출하도록 요청합니다. 콜백의 수는 보

developer.mozilla.org

 

원래는 animation을 만들 때 쓰는 메서드 같은데 스로틀링을 구현할때도 사용한다고 한다.

requestAnimationFrame을 사용하면 인자로 들어오는 콜백함수를 브라우저의 렌더링 주기에 맞춰 다음 번 repaint가 실행되기 전에 호출한다. 보통은 모니터 주사율(60fps, 144fps 등)에 맞춰 진다고 한다. 

그러니까 requestAnimationFrame을 사용하면 이벤트 발생을 1초에 60번 정도로 조절하는 것이 가능하다.

 

 

이벤트 핸들러는 위와 같은 형태로 구현했다. 간단한 설명을 해보자면 scheduledAnimationFrame이라는 변수가 false일 때 이벤트가 실행이 되고, true라면 이벤트 발생을 막는 형태이다. 

requestAnimationFrame는 비동기로 실행이 되기 때문에 등록된 requestAnimationFrame이 실행이 되면서 콜백함수 처리를 한 후 다시 scheduledAnimationFrame를 true로 만들어서 새로운 이벤트를 받을 수 있는 상태로 다시 돌려놓는다.

 

300px 넘게 스크롤을 하면 헤더색을 바꿔주도록 styled-component를 지정해준다.

 

 

 

 

** requestAnimationFrame를 공부하면서 알게 된 사실인데, requestAnimationFrame은 비동기 메서드로 처리되는데 특이하게도 태스크 큐나 마이크로태스크 큐에 등록이 되는것이 아니라 독자적인 큐에 등록되어 처리된다고 한다. (W3C에는 animation frame request callback list라고 적혀있다.)

더보기

The requestAnimationFrame method is used to signal to the user agent that a script-based animation needs to be resampled. When requestAnimationFrame(callback) is called, the user agent must schedule a script-based animation resampling by appending to the end of the animation frame request callback list an entry whose handle is a user-agent-defined integer greater than zero that uniquely identifies the entry in the list and whose callback is callback.

 

 

Reference

- https://inthedev.tistory.com/27

- https://developer.mozilla.org/ko/docs/Web/API/Element/scrollIntoView

- https://lazywon.tistory.com/53

- https://iborymagic.tistory.com/142

댓글