🧑🏻‍💻 useEffect


useEffect는 컴포넌트를 외부 시스템과 동기화할 수 있는 React 훅

🍪 useEffect 문법

useEffect(setup, dependencies?)
// setup: Effect의 로직이 포함된 함수
  • useEffect는 컴포넌트의 최상위 레벨 또는 자체 훅에서만 호출 가능하다. (React 훅)

🧑🏻‍💻 참고하기


🍪 주의 사항

  • Strict 모드가 켜져 있으면 React는 첫 번째 실제 셋업 전에 “개발 전용의 셋업+클린업 사이클”을 한 번 더 실행한다.
  • Effect가 상호작용으로 인한 것이 아니라면, React는 브라우저가 Effect를 실행하기 전에 업데이트된 화면을 먼저 그리도록 한다. 그러나, 상호작용으로 인해 Effect가 발생하더라도, 브라우저가 화면을 다시 칠하지 못하도록 차단해야 하는 경우에는 useLayoutEffect로 대체하는 것이 좋다.
  • Effects는 클라이언트에서만 실행된다. 서버 렌더링 중에는 실행되지 않는다.
  • 컴포넌트를 외부 시스템에 연결하려면 컴포넌트의 최상위 레벨에서 useEffect를 호출해야 한다.

🍪 실행 순서

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => { // 해당 시스템에 연결하는 셋업 코드가 포함된 셋업 함수
  	const connection = createConnection(serverUrl, roomId);
    connection.connect();
  	return () => {
      connection.disconnect(); 
		// 해당 시스템과의 연결을 끊는 클린업 코드가 포함된 클린업 함수
  	};
  }, [serverUrl, roomId]); // 해당 함수 내부에서 사용되는 컴포넌트의 모든 값을 포함한 의존성 목록.
  // ...
}
  1. 컴포넌트가 페이지에 추가될 때 (마운트) 마다 셋업 코드를 실행한다.
  2. 의존성이 변경된 컴포넌트를 다시 렌더링할 때마다
    1. 먼저 이전 props와 state로 클린업 코드를 실행한다.
    2. 그런 다음 새 props와 state로 셋업 코드를 실행한다.
  3. 컴포넌트가 페이지에서 제거되면 (마운트 해제) 마지막으로 한 번 클린업 코드를 실행한다.

🍪 반응형 의존성 지정

  • 반응형 값에는 props와 컴포넌트 내부에서 직접 선언된 모든 변수, 함수가 포함된다.
  • Effect의 의존성을 “선택”할 수 없고 Effect의 코드에서 사용되는 모든 반응형 값은 의존성으로 선언해야 하고 React 용으로 구성된 린터가 이를 체크한다.
  • 전달 예시
    1. 의존성 배열 전달하는 경우 : 마운트될 때 실행, 변경된 의존성으로 다시 렌더링한 후 실행, 언마운트할 때 클린업 실행
    2. 빈 의존성 배열 전달하는 경우 : 마운트될 때 실행, 언마운트할 때 클린업 실행
    3. 아예 의존성 배열을 전달하지 않는 경우 : 컴포넌트의 모든 렌더링(및 리렌더링) 후마다 매번 Effect가 실행
  • 렌더링 중에 생성된 객체나 함수는 의존성으로 사용하지 않고 대신 Effect 내에 생성하여 불필요한 의존성을 제거한다.

🧑🏻‍💻 활용 및 생각할 거리


🍪 자동 가비지 컬렉션에 의존하는 대신 명시적인 클린업 함수를 사용하는 것이 좋은 이유는?

  • 브라우저의 JavaScript 엔진에서는 더 이상 사용되지 않는 객체 및 리소스를 자동으로 수거하는 가비지 컬렉션이 있다. 메모리 해제를 담당하지만 그 동작을 제어할 수 없어서 명시적인 클린업 함수를 사용해 메모리 관리를 예측 가능하게 제어하는 것이 좋다. 이는 또한 코드의 가독성과 유지 보수의 효율을 올리고 메모리 누수 문제를 방지한다.

🍪 Effect에서 데이터 페칭하는 것을 비추천하는 이유는?

  • Effects에서 직접 데이터를 페칭하는 작업을 반복적으로 작성하면 나중에 캐싱 및 서버 렌더링과 같은 최적화를 추가하기가 어려워진다. 또한 코드 중복이 발생하고 유지 보수가 어려워질 수 있다. 직접 만들거나 커뮤니티에서 유지 관리하는 커스텀 훅을 사용하는 것이 더 효율적이고framework를 사용하는 경우, 프레임워크 빌트인 데이터 페칭 메커니즘을 사용하는 것이 더 효율적이다.

🍪 Effect의 이전 state를 기반으로 state를 업데이트하려는 경우 문제 해결 방법

import { useState, useEffect } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(c => c + 1); // ✅ Pass a state updater
    }, 1000);
    return () => clearInterval(intervalId);
  }, []); // ✅ Now count is not a dependency

  return <h1>{count}</h1>;
}
  • count + 1대신 c => c + 1을 전달하면 Effect는 더 이상 count에 의존할 필요가 없고 count가 변경될 때마다 interval을 다시 클린업하고 셋업하지 않는다.

🍪 서버와 클라이언트에 서로 다른 콘텐츠 표시하는 방법

  • hydration이 작동하려면 클라이언트와 서버의 첫 렌더링 결과가 동일해야 합니다. 드물지만 클라이언트에 다른 콘텐츠를 표시해야 하는 경우가 있을 수 있다. ex) 글을 30일동안 읽지 않기 (localStorage 이용, 서버와 관련 X)
    function MyComponent() {
      const [didMount, setDidMount] = useState(false);
    
      useEffect(() => {
        setDidMount(true); // 클라이언트 전용 렌더링 결과물로 전환
      }, []);
    
      if (didMount) {
        // ... return client-only JSX ...
      }  else { // 서버 렌더링 중에는 didMount는 false
        // ... return initial JSX ...
      }
    }
  • 연결 속도가 느린 사용자의 경우, 컴포넌트의 모양이 변경되는 것이 보일 수 있기 때문에 사용을 자제한다.

참고 https://react-ko.dev/reference/react/useEffect

profile
FE 개발자

0개의 댓글

Powered by GraphCDN, the GraphQL CDN