(프론트엔더의 고통)애니메이션부터 SEO까지, 4개 프로젝트로 배운 것들

프론트엔드 삽질기: 애니메이션부터 SEO까지, 4개 프로젝트로 배운 것들

최근 몇 달간 진행한 프론트엔드 프로젝트들을 되돌아보니, 뭔가 패턴이 보이기 시작했다. 리퍼런스 웹사이트 분석하다가 100DaysOfUI 만들고, 거기서 스크롤 애니메이션 삽질하다가 SEO 최적화까지… 겉보기엔 전혀 관련 없어 보이는 작업들이 묘하게 연결되면서 하나의 큰 그림을 그려내고 있었다.

시작은 남의 코드 분석부터

리퍼런스 웹사이트 예제 분석

graph LR
    A[Gatsby SSG] --> B[GSAP ScrollTrigger]
    B --> C[Contentful CMS]
    C --> D[컴포넌트 시스템]
    D --> E[애니메이션 패턴]

예제로 본 웹사이트를 보자마자 “어떻게 만든 거지?“라는 생각이 들었다. Gatsby 기반의 정적 사이트인데 GSAP ScrollTrigger로 부드러운 애니메이션을 구현해놨더라.

특히 인상 깊었던 건 컴포넌트 구조였다:

  • Header, Menu, Hero, Gallery, Events, Contact 등 명확한 역할 분담
  • Contentful CMS 연동으로 콘텐츠와 코드 분리
  • GSAP 애니메이션이 React 생명주기와 자연스럽게 통합

“아, 이런 식으로 애니메이션과 CMS를 조합할 수 있구나.” 이때부터 뭔가 계획이 생기기 시작했다.

100DaysOfUI: 컴포넌트 시스템의 실험장

남의 코드 구경만 하고 있을 순 없지. 직접 만들어봐야 진짜 이해가 된다는 생각으로 100DaysOfUI 프로젝트를 시작했다.

아키텍처 설계: 확장성을 고려한 구조

100DaysOfUI/
├── src/
   ├── components/
      ├── Day49/        # 독립적인 테마와 스타일
      ├── Day53/        # 각자의 컴포넌트 생태계
      └── Day59/        # 스크롤 애니메이션 특화
   ├── utils/
      ├── use-media-query.tsx    # 공통 훅
      └── commonRootUILayouts.scss   # 공통 스타일
   └── styles/
       └── tailwindImport.css

여기서 핵심 아이디어는 컴포넌트 격리였다. 각 Day 컴포넌트가 완전히 독립적인 테마를 가지면서도, 공통 유틸리티는 재사용할 수 있도록 설계했다.

// 각 컴포넌트마다 독립적인 테마 적용
useEffect(() => {
  document.documentElement.setAttribute('data-theme', 'day59-theme');
  return () => {
    document.documentElement.removeAttribute('data-theme');
  };
}, []);

이렇게 하니까 하나의 프로젝트 안에서 완전히 다른 디자인 언어를 가진 컴포넌트들을 공존시킬 수 있었다. 각자의 개성은 살리면서 코드 중복은 최소화하는 거지.

Day59: 스크롤 애니메이션의 지옥

그런데 Day59 컴포넌트를 만들면서 진짜 삽질이 시작됐다. 스크롤 애니메이션이라는 게 생각보다 훨씬 복잡하더라.

라이브러리 호환성 지옥

처음엔 ReactLenis 같은 래퍼 라이브러리를 쓰려고 했는데, Next.js 예제를 Vite 환경으로 옮기는 과정에서 온갖 문제가 터졌다.

// 이런 식으로 직접 인스턴스화하는 게 답이었다
const lenisInstance = new Lenis({
  duration: 1.2,
  smooth: true,
});

// GSAP ScrollTrigger와 수동으로 연결
lenisInstance.on('scroll', ScrollTrigger.update);

래퍼 라이브러리의 편의성을 포기하고 원본 라이브러리를 직접 제어하니까 오히려 더 안정적이었다. “편의성과 제어권은 트레이드오프다”는 걸 몸소 체험했다.

애니메이션 타이밍의 마술

// 섹션별로 독립적인 스크롤 트리거 설정
const tl1 = gsap.timeline({
  scrollTrigger: {
    trigger: imagesSectionRef.current,
    start: 'top top',
    end: '33% bottom',
    scrub: true,
  }
});

const tl2 = gsap.timeline({
  scrollTrigger: {
    trigger: heroSectionRef.current,
    start: 'top 80%',
    end: 'bottom 20%',
  }
});

각 섹션마다 다른 타이밍으로 애니메이션이 트리거되도록 세밀하게 조정하는 작업이 정말 까다로웠다. 픽셀 단위로 조정하면서 “이게 맞나?” 싶었는데, 결과물 보니까 그만한 가치가 있더라.

antiperfectclub.github.io: SEO의 현실

애니메이션에 빠져있다가 문득 깨달았다. “아무리 예쁘게 만들어도 검색에 안 걸리면 의미 없잖아?”

React SPA의 SEO 딜레마

// React Helmet Async로 페이지별 메타태그 관리
<SEOHead 
  title="Portfolio - Frontend Developer"
  description="프론트엔드 개발자의 포트폴리오"
  textContent={pageTextContent}
  noVisualElements={true}
/>

여기서 핵심은 noVisualElements={true} 부분이다. 아무리 화려한 애니메이션을 만들어도 검색 엔진은 텍스트 콘텐츠만 읽는다는 걸 받아들여야 했다.

Google Analytics와 GTM 통합

// Google Tag Manager + Analytics 4 조합
useEffect(() => {
  gtag('config', 'GA_MEASUREMENT_ID', {
    page_title: document.title,
    page_location: window.location.href,
  });
}, [pathname]);

데이터 수집도 중요하지만, 무엇보다 사용자 경험을 해치지 않는 선에서 최적화하는 게 관건이었다.

삽질하면서 깨달은 패턴들

1. 아키텍처는 단순함에서 시작한다

graph TD
    A[단순한 컴포넌트] --> B[독립적인 테마]
    B --> C[공통 유틸리티]
    C --> D[확장 가능한 구조]

처음엔 복잡한 설계를 하려고 했는데, 결국 가장 단순한 구조가 가장 유지보수하기 좋았다.

2. 라이브러리 선택의 철학

  • 래퍼보다 원본: 편의성보다 제어권이 중요할 때가 많다
  • 점진적 향상: HTML/CSS 기본기 → 애니메이션 레이어 추가
  • 호환성 우선: 최신 기술보다 안정성이 때로는 더 중요하다

3. 성능과 경험의 균형

// 애니메이션 성능 최적화
const tl = gsap.timeline({
  scrollTrigger: {
    trigger: ref.current,
    start: 'top 80%',
    toggleActions: 'play none none reverse',
  }
});

화려한 애니메이션도 좋지만, 60fps를 유지하면서 부드럽게 작동하는 게 더 중요하다.

데이터 플로우: 전체 그림

User Interaction
        │
        ▼
   Lenis Manager
        │
        ├─── GSAP ScrollTrigger ───► 이미지 스택 애니메이션
        │                      
        ├─── Framer Motion ────────► Hero 텍스트 애니메이션
        │                      
        └─── CSS Variables ────────► 테마 시스템
                                │
Analytics Event ◄───────────────┘
        │
        ▼
  Google Analytics / GTM

각각 따로 만든 것 같지만, 결국 하나의 데이터 플로우로 연결되더라. 사용자의 스크롤 입력이 애니메이션을 트리거하고, 그게 분석 이벤트로 수집되는 전체적인 흐름이 보이기 시작했다.

그래서 뭘 배웠나?

4개 프로젝트를 거치면서 깨달은 건, 프론트엔드 개발은 결국 트레이드오프의 연속이라는 것이다.

  • 편의성 vs 제어권
  • 성능 vs 시각적 효과
  • SEO vs 인터랙티브