React + TypeScript로 축제 리스트 무한 스크롤 최적화하기

2025. 5. 25. 16:12프론트엔드/TypeScript

728x90

최근 축제 정보를 제공하는 웹 서비스에서 무한 스크롤을 구현하던 중, 중복 데이터 렌더링 문제와 옵저버 재생성으로 인한 성능 저하, TypeScript의 타입 오류 등 다양한 문제를 경험했습니다. 이번 글에서는 실제 프로젝트에서 겪은 문제 상황과 그에 대한 해결 과정을 개발 회고 형식으로 정리해보려 합니다.


🧩 문제 상황 요약

초기에는 다음과 같은 형태로 데이터를 페이징하여 불러왔습니다:

const loadFestivals = useCallback(async () => {
  const today = formatDate(new Date());
  const response = await axiosInstance.get('/api/event', {
    params: { startDate: today, endDate: today, page, size: 5 },
  });
  setFestivals(prev => [...prev, ...response.data.data.content]);
}, [page]);

하지만 아래와 같은 문제가 나타났습니다:

  • 동일한 데이터가 여러 번 누적되어 렌더링되는 중복 문제
  • IntersectionObserver가 매번 재생성되며 성능 저하 유발
  • TypeScript strict 모드에서 발생하는 any 타입 관련 오류

🔧 해결 과정

1. 중복 데이터 필터링 로직 추가

축제 데이터 중 eventId 기준으로 이미 등록된 항목은 제외하고 새로운 데이터만 추가되도록 개선했습니다:

setFestivals(prev => {
  const existingIds = new Set(prev.map((f: Festival) => f.eventId));
  const filtered = newEvents.filter((f: Festival) => !existingIds.has(f.eventId));
  return [...prev, ...filtered];
});

이로써 동일한 페이지 데이터를 여러 번 불러오더라도 UI에는 중복 표시되지 않게 되었습니다.

2. useCallback 의존성 최적화

함수 내부에서 page를 참조하는 방식은 useCallback 재생성을 유발했습니다. 이를 함수 인자로 분리해 해결했습니다:

const loadFestivals = useCallback(async (pageToLoad: number) => {
  const today = formatDate(new Date());
  const response = await axiosInstance.get('/api/event', {
    params: { startDate: today, endDate: today, page: pageToLoad, size: 5 },
  });
  // 중복 필터링 로직 포함
}, []);

useEffect(() => {
  loadFestivals(page);
}, [page]);

3. TypeScript 오류 해결

다음과 같은 오류 메시지가 떴습니다:

Parameter 'f' implicitly has an 'any' type.ts(7006)

이는 map이나 filter의 매개변수 타입을 명시하지 않았기 때문이었습니다. 아래와 같이 Festival 타입을 명시해 해결했습니다:

prev.map((f: Festival) => f.eventId);
newEvents.filter((f: Festival) => !existingIds.has(f.eventId));

✅ 리팩토링 결과 및 성과

  • 중복 데이터 제거: 렌더링 성능 및 UX 개선
  • 함수 재사용 최적화: 불필요한 옵저버 재설정 방지
  • TypeScript 안정성 향상: 코드 가독성과 유지보수성 향상

 

728x90

'프론트엔드 > TypeScript' 카테고리의 다른 글

error 타입 모듈 AxiosError  (1) 2025.05.09