
React 에서 intersection Observer API를 사용해 요소가 화면에 나타나는지 여부를 감지하는데 사용되는 라이브러리
설치
npm install react-intersection-observer
useInView 훅을 호출하면 배열형태의 반환값이 나온다.
첫번째 요소는 ref 이고 두번째요소는 View 인데 ref는 감지하고자 하는 요소에 적용하고
inView는 해당요소가 뷰포트에 보이는지 여부를 나타낸다.
예시코드
import React from 'react';
import { useInView } from 'react-intersection-observer';
const MyComponent = () => {
const [ref, inView] = useInView({
triggerOnce: true, // 한 번만 트리거 옵션
});
return (
<div ref={ref}>
{inView ? 'Visible' : 'Not Visible'}
</div>
);
};
export default MyComponent;
- useInview는 intersection Observer를 기반 으로 하며 요소가 화면에 나타날 떄 특정 동작을 수행 할수 있는 기능을 제공한다.
- inView 변수를 통해 해당 요소가 화면에 보일때만 애니메이션 효과를 활성화 하는 등의 동적인 효과를 줄수 있다.
- 성능최적화
- intersection Observer를 사용하면 화면에 보이는지 여부를 효율적으로 감지할 수 있다.
- 예를 들어 특정 컴포넌트의 렌더링이나 리소스 로딩을 미루고 사용자가 해당 컴포넌트를 실제로 볼 때에만 이를 처리하고자 할때 'useInView'를 사용할 수있다.
- triggerOnce: true 옵션을 사용하면 한 번만 트리거되어 불필요한 리렌더링을 방지할 수 있다.
- 애니메이션 타이밍 조절
- delay 옵션으로 클래스에 대한 애미네이션 효과를 줄수 있다.
- User Experience 향상
- 초기 로딩시에 필요한 자원을 최적화 해 불필요한 자원 로딩을 방지하고 페이지 성능을 향상시킬수 있다.
- 사용자가 뷰포트에 스크롤 하거나 해당 영역을 볼때까지 자원을 로딩하지 않고 필요한 순간에만 로딩해 사용자 경험을 개선시킬수 있다.
내 프로젝트의 condition은 next app router 를 기반으로 설정 되어있다.
react-intersection-observer 설치하고 useInView를 적용하고 보면 not defined 에러가 발생한다. 아마 클라이언트로 보여지는 부분이기 때문에 에러가 발생한것 같다.
next app router에서는 client 컴포넌트로 인식되기때문에 해결방법으로 'use client' 를 상단에 적용시켜줘야 한다. 그럼 에러가 해결된다. !
react-intersection-observer를 이용해 구현할 것은 inView가 활성화 됨에 따라 화살표를 svg를 애니메이션화 해주고 그에 따른 페이지 이동 을 구현하려고 한다. 😀
제일 큰 부모 컴포넌트는 Portfolio.tsx 다.
하위 컴포넌트로 먼저 Intro.tsx 만들고 AnchorLink.tsx 파일을 생성해준다.
Postfolio.tsx
import Article from "@/components/Portfolio/Article";
import Intro from "@/components/Portfolio/Intro";
import AnchorLink from "@/service/AnchorLink";
export default function Portfolio() {
return(
<div>
<Intro/>
<Article/>
</div>
)
}
Intro.tsx는 말그대로 intro 컴포넌트이고 그안에 text클릭시 Link이동과 화살표애니메이션을 구현하는 곳은 AnchorLink.tsx 컴포넌트이다.
react-intersection-observer useInView 훅에서 ref 와 inView를 가져온뒤 옵션을 추가해준다.
AnchorLink.tsx
'use client';
import Link from "next/link";
import { useEffect } from "react";
import { useInView } from "react-intersection-observer";
type Props = {
text:string;
href:string;
}
export default function AnchorLink({text,href,...props}:Props) {
const {ref , inView} = useInView({
triggerOnce:true,
delay:500
})
return(
<div ref={ref}>
<Link href={href}>{text}</Link>
<svg
aria-hidden="true"
focusable="false"
width="23"
height="24"
viewBox="0 0 23 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
className="tail"
d="M0.5 1.49999C6.83333 0.999993 19.2 4.59999 18 23"
style={{ strokeDashoffset: inView ? 0 : '31.95920181274414' }}
/>
<path
className={`point`}
d="M13.5 16.5L18 23L22.5 16.5"
style={{ strokeDashoffset: inView ? 0 : '15.81138801574707' }}
/>
</svg>
</div>
)
}
inView를 콘솔에서 확인해보면 처음엔 false였다가 곧바로 true변경되는것을 알수있다. 이는 애니메이션을 페이지가 화면에 보일때만 애니메이션 효과가 활성화되어 동적으로 변경시켜줄 수 있다. ! 👍

tailwind에서 inView 상태에 따라 stroke-dash-offset을 달리해야하는데 구글링에서 적절한 방법을 찾지못해 일단 style로 동적인 상태값을 넣어줬다.
inView 가 true일경우 stroke-dash-offset 을 변경해줌으로써 애니메이션화 시켜줄수 있다.
Intro컴포넌트는 말그대로 처음 보여주는 페이지다.
안에 title 문구와 AnchorLink.tsx 을 넣어주고 내가 원하는 text문구와 prop로 클릭시 어떤 링크로 이동할지 넣어줘야한다.
Intro.tsx
import AnchorLink from "@/service/AnchorLink";
import Text from "../Text";
import Title from "../Title";
export default function Intro() {
return(
<div id="intro" className="h-screen">
<Title className="whitespace-pre-wrap">{`안녕하세요.😀 \ntest 문구 입니다.`}</Title>
<AnchorLink text="Read more" href="#article"/>
</div>
)
}
위에 코드에서 #article로 href를 설정했는데 , 일단 Article컴포넌트를 하나 만들어주고 id를 통해 이동할수있도록 id를 같은 article로 적용해준다.
'use client';
import { useEffect } from "react";
import { useInView } from "react-intersection-observer"
import Text from "../Text";
export default function Article() {
const {ref, inView} = useInView({
triggerOnce:true,
delay:500,
threshold:0.5
})
return(
<div id="article" className="h-screen py-[100px]" ref={ref}>
<Text className="articles-fade-in text-2xl">Articles 페이지 애니메이션 test</Text>
</div>
)
}
내가 원하는건 한곳에 밑으로 내려가듯이 이동하는걸 원했기때문에 전체 page에는 모든게 다 포함되어있어야한다.
import Article from "@/components/Portfolio/Article";
import Intro from "@/components/Portfolio/Intro";
import AnchorLink from "@/service/AnchorLink";
export default function Portfolio() {
return(
<div>
<Intro/>
<Article/>
</div>
)
}
이렇게 구상하면 처음 보이는 Intro 에서 inView가 활성화 됨에 따라 화살표 애니메이션이 동적으로 변경 되고 Read more을 클릭 시 article로 넘어가게 된다.
Intro에서 Article로 이동할때 딱딱 끈어지는 듯한 것보단 부드러운 동작을 원한다면
html css 부분에 scroll-behavior : smooth; 를 넣어주면 부드럽게 이동한다 !
