메모리 누수는 앱이 점점 느려지다가 결국 크래시하는 원인이 됩니다. 특히 모바일에서는 메모리 제한이 엄격합니다.


흔한 메모리 누수 패턴

1. 언마운트된 컴포넌트에서 상태 업데이트

TSX
// 나쁜 예: 컴포넌트 언마운트 후 setState 호출
function BadComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData().then((result) => {
      setData(result); // 이미 언마운트되었을 수 있음!
    });
  }, []);
}

// 좋은 예: AbortController로 취소
function GoodComponent() {
  const [data, setData] = useState(null);

  useEffect(() => {
    const controller = new AbortController();

    fetch('/api/data', { signal: controller.signal })
      .then((res) => res.json())
      .then(setData)
      .catch(() => {}); // abort 에러 무시

    return () => controller.abort(); // 언마운트 시 요청 취소
  }, []);
}

2. 이벤트 리스너 해제 누락

TSX
// 나쁜 예
useEffect(() => {
  const subscription = AppState.addEventListener('change', handleChange);
  // return으로 해제하지 않음!
}, []);

// 좋은 예
useEffect(() => {
  const subscription = AppState.addEventListener('change', handleChange);
  return () => subscription.remove(); // 클린업 필수
}, []);

3. 타이머 해제 누락

TSX
useEffect(() => {
  const timer = setInterval(() => {
    fetchNewData();
  }, 5000);

  return () => clearInterval(timer); // 반드시 해제
}, []);

4. 클로저에 의한 참조 유지

TSX
// 큰 데이터를 클로저가 참조하고 있으면 GC가 수거하지 못함
function ProcessScreen() {
  const [largeData, setLargeData] = useState<LargeObject | null>(null);

  const handleProcess = useCallback(() => {
    // largeData를 클로저가 참조 → 메모리 유지
    processData(largeData);
  }, [largeData]);

  // 화면 벗어날 때 큰 데이터 해제
  useFocusEffect(
    useCallback(() => {
      return () => setLargeData(null);
    }, [])
  );
}

이미지 메모리 관리

TSX
// 큰 이미지는 메모리를 많이 차지함
// 4000x3000 RGBA = 약 48MB 메모리

// 해결: 적절한 크기로 리사이즈
<Image
  source={{ uri: imageUrl }}
  style={{ width: 200, height: 200 }}
  resizeMethod="resize"  // Android: 메모리에서 리사이즈
/>

// FlatList에서 이미지 목록
// removeClippedSubviews로 화면 밖 이미지 메모리 해제
<FlatList
  data={images}
  renderItem={renderImageItem}
  removeClippedSubviews={true}
  windowSize={3} // 작은 값으로 메모리 절약
/>

메모리 모니터링

TSX
// 개발 중 메모리 확인
// Xcode: Debug Navigator → Memory
// Android Studio: Profiler → Memory

// Flipper에서 메모리 스냅샷 촬영
// Heap 크기 변화를 시간순으로 추적

// React Native Performance Monitor
// 개발 메뉴 → "Show Perf Monitor"
// RAM 사용량 실시간 확인

최적화 전략

  1. useEffect 클린업을 항상 작성 하세요
  2. 큰 데이터는 화면 이탈 시 해제 하세요
  3. 이미지는 적절한 크기로 제공하세요
  4. FlatList의 windowSize를 줄여 렌더링 범위를 제한하세요
  5. ** 불필요한 전역 상태를 피하세요** — 화면 로컬 상태로 충분한 것은 로컬에 두세요

정리

  • useEffect의 클린업 함수 가 메모리 누수 방지의 핵심입니다
  • 이벤트 리스너, 타이머, 구독은 반드시 해제 하세요
  • 큰 이미지는 서버에서 적절한 크기로 제공 하는 것이 최선입니다
  • Xcode/Android Studio Profiler 로 메모리 사용량을 모니터링하세요
  • 메모리 문제는 점진적으로 나타나므로 장시간 사용 테스트 가 중요합니다
댓글 로딩 중...