GDGoC KNU 4기 홈페이지 npm 패키지 내보기 - 서비스편 ver2

김규회·2024년 12월 5일
0

GDGoC KNU

목록 보기
4/4
post-thumbnail

이번에 제가 GDGoC KNU 홈페이지를 개발하면서 react-lazy-observer 패키지를 NPM에 배포하게 되었는데 이와 관련한 고찰과 패키지 소개를 하려 한다.
놀랍게도 배포한 지 일주일 만에 150회가 넘는 다운로드를 기록했다.

개발 배경

블로그 기능에 관련해서 팀원과 개발하면서 고민이 하나 생겼다.
나중에 블로그 글 목록이나 팀 페이지의 포스트들이 쌓이다 보면 인피니티 스크롤을 적용한다고 하더라도 인기글과 어느정도 블로그 글 포스트때문에 성능 이슈가 생길 것 같았다.

그래서 React에서 제공하는 lazySuspense로 코드 분할은 가능하지만, 여기에 Intersection Observer API를 결합하면 더 스마트한 지연 로딩이 가능하지 않을까? 라는 생각에서 시작했다.

어떻게 동작하는가? (초기 계획)

1. 초기 마운트

  • DOM 요소가 마운트되면 Observer가 관찰을 시작
  • 설정한 threshold(0.3)로 30% 이상 노출을 체크

2. 스크롤 감지

  • 사용자가 스크롤하면서 컴포넌트가 뷰포트로 진입
  • Observer가 실시간으로 가시성 비율을 계산

3. 로딩 트리거

  • 30% 임계점을 넘으면 상태가 변경되고
  • React가 이를 감지해서 실제 컴포넌트를 렌더링
  • 임무 완료 후 Observer는 자동으로 정리

코드로 보는 동작 방식

1. 초기 마운트 단계

실제 DOM과 React가 어떻게 상호작용하는지 자세히 살펴보자:

// 1. DOM 요소 참조 설정
const observerRef = React.useRef<HTMLDivElement>(null);

이 단계에서 일어나는 일:

  • 빈 div 엘리먼트가 실제 DOM에 마운트됨
  • useRef를 통해 이 DOM 노드를 직접 참조
  • 아직 실제 컴포넌트는 로드되지 않은 상태

2. Observer 설정 단계

const observer = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        setIsVisible(true);
        observer.disconnect();
      }
    });
  },
  {
    threshold: 0.3
  }
);

여기서 중요한 점:

  • Observer는 실제 DOM 요소를 감시
  • Virtual DOM이 아닌 실제 브라우저 레벨에서 동작
  • threshold 0.3은 컴포넌트의 30%가 보여야 활성화
  • 한번 활성화되면 observer는 자동으로 정리됨 (메모리 관리)

3. 상태 변경과 렌더링 프로세스

그래서 시각적으로 보면 이런 흐름이 된다:

  1. 초기 상태

    • fallback UI만 보이는 상태
    • 실제 컴포넌트는 아직 로드되지 않음
  2. 스크롤 진행

    • 사용자가 페이지를 스크롤
    • Observer가 실시간으로 가시성 체크
  3. 30% 임계점 도달

    setIsVisible(true);  // 상태 업데이트 트리거
    
    • isVisible 상태가 true로 변경
    • React의 리렌더링 사이클 시작
  4. 컴포넌트 로딩

    {isVisible ? (
      <React.Suspense fallback={fallback}>
        {children}
      </React.Suspense>
    ) : (
      fallback
    )}
    
    • Suspense를 통해 실제 컴포넌트 로드 시작
    • 로드 중에는 fallback UI 유지
    • 로드 완료 후 실제 컴포넌트로 전환

4. 메모리 관리

observer.disconnect();  // 임무 완료 후 정리
  • 컴포넌트가 로드되면 Observer 자동 정리
  • 불필요한 메모리 사용 방지
  • 성능 최적화에 기여

최적화 포인트

  1. 즉시 정리

    • 한 번 로드되면 더 이상 관찰이 필요 없음
    • observer.disconnect()로 즉시 정리
  2. 조건부 실행

    if (isVisible) return;
    
    • 이미 보이는 상태면 Observer 생성 안 함
    • 불필요한 리소스 사용 방지
  3. 효율적인 DOM 접근

    • useRef로 직접 DOM 참조
    • Virtual DOM 렌더링 최소화

이런 최적화 덕분에 LazyLoad 컴포넌트는 효율적으로 동작하면서도 브라우저 리소스를 아낄 수 있다.

실제 사용해보니...

라이트하우스로 성능 측정해보니 몇 가지 재밌는 점을 발견했다:

  1. 짧은 컨텐츠에선 오히려 역효과
    • 레이어가 하나 더 생기면서 성능이 살짝 떨어지기도
    • Observer 설정하고 관리하는 오버헤드가 이득보다 클 수도
  2. 적용 위치가 중요함
    • 화면 하단 컴포넌트에 적용해도 성능 향상이 미미할 때가 있음
    • 최신 브라우저들이 이미 나름의 최적화를 하고 있어서 그런듯

이럴 때 쓰면 좋다

  1. 무거운 컴포넌트
    • 이미지/동영상 있는 컴포넌트
    • 복잡한 차트나 시각화
    • 외부 라이브러리 쓰는 무거운 컴포넌트
  2. 반복되는 컨텐츠
    • 블로그 포스트 목록
    • 무한 스크롤 페이지

이럴 땐 다시 생각해보자

  1. 간단한 텍스트나 기본 UI
  2. 첫 화면에 바로 보이는 컴포넌트
  3. 가벼운 컴포넌트

결론

react-lazy-observer는 꽤 괜찮은 도구지만, 모든 상황에 딱 맞는 건 아니다. 적용하기 전에 잘 고민해보자:

  1. 컴포넌트가 얼마나 무거운지
  2. 실제 UX에 얼마나 도움되는지
  3. 구현 복잡도 대비 성능 향상이 괜찮은지

그래도 앞으로 있을 기능에 대해 기술 부채보단 좋지 않을까라는 생각과 뭔가 재미난 고민에 대한 해결책을 구현할 수 있었던 것 같아서 재밌었다.
더 자세한 내용은 npm 패키지 페이지에서 확인할 수 있다.

profile
프론트엔드 Developer

0개의 댓글