채용 과제를 만들면서 틈틈이 파이널 프로젝트 개선 작업을 진행하고 있다. 사각사각의 랜딩 페이지를 수정하면서 커스텀 훅을 하나 만들었는데, 이 훅이 하는 일이 바로 마치 스크롤링을 할 때마다 무언가(?!)에 맞추어 페이지 요소들이 짠!하고 나타나는 것처럼 보이는 애니메이션을 추가하는 것이다.
백문이 불여일견! 애니메이션 추가 Before와 After를 살펴보자.
<Before>
<After>
완전 동일한 디자인이지만, 화면에 표시되는 엘리먼트들에 스크롤링에 따른 slide-in 애니메이션을 추가했더니 조금 더 역동적인 모습이 연출되는 것 같다. 물론 메인 랜딩 디자인은 수정할 예정이지만, 그럼에도 불구하고 나의 커스텀 훅은 길이길이 살아남을 수 있으니 너무 행복하다. 행복을 주는 이 커스텀훅의 핵심이 바로 Intersection Observer API이다.
Intersection Observer API란? (출처: MDN)
Intersection Observer API는, 내가 관찰하고 싶은 특정 타겟 엘리먼트가 자기 자신의 조상 엘리먼트들 중 정해진 누군가 또는 최상위 document의 viewport와 교차하는 여부에 변화가 생겼는지를 비동기적으로 감지하는 API이다.
Intersection Observer API가 생기기 이전엔 스크롤링 이벤트로 위와 같은 애니메이션의 발동 여부를 결정했다고 하는데, 그럴 경우엔 불필요하게 많은 메모리 소모가 일어났다고 한다.
Intersection Observer API는 내 타겟 엘리먼트가 등장하거나, 뷰포트에서 사라지거나, 또는 지정한 %만큼 두 엘리먼트의 교차점이 바뀐다거나 할 때 지정된 콜백함수가 실행되게 해준다. 그렇기 때문에, Intersection Observer API가 생기기 이전에 사용하던 이전 방식들과는 다르게 브라우저의 교차점들을 최적화하여 관리할 수 있게 해준다.
다만, Intersection Observer API는 아주 정확한 픽셀 수치로 교차 정보를 관찰하진 않는다. 그럼에도 불구하고 "정해진 N% 정도로 교차를 한다면~" 정도와 같은 경우를 모두 커버하기 때문에 사용하는데 별다른 문제는 없다.
<Intersection Observer 생성하기>
let observer = new IntersectionObserver(callback, options)
IntersectionObserver 생성자를 사용하여 옵저버를 만들 수 있다. 인자로는 (1)콜백함수와 (2) 옵션들이 들어가는데, 옵션들을 살펴보도록 하겠다.
const options = {
root: ...,
rootMargin: ...,
threshold: ...
}
IntersectionObserver 생성자에 전달하는 두 개의 인자 중 두번째인 옵션엔, 세 가지 필드가 있다.
(1) root:
- 타겟 엘리먼트와의 교차 정도를 체크할 상대방이다. 꼭 타겟의 윗 조상님들 중 하나여야 한다. 만약 따로 지정하지 않거나, 명확하게 null이라고 표기할 경우엔 디폴트로 브라우저의 뷰포트가 root로 지정된다.
- Vanilla JS로 할 땐 document.queryselector~~~와 같이 작성하면 된다.
- 사각사각 프로젝트에선, 전체 뷰포트에 대비해서 내 타겟 엘리먼트의 위치(style의 transform3d로 조정)를 바꿔주는 콜백함수를 부르는 것이 주된 목적이었기 때문에, 이 root 부분은 지정하지 않았다 (= 즉, 디폴트로 브라우저 뷰포트가 되었다).
(2) rootMargin:
- 루트를 감싸고 있는 마진을 뜻한다. CSS margin 속성이랑 비슷한 모양으로 값을 넣을 수 있다. (예를 들어, CSS에서 10px 10px 10 px 10px 이라고 쓰면 top, right, bottom, left 인 것과 같이 저 순서대로 값을 넣을 수 있다). 이 값은 %로 작성 될 수도 있다.
- 이 값들은 교차를 계산하기 전에 루트 엘리먼트의 바운딩 박스의 양 분의 늘어남/줄어남을 담당한다. 디폴트는 모두 0이다.
(3) threshold:
- 타겟 엘리먼트가 몇 % 정도 보여줘야 IntersectionObserver의 첫번째 인자로 전달된 콜백이 실행되어야 하는지를 정하는 값이다.
- 그냥 일반 숫자 또는 배열로 표기한다. 만약 "내 타겟이 50%정도 노출되었을 때 콜백을 실행시켜!"라고 명령하고 싶다면 0.5를 넣어주면 된다. 위에 사각사각 같은 경우엔 0.7값을 넣었다. 만약, 이것보다 조금 더 복잡하게, 노출도(?)가 25% 늘어날때마다 콜백이 실행되게 하고 싶다면 배열 값을 넣어주면 된다: [0, 0.25, 0.5, 0.75, 1].
- 디폴트값은 0이며,이는 즉 내 타겟의 머리카락 한 개 픽셀 한 개만 보이더라도 콜백을 덜컥 실행해버리겠다는 의미이다. 반대로, 1.0으로 설정하면 내 타겟을 이루는 모든 픽셀이 보여져야만 콜백이 실행된다는 걸 뜻한다.
관찰 대상, 타겟 지정하기
let myTarget = document.querySelector('#someElement')
observer.observe(myTarget)
관찰자인 Observer를 만들었으니 그 관찰자의 관심 대상인 관찰 대상, target element도 만들어주어야 한다. 타겟은 .observer(타겟) 메소드를 통해 지정할 수 있다.
콜백이 실행되면...
지정된 threshold를 만나는 순간 콜백이 실행된다. 콜백이 실행되면, 콜백은 IntersectionObserverEntry 객체와 우리의 관찰자 observer를 전달받게 된다.
let callback = (entries, observer) => {
entries.forEach (entry => {
...
}
}
콜백함수가 받게되는 entry list는 각 타겟당 하나의 엔트리를 포함하고 있다. 이 엔트리는 Intersection Observer API의 인터페이스로, 특정 시점에서의 타겟 엘리먼트와 루트의 교차 여부를 보여준다. 메소드는 없고, read-only인 속성 7개를 가지고 있는데, 사각사각에선 이중 딱 한 개만 사용했다. 바로 isIntersection 속성이다.
isIntersection 속성은 Boolean으로 ture/false인데, true일 경우엔 엔트리의 주인공인 타겟이 루트와 현재 교차하고 있다는 것을 뜻한다. 그래서, 사각사각에서 사용할 때엔 "만약 내 타겟 엔트리의 isIntersection이 true라면, 이러이러한 효과를 넣어라!"고 작성을 했다. 아래는 React hooks 내부 대략의 예시이다:
const myElement = useRef()
...
const handleScrolling = useCallback (( [entry] ) => {
if (entry.isIntersecting) {
// 만약 지금 타겟이 루트랑 교차중이라면, 이 안의 부분들을 실행해라!
// ...
}
}, [...])
useEffect(() => {
let observer;
const { current } = myElement;
if ( current ) {
observer = new IntersectionObserver(handleScrolling, { threashold: 0.7})
observer.observe(current)
return () => observer && observer.disconnect()
}
}, [handleScrolling])
마치며..
프론트엔드 개발자가 되고 싶었던 가장 큰 이유 중 하나가 바로 보기 좋고 맛도 좋은(?) 각종 인터랙션과 애니메이션 등을 많이 만들어보고 싶었기 때문이다. 이번에 Slide-in 애니메이션을 React로 만들어보면서 Intersection Observer API를 처음 사용해보았는데 너무 재미있는 것 같다. 마우스 스크롤링에 따라 전체 백그라운드 컬러의 RGB를 조정한다던가와 같은 것도 할 수 있지 않을까? 그러면 스크롤링에 따라 밤이었다가 낮이었다가 새때가 지나가고 달이 뜨고 밤이되고 하는 효과 같은 것도 만들 수 있을 것 같다 +_+
'Learn to Code' 카테고리의 다른 글
[JS] 즉시실행함수(IIFE)란? (0) | 2021.08.19 |
---|---|
[JS] event loop이란? (0) | 2021.08.18 |
[JS] Javascript (ES6+) (0) | 2021.08.10 |
[TS] 타입스크립트 설치부터 데이터 타입 명시까지 (0) | 2021.07.30 |
코드스테이츠 수료 완료! 추가 공부 및 파이널 프로젝트 보완 계획 (0) | 2021.07.25 |
댓글