티스토리 뷰

// Animate On Scroll
npm install aos

 

// type script
npm install --save-dev @types/aos

 

// 예제: AOS를 초기화하는 React 컴포넌트
import { useEffect } from "react";
import AOS from "aos";
import "aos/dist/aos.css";

/**
 * AOS 예제 컴포넌트
 * 스크롤 시 요소에 애니메이션 효과를 적용합니다.
 *
 * @returns {JSX.Element} 애니메이션이 적용된 컴포넌트
 */
export default function AOSExample(): JSX.Element {
  useEffect(() => {
    // AOS 초기화 (애니메이션 지속 시간 1000ms)
    AOS.init({ duration: 1000 });
  }, []);

  return (
    <div className="p-8">
      <h1 data-aos="fade-up" className="text-2xl font-bold mb-4">
        스크롤 애니메이션 예제
      </h1>
      <p data-aos="fade-left" className="mb-4">
        페이지를 스크롤하면 이 문장이 부드럽게 나타납니다.
      </p>
      <p data-aos="fade-right">
        AOS는 매우 간단하게 스크롤 애니메이션을 추가할 수 있도록 도와줍니다.
      </p>
    </div>
  );
}

 

 

기타 참고 사항

 

AOS 라이브러리가 클라이언트에서 DOM 요소에 클래스(예: aos-init, aos-animate)를 추가하면서, 서버에서 렌더링된 HTML과 차이가 발생하기 때문에 나타납니다. 즉, 서버 사이드 렌더링(SSR) 시점에는 해당 클래스가 없는데, 클라이언트에서 AOS 초기화 후 DOM에 추가되면서 hydration mismatch가 발생하는 것입니다.

이 문제를 해결하는 방법은 해당 컴포넌트를 클라이언트 전용으로 렌더링하는 것입니다. 대표적인 두 가지 접근 방식은 다음과 같습니다.


1. 동적 임포트 (Dynamic Import)로 클라이언트 전용 렌더링

Next.js의 동적 임포트를 사용하여 SSR을 비활성화할 수 있습니다. 이렇게 하면 AOSExample 컴포넌트는 클라이언트에서만 렌더링되어 서버와의 HTML 차이가 발생하지 않습니다.

예시:

// 페이지나 부모 컴포넌트에서
import dynamic from "next/dynamic";

// ssr: false 옵션을 추가하여 클라이언트 전용 컴포넌트로 임포트
const AOSExample = dynamic(() => import("../components/AOSExample"), { ssr: false });

export default function HomePage() {
  return (
    <div>
      <h1>홈 페이지</h1>
      <AOSExample />
    </div>
  );
}

이렇게 하면 AOSExample은 서버 사이드 렌더링 없이 클라이언트에서만 렌더링되므로, AOS가 DOM을 수정해도 초기 SSR HTML과 충돌하지 않습니다.


2. 마운트 후 렌더링 (Conditional Rendering After Mount)

컴포넌트 내부에서 useEffect를 사용해 컴포넌트가 클라이언트에서 마운트된 후에만 AOS 관련 로직 및 렌더링을 진행할 수도 있습니다. 예를 들어:

"use client";

import { useEffect, useState } from "react";
import AOS from "aos";
import "aos/dist/aos.css";

/**
 * AOSExample 컴포넌트
 *
 * @returns {JSX.Element | null} AOS 애니메이션 적용 컴포넌트 또는 초기 로딩 시 null
 */
export default function AOSExample(): JSX.Element | null {
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    // 컴포넌트가 마운트되었음을 표시
    setMounted(true);
    AOS.init({ duration: 1000 });
  }, []);

  // 클라이언트에서 마운트 전까지는 아무것도 렌더링하지 않음
  if (!mounted) {
    return null;
  }

  return (
    <div className="p-8">
      <h1 data-aos="fade-up" className="text-2xl font-bold mb-4">
        스크롤 애니메이션 예제
      </h1>
      <p data-aos="fade-left" className="mb-4">
        페이지를 스크롤하면 이 문장이 부드럽게 나타납니다.
      </p>
      <p data-aos="fade-right">
        AOS는 매우 간단하게 스크롤 애니메이션을 추가할 수 있도록 도와줍니다.
      </p>
    </div>
  );
}

여기서 mounted 상태는 컴포넌트가 클라이언트에서 마운트된 이후에만 콘텐츠를 렌더링하도록 도와줍니다. 이 경우 SSR 단계에서는 아무 것도 렌더링되지 않으므로, 클라이언트에서 AOS가 적용된 후에 HTML이 일치하게 됩니다.


두 방법 모두 AOS가 클라이언트에서만 동작하도록 하여 hydration mismatch 문제를 해결할 수 있습니다. 프로젝트 상황에 맞게 선택하여 적용해 보세요.

728x90
반응형