웹 시스템 개발 #React UseEffect

학교 공부를 복습할 겸 적는 것이기에 내용이 부족할 수 있습니다.

 

부족한 것은 상관 없으나, 잘못된 부분이 발견된다면 지적해주시면 감사하겠습니다.


 

ref


React의 ref는 실제로 변경 시 추가 렌더링을 발생시키지 않고 구성 요소를 다시 렌더링하는 사이에 지속되는 값을 저장할 수 있습니다. DOM 요소에 직접 액세스하거나 업데이트 시 다시 렌더링을 트리거할 필요가 없는 값을 추적하는 데 자주 사용됩니다.

import React, { useRef } from 'react';

export default function Counter() {
  const ref = useRef(0);

  const handleClick = () => {
    ref.current = ref.current + 1;
    alert('You clicked ' + ref.current + ' times!');
  };

  return (
    <button onClick={handleClick}>
      Click me!
    </button>
  );
}

 

  • useRef는 0으로 초기화됩니다. 반환된 객체의 '.current' 속성은 변경 가능한 값 '0'이 저장되는 곳입니다.
  • 버튼을 클릭하면 handleClick 함수가 ref 개체의 .current 속성을 업데이트하여 개수를 추적합니다. 그러나 ref를 업데이트해도 useState 또는 setState와 같은 변경 사항을 React에 알리지 않기 때문에 이로 인해 구성 요소가 다시 렌더링되지 않습니다.
  • 업데이트된 ref.current 값이 경고에 사용됩니다.

 


effects

effects는 Component가 DOM에 렌더링된 후(즉, "커밋 단계" 이후) 수행될 수 있는 작업입니다. 특정 사용자 작업(예,

클릭, 양식 제출 또는 키보드 이벤트)과 직접적으로 연결되지 않은 'Side Effect'을 처리하는 데 사용됩니다.

 

'useEffect' 후크를 사용하여 구성 요소가 렌더링된 후 코드를 실행합니다.

useEffect(() => {
  // Your side effect code here.
});

 

종속성 지정: useEffect의 두 번째 인수는 effect가 다시 실행되어야 하는 시기를 React에게 알려주는 종속성 배열입니다.

useEffect(() => {
  // Code to run when `dependency` changes.
}, [dependency]);

 

 

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);
  
  // This is not correct; it should be inside an effect.
  if (isPlaying) {
    ref.current.play();
  } else {
    ref.current.pause();
  }

  return <video ref={ref} src={src} loop playsInline />;
}

export default function App() {
  const [isPlaying, setIsPlaying] = useState(false);

  return (
    <>
      <button onClick={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
    </>
  );
}

 

위와 같은 코드가 있다고 가정해보겠습니다. 플레이어는  버튼을 눌러 비디오 플레이어를 중단 혹은 플레이 할 수 있습니다. 위 코드에서는 렌더링 단계 중에 DOM을 직접 조작하려고 하지만, React에서는 이를 허용하지 않습니다. 즉, 버튼을 눌러도 비디오 플레이어를 컨트롤 할 수 없다는 것입니다.

 

대신 위와 같은 문제는 useEffect를 이용하여  DOM과 상호작용하는 side effect을 수행하여 해결할 수 있습니다.

import { useState, useRef, useEffect } from 'react';

function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  useEffect(() => {
    // Perform side effects after the DOM has been updated.
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  }, [isPlaying]); // Only re-run the effect if `isPlaying` changes.

  return <video ref={ref} src={src} loop playsInline />;
}

export default function App() {
  const [isPlaying, setIsPlaying] = useState(false);

  return (
    <>
      <button onClick={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
    </>
  );
}

 

이런식으로 useEffect를 이용하여 렌더링 중 DOM을 수정하여 비디오 플레이어를 멈추거나 실행시킬 수 있게 되었습니다.

 

주의 - 무한루프 

종속성을 올바르게 지정하지 않고 'useEffect'를 사용하면 무한 루프가 발생할 수 있습니다.

const [count, setCount] = useState(0);

useEffect(() => {
  setCount(count + 1); // This will cause an infinite loop
});

 

해결책은 다음과 같습니다.

useEffect(() => {
  // Runs only on mount
}, []);

 

초기 렌더링 후 한 번만 실행되게 하거나,

useEffect(() => {
  // Runs on mount and if `a` or `b` change
}, [a, b]);

 

마운트 될 시에만 실행시키는 방법이 있습니다.

 

 

필요한 경우 정리

일부 effect는 메모리 누수나 기타 원치 않는 동작을 방지하기 위해 정리가 필요합니다. 구독, 타이머 또는 외부 서비스 연결의 경우를 예로 들 수 있습니다. 정리 함수는 effect에서 반환되며 구성 요소가 마운트 해제되거나 효과가 다시 실행되기 전에 실행됩니다.

useEffect(() => {
  const connection = createConnection(); 
  connection.connect();

  return () => {
    connection.disconnect(); // Cleanup function
  };
}, []); // Empty array means this effect runs only on mount.

 

마운트란?

웹 개발 및 React의 맥락에서 "마운트"라는 용어는 DOM(Document Object Model)에 구성 요소를 초기화하고 삽입하는 프로세스를 나타냅니다. React 구성 요소가 "마운트"된다는 것은 해당 구성 요소가 처음으로 생성되어 웹 페이지에 삽입된다는 의미입니다. 

 

쉽게 생각하면 렌더링될 때 마운트가 된다고 생각하시면 됩니다.