All'alba vincerò

At dawn, I will win!

React

[React] useEffect() : 렌더링 시 발생하는 사이드 이펙트 처리

나디아 Nadia 2024. 8. 18. 19:02

📌 useEffect()

: 리액트 컴포넌트와 외부 시스템을 동기화할 때 사용

    • 렌더링 자체에 의해 발생하는 부수 효과를 특정하는 것
    • 특정 이벤트가 아닌 렌더링에 의해 직접 발생한다.
      (어떤 상호작용과도 상관없이 발생, 커밋 단계 이후 실행 됨)
    • React는 화면을 업데이트한 다음 useEffect 내부의 코드를 실행한다.
      ➡️ useEffect는 화면에 렌더링이 반영될 때까지 코드 실행을 지연 시킨다.

    • 외부 시스템(external)
      : React에서 제어되지 않는 시스템

      (ex. 네트워크, 브라우저 API, 서드파티 라이브러리와의 연결, 데이터 가져오기, DOM 수정, 타이머 설정, 구독 등)
      • ex) 서버에 접속하는 것
        ➡︎ 순수한 계산이 아니고 부수 효과를 발생시키기 때문에 렌더링 중에는 할 수 없다❌
        ➡︎ 이벤트도 아니다❌
        👉 이펙트(Effects) 사용⭕
  • 컴포넌트
    • 렌더링 ➡︎ 순수 함수
    • 이벤트(사용자 액션 O) ➡︎ 이벤트 핸들러
    • 부수 효과 (사용자 액션 X)  ➡︎ Effect 함수

 

useEffect(setup, dependencies?)
    • 매개변수
      • setup
        : Effect 로직이 포함된 설정 함수
        • clean up(정리) 함수를 반환
        • DOM 커밋 이후 시점에서 실행
        • 상태가 업데이트 될때마다 실행
      • dependencies
        : 의존성(종속성) 배열

 

 

 

⏩ 사용

 

1. Effect 불러오기 

  • 기본적으로 Effect는 모든 commit 이후에 실행된다.
import { useEffect } from 'react';

 

 

 

2. Effect 선언하기

  • 컴포넌트의 최상위 레벨에서 호출
  • 부수 효과를 렌더링 연산에서 분리하기 위해 useEffect로 감싼다.
function 컴포넌트명() {

  useEffect(() => {
      //...
  });
  
  return (
  );
}

 

 

 

3. 의존성 배열(dependency array) 지정

  • useEffect가 언제 실행될지를 결정하는 데 사용
    👉 Effect를 불필요하게 다시 실행하지 않도록 지시하기 위해 설정
  • 배열에 포함된 변수들(useEffect가 의존하는 값들)이 변경될 때마다 useEffect는 다시 실행된다.
  • 의존성 배열에 지정된 값이 이전 렌더링과 동일한지 비교하고,  동일한 값이면 Effect를 다시 실행하지 않는다.
  useEffect(() => {
    // ...
  }, [dependency]);

 

 

✨ 의존성 배열이 없는 경우와 [] 의존성 배열이 있는 경우

// 모든 렌더링 후에 실행
useEffect(() => {
  // ...
});

// 처음 한 번만 실행(마운트될 때, 컴포넌트가 나타날 때)
useEffect(() => {
  // ...
}, []);

// 마운트될 때 실행, 렌더링 이후에 a 또는 b 중 하나라도 변경된 경우에도 실행
useEffect(() => {
 // ...
}, [a, b]);

 

 

 

4. 클린업 함수(cleanup function) 추가

  • Effect는 수행 중이던 작업을 취소, 정리하는 방법을 지정해야 한다.
    ex) 연결 ↔️ 연결 해제 / 구독 ↔️ 구독 취소 / 불러오기(fetch) ↔️ 취소
    • 이벤트 리스너: addEventListener() ↔️  removeEventListener()]
    • 타이머: setInterval() ↔️  clearInterval()
    • 네트워크 요청: AbortController() ↔️  abort()
  • React는 Effect가 다시 실행되기 전마다 클린업 함수를 호출하고, 컴포넌트가 마운트 해제(제거)될 때에도 마지막으로 호출한다.
function 컴포넌트명() {

  useEffect(() => {
      //...
      
     return () => { 
        // 클린업(clean up) 함수
     };
  }, []);
  
  return (
    <div />
  );
}

 

 


✅ 렌더링 이슈

 

⏩ 이펙트(Effect) 내부에서 실행되는 함수

: 이 함수는 useEffect 안에서만 사용되기 때문에, 이펙트 외부의 상태나 변수를 참조하지 않는다. 

  • 이런 함수들은 이펙트가 리렌더링될 때마다 새로 만들어져도 큰 문제가 되지 않기 때문에
    굳이 메모이제이션(= useCallback 사용)할 필요는 없다.
  • 종속성 배열에 포함할 필요가 없다.
useEffect(() => {
  const logMessage = () => {
    console.log('Effect is running');
  };

  logMessage();
}, []); // logMessage는 useEffect 내부에 있으므로 종속성 배열에 포함할 필요가 없음

 

 

 

⏩ 이펙트(Effect) 외부에 있는 함수

: 이 함수가 상태나 다른 값을 참조하는 경우, 리렌더링 시에 해당 함수가 변할 수 있다. 

  • 이 함수가 변하면 useEffect는 다시 실행되어 잦은 리-렌더링을 유발할 수 있다. 
  • 만약 함수가 이펙트 외부에서 정의되어 있고, 리렌더링 시에 함수가 변할 가능성이 있다면, 
    1) useCallback을 사용하여 메모이제이션하거나
    2) 종속성 배열에 포함해야 한다.
    3) 가능한 경우 이펙트 내부에 함수를 포함하는 것이 더 좋다.
const fetchData = () => {
  // 데이터 fetch 로직
};

useEffect(() => {
  fetchData();
}, [fetchData]); // fetchData가 외부에 있으므로 종속성 배열에 포함되어야 함

 

 


📢 이벤트(Event)  vs  이펙트(Effect) 

  이벤트(Event) 이펙트(Effect)
동작 시점 사용자의 상호작용 시 즉각적으로 발생 컴포넌트가 렌더링된 후 또는 상태가 변경된 후 발생
주요 목적 유저 상호작용 처리 (클릭, 키 입력 등) 비동기 작업, 사이드 이펙트 처리 (API 호출, 타이머 등)
바인딩 위치 DOM 요소에 직접 연결
(onClick, onChange)
컴포넌트의 렌더링 후 발생하며,
리액트 컴포넌트 내부에서 처리
실행 타이밍 즉각적인 실행 렌더링 완료 후 비동기적으로 실행
사용 사례 클릭, 키보드 입력, 마우스 이동 등 데이터 가져오기, 타이머 설정, 구독, 외부 자원 접근 등
반환 값 보통 아무 것도 반환하지 않음❌ 클린업 함수 반환(이전 사이드 이펙트 정리)
반응 대상 유저의 직접적 행동 컴포넌트의 상태 변화, props 변경
클린업
필요
보통 클린업 불필요❌ 클린업 작업을 통해 메모리 누수 방지⭕

 

 


 

 

useEffect – React

The library for web and native user interfaces

ko.react.dev

 

 

Effect로 동기화하기 – React

The library for web and native user interfaces

ko.react.dev