Front-End/React

[React] Page 랜딩 시, Axios로 받은 데이터 '1회만' state에 set하기 (useEffect 활용)

유자맛바나나 2022. 1. 16. 06:44

 

 

 Situation: 최초 Page 랜딩 시 서버로부터 받은 데이터를 state에 초기화하고 싶다

네이버 웹 페이지에 가면 내가 접속한 그 때 웹 페이지에 보여줄 데이터들을 서버로부터 받아 보여준다. 내가 원하는 것도 최초 Page 오픈 시 Axios를 이용해 서버로부터 데이터를 받아 state에 초기화하고, 해당 state를 화면에 보여주는 것이다.

 

참고로, Problem1에서 살펴 보겠지만 Axios를 이용해 서버로부터 받은 결과는 Promise다. 따라서 본 포스팅은 최초 Page 랜딩 시 Promise의 데이터를 state에 초기화하는 방법(useEffect를 활용)으로 이해해도 좋다.

 

[Basic knowledge] 함수형 컴포넌트의 state의 초기화 방법

기본적으로 함수형 컴포넌트의 state는 useState 내에 값을 전달하여 초기화한다.

 

const [variable, setVariable] = useState(초기화 값);

 

 Problem1: Axios의 결과는 Promise다

원하는 것은 Axios를 이용해 서버로부터 데이터를 받아와서 state에 초기화 하는 것이니 아래와 같이 Axios의 결과로 초기화해주면 어떨까?

export default function MyComponent() {
  const [variable, setVariable] = useState(axios.get(uri));
}

안타깝게도 axios의 결과는 Promise다. 내가 원하는 것은 Promise가 아니라 PromiseResult에 있는 data이다. 따라서 PromiseResult에 있는 data를 state에 set 하는 방법을 찾아야 한다.

 

axios의 결과는 Promise

 

 Problem2: 무한 렌더링 현상

axios의 결과가 Promise이니 Promise의 문법을 활용해 아래처럼 작성하면 되지 않을까?

export default function MyComponent() {
  const [variable, setVariable] = useState();
  
  axios.get(uri)
  .then(response => {
    console.log(response.data);
    setVariable(response.data);
  })
}

이번에도 안된다. 함수형 컴포넌트 내에 setState 코드를 작성하면 무한 렌더링 현상이 발생하기 때문이다. 

Problem2의 코드를 실행할 경우 무한 렌더링으로 현상으로 axios.get()을 무한히 호출한다

 

 

 Solution1: useEffect를 이용한 초기화

useEffect의 기본 동작은 모든 렌더링이 완료된 후 첫 번째 인자(Parameter)로 전달한 콜백(Callback) 함수가 실행되는 것이다. 그런데 전달한 콜백 함수에 setState가 포함되면 다시 렌더링하게 될 것이고, 또 다시 콜백 함수가 실행되며 무한 렌더링 문제인 Problem2가 발생하게 된다.

이를 방지하기 위해 useEffect의 두 번째 인자로 빈 배열([ ], Empty array)을 전달해 해결할 수 있다. 원래는 배열에 관찰하고 싶은 특정 변수(props, state)를 담아 해당 변수가 변경될때만 콜백이 실행될 수 있도록 하는 '조건부 useEffect'로 사용된다. 하지만 빈 배열([ ])을 전달하면 관찰 대상이 없으므로 최초 1회만 콜백 함수가 실행되고 그 뒤로 다시 실행되지 않는다.

 

 

export default function MyComponent() {
  const [variable, setVariable] = useState();
  
  useEffect(() => {    
    axios.get(uri)
    .then(response => {
      console.log(response.data);
      setVariable(response.data);
    })    
  }, []);
}

 

 Solution2: Event(버튼 클릭) 기반 일회성 작동

Event 기반 setState를 한다면 무한 렌더링을 피해갈 수 있다. 단 이 방법은 버튼 클릭과 같은 이벤트가 발생해야하기 때문에 이전 컴포넌트(Previous Page)에서 데이터를 요청해야 한다.

 

  • 버튼 클릭이라는 Event가 생길 때 일회성으로 requestToServer 함수가 작동한다
export default function MyComponent() {
  const [variable, setVariable] = useState();
  
  const requestToServer = () => {
    axios.get(uri)
    .then(response => {
      setVariable(response);
    })
  }
  
  return (
    <div>
      <button onClick={requestToServer}>Request to Server<button>
    </div>
  )
}