react-native

React Native 성능 최적화 전략(1)

프론트엔드개발자치키닝 2025. 2. 24. 10:27

단계별 성능 최적화 계획

1. 메모리 누수 방지

1.1 메모리 누수가 발생하는 일반적인 원인

const BadExample = () => {
  useEffect(() => {
    const interval = setInterval(() => {
      fetchData();
    }, 1000);
    // 메모리 누수 발생! cleanup 함수 없음
  }, []);
};

1.2 메모리 누수 해결 방법

const GoodExample = () => {
  useEffect(() => {
    const interval = setInterval(() => {
      fetchData();
    }, 1000);
    
    return () => {
      clearInterval(interval);
      // 구독 해제
      someSubscription?.unsubscribe();
      // 이벤트 리스너 제거
      someEventEmitter?.remove();
    };
  }, []);
};

2. 렌더링 성능 최적화

2.1 불필요한 리렌더링 방지

const ItemComponent = React.memo(({ item }) => {
  return (
    <View>
      <Text>{item.title}</Text>
      <Text>{item.description}</Text>
    </View>
  );
});

const OptimizedList = ({ data }) => {
  const renderItem = useCallback(({ item }) => (
    <ItemComponent item={item} />
  ), []);

  const memoizedData = useMemo(() =>
    data.map(item => ({
      ...item,
      processed: heavyComputation(item)
    }))
  , [data]);

  return (
    <FlatList
      data={memoizedData}
      renderItem={renderItem}
      getItemLayout={(data, index) => ({
        length: ITEM_HEIGHT,
        offset: ITEM_HEIGHT * index,
        index,
      })}
      removeClippedSubviews={true}
      maxToRenderPerBatch={10}
      windowSize={5}
      initialNumToRender={5}
    />
  );
};

3. 이미지 최적화

3.1 이미지 캐싱 및 사이즈 최적화

import FastImage from 'react-native-fast-image';

const OptimizedImage = ({ uri, size }) => {
  const [imageError, setImageError] = useState(false);
  const [loading, setLoading] = useState(true);

  const imageSize = useMemo(() => ({
    width: size,
    height: size,
  }), [size]);

  const source = useMemo(() => ({
    uri: uri,
    headers: { Authorization: 'auth-token' },
    priority: FastImage.priority.normal,
    cache: FastImage.cacheControl.immutable,
  }), [uri]);

  return (
    <View style={[styles.container, imageSize]}>
      <FastImage
        style={imageSize}
        source={source}
        onLoadStart={() => setLoading(true)}
        onLoadEnd={() => setLoading(false)}
        onError={() => setImageError(true)}
        resizeMode={FastImage.resizeMode.cover}
      />
      {loading && <ActivityIndicator />}
      {imageError && <Text>이미지 로드 실패</Text>}
    </View>
  );
};

3.2 이미지 프리로딩

const preloadImages = (images) => {
  const imageUrls = images.map(image => ({
    uri: image.uri,
    priority: FastImage.priority.low,
  }));

  FastImage.preload(imageUrls);
};

4. 성능 모니터링

4.1 성능 측정 유틸리티

import { PerformanceObserver, performance } from 'perf_hooks';

export const PerformanceMonitor = {
  startMeasure: (label) => {
    performance.mark(`${label}-start`);
  },

  endMeasure: (label) => {
    performance.mark(`${label}-end`);
    performance.measure(
      label,
      `${label}-start`,
      `${label}-end`
    );
    
    const measurements = performance.getEntriesByName(label);
    console.log(`${label} 실행 시간:`, measurements[0].duration);
  },

  setupObserver: () => {
    const obs = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      entries.forEach((entry) => {
        console.log(`${entry.name}: ${entry.duration}ms`);
      });
    });
    
    obs.observe({ entryTypes: ['measure'] });
    return obs;
  }
};

4.2 실제 사용 예시

const MainScreen = () => {
  useEffect(() => {
    const observer = PerformanceMonitor.setupObserver();
    
    return () => {
      observer.disconnect();
    };
  }, []);

  const handlePress = () => {
    PerformanceMonitor.startMeasure('expensive-operation');
    // 무거운 작업 수행
    expensiveOperation();
    PerformanceMonitor.endMeasure('expensive-operation');
  };

  return (
    <View>
      <Button onPress={handlePress} title="성능 측정" />
    </View>
  );
};

결론

react-native는 최적화가 되어있지 않으면 성능저하가 두드러지게 나타나기 때문에 반드시 코드를 작성한 뒤에 성능측정 하여 최적화를 해주어야할 것 같다.