늘 겸손하게

React - useEffect 두 번 렌더링되는 문제 해결 (의존성 배열 활용) 본문

Toy Projects 기록/Korea Crime Graph

React - useEffect 두 번 렌더링되는 문제 해결 (의존성 배열 활용)

besforyou999 2023. 10. 28. 14:48

 

 

문제 상황

 

useEffect 메서드의 첫 번째 인자로 컴포넌트가 렌더링 된 이후에 실행할 코드를 넘겨주었습니다.

 

api를 통해 데이터를 가져오는 코드였는데 데이터를 성공적으로 가져오면 dataRead state를 true로 변경하여 loading 화면에서 실제 앱 화면으로 변환되도록 구현했습니다.

 

하지만 불필요하게 api 호출을 두 번하는 문제가 발생했습니다. 

 

useEffect(() => {
    async function fetch_data() {
      console.log("fetch_data")
      console.log("dataRead : ", dataRead)
      const api_key = process.env.REACT_APP_APIKEY;
      const url = `https://api.odcloud.kr/api/15085727/v1/uddi:d57791f7-1e1e-46c9-bbfd-911fa64ee8a4?page=1&perPage=200&serviceKey=${api_key}&dataType=JSON`;
      try {
        const result = await fetch(url)
        fetch_success(result)
      } catch (err) {
        console.log(err);
        console.log("데이터 불러오기 실패. csv 데이터 파일 파싱 실행")
        fetch_fail()
      }
    }
    fetch_data()
 })

 

 

문제 원인

 

useEffect 내부 코드가 실행되어 데이터를 제대로 읽어오면 dataRead state를 변경합니다.

 

컴포넌트는 state가 변경되면 리렌더링 되기 때문에 useEffect -> 코드 실행, api 호출 -> state 변경 -> 컴포넌트 리렌더링 -> useEffect -> 코드 실행, api 호출하는 루프가 생겨 발생한 문제였습니다.

 

 

해결책 - dependencies 배열 (의존성 배열)

 

useEffect의 두 번째 인자로 dependencies 배열을 줄 수 있습니다. 이 배열에는 useEffect 첫 인자로 준 코드에서 의존하고 있는 데이터 (props, state, 변수들)를 담는것이 원칙입니다. 이 dependencies 배열(의존성 배열)의 역할은 무엇일까요?

 

useEffect(setup, dependencies?)

 

useEffect는 컴포넌트가 렌더링 되었을때 처음 한 번 렌더링 되고, 의존성 배열에 담긴 값들이 변경되면 변경된 데이터를 가지고 useEffect setup 코드, 즉 첫 인자로 전달한 코드를 다시 실행합니다.

즉, setup 함수 내부의 데이터값을 동기화시키기 위해 setup 함수가 의존하고 있는 props, state, 변수를 dependencies 배열에 담아 두 번째 인자로 전달하는 것입니다.

 

이 의존성 배열로 setup 코드가 두 번 실행되는 것을 막을 수 있습니다. 의존성 배열로 배열에 아무것도 담지 않은 빈 배열을 전달한다면 setup 코드는 컴포넌트가 렌더링되고 단 한번 실행됩니다. 지금 문제를 해결하기 위한 알맞은 선택지인 거죠.

 

그래서 위에 있는 코드에서 두 번째 인자로 빈 배열을 전달하면

 

useEffect(() => {
    async function fetch_data() {
      console.log("fetch_data")
      console.log("dataRead : ", dataRead)
      const api_key = process.env.REACT_APP_APIKEY;
      const url = `https://api.odcloud.kr/api/15085727/v1/uddi:d57791f7-1e1e-46c9-bbfd-911fa64ee8a4?page=1&perPage=200&serviceKey=${api_key}&dataType=JSON`;
      try {
        const result = await fetch(url)
        fetch_success(result)
      } catch (err) {
        console.log(err);
        console.log("데이터 불러오기 실패. csv 데이터 파일 파싱 실행")
        fetch_fail()
      }
    }
    fetch_data()
 }, [])

 

setup 코드는 단 한번 실행됩니다.

 

 

 

dependencies 배열 특징에 따른 결과

 

useEffect에 두 번째 인자를 어떻게 전달하는지에 따라 실행방식이 달라집니다. 크게 세 가지 경우로 나눌 수 있습니다.

 

 

1. 의존성 배열 전달 안 함

 

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

 

의존성 배열을 주지 않으면 컴포넌트가 리렌더링 될 때마다 useEffect가 실행됩니다.

이전과 같은 작업을 반복하게 될 가능성이 존재합니다.

 

 

2. 의존성 배열에 의존하는 데이터(props, state, 변수 등) 전달

 

useEffect(() => {
	// ...
}, [a, b]);

 

setup 코드가 의존하고 있는 데이터가 변경될 경우 useEffect가 다시 실행되어 setup 코드의 데이터가 동기화됩니다. 

setup 코드가 api를 통해 데이터를 호출하는 경우, 호출할 페이지 번호, 데이터 이름, url를 의존성 배열에 넣어 전달하면 url 혹은 호출할 페이지 번호가 변하면 useEffect가 다시 실행되게끔 구현할 수 있습니다.

 

 

3. 의존성 배열에 빈 배열 전달

 

useEffect(() => {
	// ...
}, []);

 

컴포넌트 내부 데이터(props, state, 변수 등)에 어떤 변화가 생기더라도 useEffect는 단 한번 호출됩니다.

setup 코드가 반복할 필요 없이 단 한번 수행하면 되는 작업인 경우 사용하기 적합합니다.