[React] React에서 Ajax 요청

따봉도치 개발자·2023년 4월 26일
0

Effect Hook

우선 Ajax요청을 배우기 전에 Effect Hook에 대해서 알아보자.

Side Effect

함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다. React에서는 컴포넌트 내에서 fetch를 사용해 API 정보를 가져오거나 이벤트를 활용해 DOM 직접 조작할 때 Side Effect가 발생한다.

let foo = 'hello';

function bar() {
  foo = 'world';
}

bar(); // bar는 Side Effect를 발생시킵니다!

Pure Function (순수 함수)

순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미한다. 함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우, 순수 함수라고 부를 수 없다. 또한 순수 함수는, 입력으로 전달된 값을 수정하지 않는다.

function upper(str) {
  return str.toUpperCase(); // toUpperCase 메소드는 원본을 수정하지 않습니다 (Immutable)
}

upper('hello') // 'HELLO'

순수 함수에는 네트워크 요청과 같은 Side Effect가 없다. 순수 함수의 특징 중 하나는, 어떠한 전달 인자가 주어질 경우, 항상 똑같은 값이 리턴된다. 그래서 예측 가능한 함수이다.

Effect Hook

useEffect는 컴포넌트 내에서 Side effect를 실행할 수 있게 하는 Hook이다. 이 컴포넌트에서 실행하는 Side effect는 브라우저 API를 이용하여, 타이틀을 변경할 수 있다.

API

useEffect의 첫 번째 인자는 함수이고 해당 함수 내에서 side effect를 실행하면 된다. 이 함수는 아래 조건에서 실행된다.

조건부 effect 발생 (dependency array)

useEffect의 두 번째 인자는 배열이다. 이 배열은 조건을 담고 있다. 여기서 조건은 boolean 형태의 표현식이 아닌, 어떤 값의 변경이 일어날 때를 의미한다. 따라서, 해당 배열엔 어떤 값의 목록이 들어간다. 이 배열을 특별히 종속성 배열이라고 부른다.
API useEffect(함수, [종속성1, 종속성2, ...])
useEffect의 두 번째 인자는 종속성 배열이다. 배열 내의 종속성1, 또는 종속성2의 값이 변할 때, 첫 번째 인자의 함수가 실행된다.
배열 내의 어떤 값이 변할 때에만, (effect가 발생하는) 함수가 실행된다.

단 한 번만 실행되는 Effect 함수

만약 종속성 목록에 아무런 종속성이 없다면 즉, 두 번째 배열을 빈 배열로 둘 경우와 아예 안 넘길 때 어떤 차이가 있을까?
빈 배열(useEffect(함수, []))을 useEffect의 두 번째 인자로 사용하면, 이때에는 컴포넌트가 처음 생성될 때만 effect 함수가 실행한다.
아예 안넘길 때(useEffect(함수)) useEffect는 컴포넌트가 처음 생성되거나, props가 업데이트되거나, 상태(state)가 업데이트될 때 effect 함수가 실행된다.

컴포넌트 내에서 Ajax 요청(내부,외부)

내부에서 불러올 때

import { useEffect, useState } from "react";
import "./styles.css";
import { getProverbs } from "./storageUtil";

export default function App() {
  const [proverbs, setProverbs] = useState([]);
  const [filter, setFilter] = useState("");

  useEffect(() => {
    console.log("언제 effect 함수가 불릴까요?");
    const result = getProverbs();
    setProverbs(result);
  }, []);

  const handleChange = (e) => {
    setFilter(e.target.value);
  };

  return (
    <div className="App">
      필터
      <input type="text" value={filter} onChange={handleChange} />
      <ul>
        {proverbs
          .filter((prvb) => {
            return prvb.toLowerCase().includes(filter.toLowerCase());
          })
          .map((prvb, i) => (
            <Proverb saying={prvb} key={i} />
          ))}
      </ul>
    </div>
  );
}

function Proverb({ saying }) {
  return <li>{saying}</li>;
}
**외부에서 불러올 때**
import { useEffect, useState } from "react";
import "./styles.css";
import { getProverbs } from "./storageUtil";

export default function App() {
  const [proverbs, setProverbs] = useState([]);
  const [filter, setFilter] = useState("");
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log("언제 effect 함수가 불릴까요?");
    const result = getProverbs(filter);
    setProverbs(result);
  }, [filter]);

  const handleChange = (e) => {
    setFilter(e.target.value);
  };

  const handleCounterClick = () => {
    setCount(count + 1);
  };

  return (
    <div className="App">
      필터
      <input type="text" value={filter} onChange={handleChange} />
      <ul>
        {proverbs.map((prvb, i) => (
          <Proverb saying={prvb} key={i} />
        ))}
      </ul>
      <button onClick={handleCounterClick}>카운터 값: {count}</button>
    </div>
  );
}

function Proverb({ saying }) {
  return <li>{saying}</li>;
}

내부, 외부에서 불러올 때 차이점

Ajax 요청 보내기!

임의로 만든 storageUtil.js 대신에 fetch API를 써서, 서버에 요청해보자

useEffect(() => {
  fetch(`http://서버주소/proverbs?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setProverbs(result);
    });
}, [filter]);

Ajax 요청이 느릴 경우 로딩화면 만들기

로딩화면을 구현하려면 상태처리가 필요하다.

const [isLoading, setIsLoading] = useState(true);

// 생략, LoadingIndicator 컴포넌트는 별도로 구현했음을 가정합니다
return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}

useEffect(() => {
  setIsLoading(true);
  fetch(`http://서버주소/proverbs?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setProverbs(result);
      setIsLoading(false);	// 이 부분!
    });
}, [filter]);
profile
Explain Like I'm 5

0개의 댓글