본문 바로가기
프로젝트 답지

답지 영상 데이터 소모량 개선

by 띠리에이터 2025. 1. 3.

🚨 문제점: 과도한 데이터 소모 & 깜빡임 현상 발생

초기 방식: 모든 동영상을 한 번에 불러옴

초기에는 FlatList를 사용해 한 페이지당 **3개의 답지 인덱스(최대 45개 영상)**를 불러오도록 구현했다.
하지만 모든 동영상이 동시에 요청되고 자동 재생되어, 데이터 사용량이 급격히 증가했다.

📌 문제점 정리
한 페이지당 최대 2.5GB 데이터 사용
화면에 보이지 않는 동영상도 미리 로드됨
스크롤 시 깜빡임 발생 (썸네일 → 동영상 전환 시)

해결 목표:

  • 보이는 동영상만 로드 & 재생하도록 최적화
  • 불필요한 데이터 요청을 최소화
  • 부드러운 전환을 위한 깜빡임 방지

🔍 해결 과정: 최적화 적용하기

1. onViewableItemsChanged 활용 - 보이는 영상만 재생

📌 기존 방식

  • FlatList가 한 번에 모든 동영상을 렌더링
  • 보이지 않는 동영상도 API 요청 & 자동 재생 → 데이터 낭비

📌 개선 방법

  • onViewableItemsChanged를 사용해 뷰포트(현재 보이는 화면)에 있는 동영상만 재생
  • setVisiblePostIndex()를 이용해 현재 인덱스만 업데이트
const onViewableItemsChanged = useRef(({ viewableItems }: any) => {
    if (viewableItems.length > 0) {
      const visibleIndex = viewableItems[0].index;
      setVisiblePostIndex(visibleIndex);
    }
}).current;
 

적용 후:
✔ 화면에 보이는 동영상만 API 요청
✔ 데이터 사용량 대폭 감소
✔ 불필요한 GPU/CPU 리소스 사용 줄어듦


2.  Lazy Loading (지연 로딩) 적용 - 썸네일 먼저 로드 후 동영상 요청

📌 기존 방식

  • media 배열에 포함된 모든 동영상 URL을 즉시 요청
  • 초기 로딩 시 불필요한 네트워크 사용량 증가

📌 개선 방법

  • 썸네일 이미지(thumbnailUrl)를 먼저 로드
  • 동영상은 사용자가 실제로 보려고 할 때만 요청
const renderItem = ({ item, index }: { item: string; index: number }) => (
    <View style={styles.videoBox}>
      {isParentVisible && currentIndex === index ? (
        <VideoItem uri={item} isPlaying={true} />
      ) : (
        <Image
          source={{ uri: "thumbnail_url" }} // 썸네일 URL 사용
          style={{ width: SCREEN_WIDTH, height: 200 }}
        />
      )}
    </View>
  );

적용 후:
썸네일만 표시 → 동영상이 필요할 때만 요청
네트워크 트래픽 감소 → 데이터 사용량 절감
렌더링 속도 개선 → 사용자 경험(UX) 향상


3. API 요청 방식 변경 - 개별 동영상 요청으로 최적화

📌 기존 방식

  • 답지별로 한 번에 모든 동영상을 로드
  • 사용자가 실제로 보지 않는 영상도 불필요하게 요청

📌 개선 방법

  • media 배열을 제거하고 각 동영상을 개별 API 요청
  • 사용자가 해당 동영상을 클릭하거나 스크롤할 때만 요청
const {
    data: currentVideoUrl,
    isLoading, 
} = useQuery({
    queryKey: ["singleVideoDatasKey", postId, currentIndex],
    queryFn: () => fetchRenderSingleVideo(postId, currentIndex),
    enabled: !!isParentVisible, 
});

적용 후:
✔ API 요청 수는 증가하지만, 총 데이터 소모량은 획기적으로 감소
한 번에 모든 데이터를 불러오지 않고, 필요한 데이터만 가져옴

 

- 하지만 여기서 인덱스를 바꿀 때마다 데이터를 계속 요청하늠 문제 발생, 

-  인덱스가 바뀔 때마다 쿼리 키가 달라지기 때문일것이라 생각, 


4. React Query staleTime, gcTime 적용 - 캐시를 활용해 중복 요청 방지

📌 기존 문제

  • 인덱스를 변경할 때마다 같은 데이터를 반복 요청
  • 불필요한 API 호출 증가 → 성능 저하 & 데이터 낭비

📌 개선 방법

  • staleTime을 설정해 캐시된 데이터를 일정 시간 유지
  • 동일한 key로 요청해도, 백그라운드에서 재요청하지 않고 캐시 데이터를 사용
const {
    data: currentVideoUrl,
  } = useQuery({
    queryKey: ["singleVideoDatasKey", postId, currentIndex],
    queryFn: () => fetchRenderSingleVideo(postId, currentIndex),
    enabled: !!isParentVisible, 
    staleTime: 1000 * 60 * 5, // 5분 동안 fresh 상태 유지
    gcTime: 1000 * 60 * 10,   // 10분 후 캐시 삭제
  });

적용 후:
동일한 인덱스의 데이터를 반복 요청하지 않음
캐시 덕분에 빠른 로딩 & 성능 최적화

 

- fresh상태가 되어 인덱스를 넘겨도 데이터가 재요청 되지 않음, 일단은 성공적


5. 깜빡임 방지 - isVideoReady 상태 추가

📌 기존 문제

  • 동영상이 로드될 때 썸네일에서 동영상으로 전환하는 순간 깜빡임 발생
  • 썸네일과 동영상의 비율이 다를 경우 UI 불안정

📌 개선 방법

  • isVideoReady 상태를 추가하여 동영상이 준비된 후에만 썸네일 제거
  • onReadyForDisplay 이벤트를 활용하여 비디오 로드 완료 후 상태 업데이트
const handleVideoReady = (index: number) => {
  setVideoReadyStates((prev) => ({
    ...prev,
    [index]: true,
  }));
};

const renderItem = ({ item, index }: { item: string; index: number }) => (
  <View style={styles.videoBox}>
    <VideoItem
      uri={index === currentIndex ? currentVideoUrl : null}
      isPlaying={!!isParentVisible && currentIndex === index}
      isVideoReady={!!videoReadyStates[index]}
      onVideoReady={() => handleVideoReady(index)}
      thumbnailUri={item} // 썸네일 URL 전달
    />
  </View>
);

적용 후:
썸네일이 사라지기 전에 동영상이 완전히 준비됨 → 깜빡임 방지
썸네일과 동영상의 크기를 동기화하여 UI 안정성 향상