본문 바로가기
Frontend/React

React: useEffect에 대해서

by 코딩쥐 2024. 9. 3.

useEffect는 컴포넌트의 생명주기 동안 특정 작업을 수행할 수 있게 해주는 리액트 훅이다. useEffect가 나오기 전에는 클래스 컴포넌트에서 생성(componentDidMount), 업데이트(componentDidUpdate), 제거(componentWillUnmount) 메서드를 통해 생명주기 변화에 대응했었다. 현재는 useEffect를 통해 간편하게 사용할 수 있게 되었다. 

 

useEffect

  • useEffect( () => {
      생성이 되었을 때 시행되는 로직
      return () => {제거 되었을 때 시행되는 로직}
      }, [ 변화를 관찰할 props/state ]

생성(mounting)은 컴포넌트가 처음 생성되어 DOM에 추가될 때를 의미하며, 제거(unmounting)은 컴포넌트가 DOM에서 제거될 때를 의미한다. 업데이트(updating)의 경우 컴포넌트의 상태나 속성이 변경되어 다시 렌더링 될 때를 의미하는데, useEffect에서는 의존성 배열([])안에 명시된 값이 변경될 때 해당하는 useEffect가 다시 시행된다. 의존성 배열을 적지 않으면 모든 렌더링에 useEffect가 시행되고, 의존성 배열이 비어있을 경우([]) 생성과 제거시에만 시행된다. 

 

useEffect예제

useEffect의 생성(mounting)

import './App.css'
import { useState, useEffect } from 'react';

function App() {
  const [num, setNum] = useState(0);

  useEffect(() => {
    console.log("컴포넌트 생성")
  })

  return (
    <>
      <button onClick={() => setNum(num => num+1)}>재렌더링</button>
    </>
  )
}

export default App

 

처음에 생성되어 DOM에 추가될 때 로직이 시행되는 것을 볼 수 있고, 이후 버튼을 클릭했을 때 재렌더링이 되면서 로직이 시행되는 것을 볼 수 있다. useEffect 사용 시에 의존성 배열을 작성하지 않았기 때문에 모든 렌더링에 useEffect가 시행되어서 "컴포넌트 생성"이라는 콘솔이 찍힌 모습을 볼 수 있다. 

 

 

useEffect의 업데이트(updating)

컴포넌트의 상태나 속성이 변경되어 다시 렌더링 될 때를 의미하는데, useEffect에서는 의존성 배열에 따라서 업데이트에 차이가 있다. 

  • 의존성배열을 작성하지 않았을 때 : 모든 렌더링에서 useEffect 로직 시행
  • 의존성배열을 빈 배열([])로 작성했을 때 : DOM에 추가될 때 한 번 시행되고 이후에 시행되지 않음
  • 의존성배열 안에 명시된 값([값])이 있을 때 : 명시된 값이 변경될 때마다 useEffect 시행
import './App.css'
import { useState, useEffect } from 'react';

function App() {
  const [num, setNum] = useState(0);
  const [text, setText] = useState("");

  useEffect(() => {
    console.log("의존성배열이 없는 useEffect")
  })

  useEffect(() => {
    console.log("의존성배열이 비어있는([]) useEffect")
  },[])

  useEffect(() => {
    console.log("의존성배열에 num만 있는([num]) useEffect")
  },[num])

  return (
    <>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button onClick={() => setNum(num => num+1)}>재렌더링</button>
    </>
  )
}

export default App

 

처음에 생성되어 컴포넌트가 DOM에 추가되었을 때 3개의 useEffect가 전부 다 시행된다. input에 글자를 작성하면 setText에 의해 재렌더링이 발생하면서 의존성배열이 없는 useEffect만 시행된다. button을 클릭하면 setNum에 의해서 재렌더링이 발생하면서 의존성 배열이 없는 useEffect와 의존성 배열에 [num]을 명시한 useEffect가 시행된다.

 

 

useEffect의 제거(unmounting)

useEffect return 결과에 작성되어 리턴되는 함수를 clean up function이라고 한다. 컴포넌트가 제거 될 때 이 함수가 1회 시행된다. clean up function이 1회 시행된다. 

import './App.css'
import { useState, useEffect } from 'react';

function App() {
  const [num, setNum] = useState(0);

  useEffect(() => {
    console.log("컴포넌트 생성");

    return () => {
      console.log("컴포넌트 제거")
    }
  },[num])

  return (
    <>
      <button onClick={() => setNum(num => num+1)}>재렌더링</button>
    </>
  )
}

export default App

 

버튼을 클릭해서 setNum을 통해서 재렌더링을 시행할 때 컴포넌트가 DOM이 제거되었다가 다시 생성이 되는 모습을 볼 수 있다. 


useEffect 실행시점

컴포넌트 안의 로직은 렌더링이 되기 전에 실행되고, useEffect의 경우 렌더링이 다 된 이후에 시행된다. 이런 실행 시점의 차이 때문에 시간이 오래 걸리는 반복연산, 서버에서 데이터를 가져오는 작업, 타이머 등의 경우 useEffect 를 사용하여 로직을 시행한다.

import './App.css'
import { useState, useEffect } from 'react';

function App() {

  console.log("컴포넌트 안 로직 1: 1");
  useEffect(() => { console.log("useEffect 안의 로직 : 2"); })
  console.log("컴포넌트 안 로직 2: 3");

  return (
    <>
      {console.log("return 문 안 로직 : 4")}
    </>
  )
}

export default App

 

컴포넌트 안의 로직은 렌더링이 되기 전에 실행 된 후 컴포넌트의 return 문이 렌더링이 되고, useEffect 로직이 렌더링 된 이후에 시행된 것을 볼 수 있다.

 

컴포넌트가 중첩되어 있을 경우에는 안쪽 컴포넌트에서부터 전체페이지로 useEffect가 전파된다. 아래의 예시를 보면 Inner에서 Outer 그리고 App으로 useEffect가 시행되는 모습을 볼 수 있다. 

import { useEffect } from 'react'
import './App.css'

const Inner = () => {
  useEffect(() => {
    console.log("Inner의 useEffect");
  })

  return (
    <>
    </>
  );
}

const Outer = () => {
  useEffect(() => {
    console.log("Outer의 useEffect");
  })

  return (
    <Inner />
  )
}

function App() {
  useEffect(() => {
    console.log("App의 useEffect");
  })

  return (
    <Outer />
  )
}

export default App