2025년 3월 회고: 애니메이션 디버깅 지옥에서 React Hooks 천국까지

2025년 3월 회고: 애니메이션 디버깅 지옥에서 React Hooks

프롤로그: 코드의 바다에 허우적

3월은 한마디로 정의하자면 “순수 웹 개발 몰입”의 달이었다. 2월의 창의적 실험들이 어느 정도 방향을 잡아주면서, 이제는 진짜로 내가 원하는 것을 웹에서 구현하는 방법에 집중할 때가 온 것이다.

애니메이션 디버깅의 지옥 같은 시간들, 갤러리 리팩토링의 끝없는 삽질, 그리고 React Hooks라는 새로운 세계의 발견. 이 모든 과정이 마치 게임의 레벨업 과정 같았다.

3월의 기술 여행 맵
├── 🐛 애니메이션 디버깅 (현실과의 첫 충돌)
├── 🔧 갤러리 리팩토링 (코드 정리의 중요성)
├── ⚛️ React Hooks (새로운 패러다임의 발견)
└── 📝 일일 기록 (꾸준함의 힘)

2025-03-19

VFX 연습을 알아보려고 이미지에 적응해보는 시험을 했다.

2025-03-20

2025-03-21

2025-03-30

애니메이션 디버깅, 쓰라린 경험

“왜 안 움직여?” 애니메이션과의 첫 전쟁

3월 초반은 온통 애니메이션 디버깅으로 도배되었다. 2월에 Remotion과 GSAP를 만지작거리면서 “아, 이제 애니메이션은 내 것”이라고 생각했는데… 현실은 그리 호락호락하지 않았다.

/* 이런 간단해 보이는 코드가 왜 이렇게 말썽일까? */
.animation-element {
  transform: translateX(0);
  transition: transform 0.3s ease-in-out;
}

.animation-element.active {
  transform: translateX(100px);
  /* 분명히 움직여야 하는데... */
}

디버깅 과정에서 배운 교훈들

애니메이션 디버깅을 하면서 깨달은 것:

  1. Timing Functions의 중요성easeease-inease-out의 차이가 체감상 엄청나다
  2. GPU 가속 활용transform3d(0,0,0) 같은 핵으로 성능 최적화
  3. 브라우저별 차이: Safari는 정말 특별한 아이다
  4. Reflow vs Repaint: 어떤 속성을 바꾸느냐에 따라 성능이 천지차이
// 디버깅 과정에서 만든 유틸리티 함수
const debugAnimation = (element, options = {}) => {
  console.log('🎬 Animation Debug Start');
  console.log('Element:', element);
  console.log('Computed Style:', getComputedStyle(element));
  console.log('Animation Options:', options);
  
  element.addEventListener('animationstart', () => 
    console.log('✅ Animation Started'));
  element.addEventListener('animationend', () => 
    console.log('🏁 Animation Ended'));
};

디버깅의 현실

밤 1시부터 11시까지… 거의 하루 종일 애니메이션과 씨름했다는 걸 알 수 있다. console.log가 내 가장 친한 친구가 되었다.

3월 19일 타임라인
01:47 AM - 애니메이션 버그 발견
01:57 AM - 원인 분석 시작  
11:29 PM - 드디어 해결책 발견
11:30 PM - 테스트 완료

갤러리 리팩토링, 코드 대청소

“이 코드 누가 짰어?” (답: 3개월 전의 나)

3월 20일부터는 갤러리 프로젝트 리팩토링에 돌입했다. 몇 달 전에 만들어둔 갤러리 코드를 다시 보니… 부끄러웠다. 이게 정말 내가 짠 코드인가.

// Before (리팩토링 전)
function showImage(img) {
  document.getElementById('modal').style.display = 'block';
  document.getElementById('modalImg').src = img.src;
  document.getElementById('modalTitle').innerHTML = img.alt;
  // 반복되는 코드들...
}

// After (리팩토링 후)
class GalleryModal {
  constructor(modalId) {
    this.modal = document.getElementById(modalId);
    this.modalImg = this.modal.querySelector('.modal-image');
    this.modalTitle = this.modal.querySelector('.modal-title');
  }
  
  show(imageData) {
    this.modal.classList.add('active');
    this.modalImg.src = imageData.src;
    this.modalTitle.textContent = imageData.title;
  }
}

이번 리팩토링 과정에서 세운 나의 원칙들

리팩토링 체크리스트
├── DRY (Don't Repeat Yourself)
│   └── 중복 코드 제거하기
├── SRP (Single Responsibility Principle)  
│   └── 함수 하나당 역할 하나만
└── 가독성 (Readability)
    └── 3개월 후의 내가 이해할 수 있을까?

리팩토링 과정에서 발견한 성능 이슈들

  1. 이미지 lazy loading 미적용: 갤러리에 이미지가 100개가 넘는데 모두 한 번에 로딩
  2. Event listener 중복 등록: 같은 이벤트가 여러 번 등록되어 메모리 누수
  3. DOM 쿼리 반복: 같은 엘리먼트를 계속 찾아서 성능 저하
// 이미지 lazy loading 구현
const observerOptions = {
  root: null,
  rootMargin: '50px',
  threshold: 0.1
};

const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      imageObserver.unobserve(img);
    }
  });
}, observerOptions);

React Hooks, 새로운 세계의 문

3월부터 React Hooks를 파기 시작했다. 클래스 컴포넌트로만 React를 접해왔던 내게, Hooks는 완전히 새로운 패러다임이었다.

useState: 상태 관리의 혁명

처음 useState를 사용했을 때 기억한다. 이렇게 간단할 수가 있나?

// 갤러리 모달 상태 관리
const [modalState, setModalState] = useState({
  isOpen: false,
  currentImage: null,
  currentIndex: 0
});

// 이미지 클릭 핸들러
const handleImageClick = (image, index) => {
  setModalState({
    isOpen: true,
    currentImage: image,
    currentIndex: index
  });
};

useEffect: 생명주기의 단순화

useEffect는 정말 마법 같았다. 여러 생명주기 메서드를 하나로 통합할 수 있다니.

// 이미지 프리로딩 효과
useEffect(() => {
  const preloadNextImages = () => {
    const nextImages = images.slice(currentIndex + 1, currentIndex + 4);
    nextImages.forEach(img => {
      const image = new Image();
      image.src = img.src;
    });
  };
  
  if (images.length > 0) {
    preloadNextImages();
  }
}, [currentIndex, images]);

Custom Hooks: 재사용 가능한 로직의 발견

가장 흥미로웠던 건 Custom Hooks였다. 반복되는 로직을 깔끔하게 분리할 수 있다는 게 신세계였다.

// 키보드 네비게이션을 위한 Custom Hook
const useKeyboardNavigation = (images, currentIndex, onIndexChange) => {
  useEffect(() => {
    const handleKeyPress = (e) => {
      switch(e.key) {
        case 'ArrowLeft':
          if (currentIndex > 0) {
            onIndexChange(currentIndex - 1);
          }
          break;
        case 'ArrowRight':
          if (currentIndex < images.length - 1) {
            onIndexChange(currentIndex + 1);
          }
          break;
        case 'Escape':
          onIndexChange(-1); // 모달 닫기
          break;
      }
    };
    
    window.addEventListener('keydown', handleKeyPress);
    return () => window.removeEventListener('keydown', handleKeyPress);
  }, [currentIndex, images.length, onIndexChange]);
};

일일 기록의 힘

꾸준함이 주는 놀라운 인사이트

3월에는 같은 일일 노트를 꾸준히 작성했다. 처음에는 그냥 “오늘 뭐 했는지 기록해두자” 정도였는데, 시간이 지나면서 이게 얼마나 소중한 데이터인지 깨달았다.

일일 기록 분석

├── 가장 생산적인 시간: 새벽 1-3시 (야행성 확정) ├── 막히는 패턴: 오후 2-4시 (점심 후 에너지 드랍) ├── 돌파구 발견 시간: 밤 10-12시 (집중력 최고조) └── 번아웃 신호: 연속 3일 기록 안 함

기록이 가져다준 성찰

일일 기록을 통해 발견한 내 개발 패턴들:

  1. 문제에 너무 오래 매달리는 경향: 2시간 넘게 같은 버그와 씨름
  2. 완벽주의의 함정: 80% 완성된 기능을 100%로 만들려다 시간 낭비
  3. 학습과 적용의 균형: 새로운 기술을 배우는 시간 vs 기존 프로젝트에 적용하는 시간

오늘 한 일

  • 애니메이션 버그 디버깅 (7시간…)
  • GSAP timeline 동작 원리 이해
  • 브라우저별 렌더링 차이 조사

배운 점

  • 성급하게 복잡한 애니메이션부터 만들지 말자
  • 작은 단위로 테스트하면서 진행하기

내일 할 일

  • 갤러리 컴포넌트 리팩토링 시작
  • React Hooks 튜토리얼 완주

3월의 여정에서 시각적 증거들이 말해주는 것들

3월 스크린샷들을 보면 내 개발 과정의 변화가 한눈에 보인다:

3월 19일: 브라우저 개발자 도구 천국

  • Console 탭과 Elements 탭을 오가며 디버깅
  • Network 탭에서 애니메이션 성능 분석
  • 여러 브라우저 창이 동시에 열려있는 카오스

3월 20일: 코드 에디터 중심의 작업

  • VS Code에서 파일 트리가 복잡하게 펼쳐진 모습
  • Git diff 화면으로 리팩토링 진행상황 확인
  • ESLint 경고들과의 끝없는 전쟁

3월 28-29일: React 개발 환경의 완성

  • React DevTools의 등장
  • Component 구조가 깔끔하게 정리된 모습
  • Hot reload로 빠른 개발 사이클 확립

개발 환경의 진화

graph LR
    A[단순한 HTML/CSS] --> B[JavaScript 추가]
    B --> C[모듈 번들러 도입]
    C --> D[React 환경 구축]
    D --> E[개발도구 최적화]
    
    A --> F[메모장 수준]
    B --> G[브라우저 콘솔 의존]
    C --> H[Webpack 설정 지옥]
    D --> I[컴포넌트 사고방식]
    E --> J[생산성 극대화]

기술적 성장의 단계들

Level 1: 애니메이션 초보자 → Level 5: 디버깅 마스터

3월 초의 나와 3월 말의 나는 확실히 다른 사람이었다. 특히 문제 해결 접근법이 완전히 바뀌었다.

// 3월 초: 무작정 시도
function fixAnimation() {
  // 이거 안되면 이것도 해보고 저것도 해보고...
  element.style.transform = 'translateX(100px)';
  setTimeout(() => {
    element.style.transform = 'translateX(0)';
  }, 1000);
}

// 3월 말: 체계적 접근
function debugAnimation(element, targetTransform) {
  // 1. 현재 상태 확인
  const currentStyle = getComputedStyle(element);
  console.log('Current transform:', currentStyle.transform);
  
  // 2. 브라우저 호환성 확인
  const supportsTransform = 'transform' in element.style;
  
  // 3. GPU 가속 활용
  element.style.willChange = 'transform';
  
  // 4. 애니메이션 실행
  element.style.transform = targetTransform;
  
  // 5. 정리
  element.addEventListener('transitionend', () => {
    element.style.willChange = 'auto';
  });
}

React 패러다임의 체화

Hooks를 배우면서 가장 큰 변화는 사고방식이었다. 명령형에서 선언형으로, 클래스에서 함수형으로.

명령형 사고 (Before)

“버튼을 클릭했을 때 모달을 열어라” “이미지를 로드했을 때 로딩 스피너를 숨겨라”

선언형 사고 (After)

“모달의 상태에 따라 화면에 표시하거나 숨긴다” “로딩 상태에 따라 스피너 또는 컨텐츠를 보여준다”

예상치 못한 발견들

코드 리뷰의 중요성

갤러리 리팩토링을 하면서 깨달은 건, 과거의 내가 미래의 내에게 주는 가장 큰 선물은 읽기 좋은 코드라는 것이었다.

// 3개월 전의 나에게 하고 싶은 말
const handleImageClick = (img, idx) => { // img? idx? 이게 뭐야?
  // 주석도 없고...
  if (img) {
    modal.show();
    modalImg.src = img.src;
    currentIdx = idx;
  }
};

// 3개월 후의 나가 원하는 코드
const handleImageClick = (imageData, imageIndex) => {
  /**
   * 갤러리 이미지 클릭 시 모달을 열고 선택된 이미지를 표시
   * @param {Object} imageData - 이미지 정보 (src, alt, title 포함)
   * @param {number} imageIndex - 갤러리에서의 이미지 순서
   */
  if (!imageData?.src) return;
  
  setModalState(prev => ({
    ...prev,
    isOpen: true,
    currentImage: imageData,
    currentIndex: imageIndex
  }));
};

3월에 가장 중요한 깨달음은 완벽한 코드를 추구하다가 아무것도 완성하지 못하는 것보다, 동작하는 코드를 만들고 점진적으로 개선하는 것이 낫다는 것이었다.

개발 철학의 변화
Before: "한 번에 완벽하게 만들어야 해"
After: "일단 돌아가게 만들고, 점점 개선하자"

Before: "모든 edge case를 고려해야 해"  
After: "주요 use case부터 해결하고, 필요에 따라 확장하자"

Before: "최신 기술을 모두 써야 해"
After: "문제에 맞는 도구를 선택하자"

3월을 마무리하며

기술적 성과 정리

3월 한 달 동안 확실히 늘어난 것들:

  1. 디버깅 스킬: 문제를 체계적으로 분석하고 해결하는 능력
  2. React Hooks 활용: 함수형 컴포넌트로 복잡한 상태 관리
  3. 코드 품질 의식: 가독성과 유지보수성에 대한 고민
  4. 성능 최적화: 실제 사용자 경험을 고려한 최적화
  5. 문서화 습관: 코드뿐만 아니라 과정도 기록하는 습관

4월을 향한 계획

🗓️ 4월 로드맵 ├── Three.js 본격 입문 (3D 웹 개발) ├── 갤러리 프로젝트 완성 및 배포 ├── Custom Hooks 라이브러리 만들기 └── 웹 애니메이션 성능 최적화 심화 학습

개인적 성찰

3월은 기술적 성장뿐만 아니라 내 정체성을 확립하는 달이기도 했다.