2 minute read

2022.12.06

💡 Effect Hook -2

📌 Effect Hook 조건부 실행

조건부 effect 발생 ( dependency array )

useEffect의 두 번째 인자는 배열이다. 이 배열은 boolean형태의 표현식이 아닌, 어떤 값의 변경이 일어날 때의 조건을 의미한다.

따라서 해당 배열엔 어떤 값의 목록이 들어간다. 이 배열을 특별히 종속성 배열이라고 부른다.

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>;
}

이 예제는, filter가 변할 때에만, effect 함수가 실행된다. 한편, 카운트를 올리는 버튼은 컴포넌트의 상태가 바뀌고 업데이트되지만, 아무리 버튼을 눌러도 effect 함수는 실행되지 않는다. 왜냐하면, 종속성 배열에는 filter만 존재하고, count는 존재하지 않기 때문

종합해서 이야기하면

useEffect(함수, [종속성1, 종속성2, …])

useEffect의 두 번째 인자는 종속성 배열이다. 배열 내의 종속성1, 또는 종속성2의 값이 변할 때, 첫 번째 인자의 함수가 실행된다.

배열 내의 어떤 값이 변할 때에만, (effect가 발생하는) 함수가 실행된다.

단 한 번만 실행되는 Effect 함수

만일 종속성 목록에 아무런 종속성도 없다면 어떤 일이 발생할까? 달리 말해, 두 번째 배열을 빈 배열 [ ]로 둘 경우에는 무슨 일이 발생할까? 두 번째 인자를 아예 안 넘기는 것과 어떻게 다를까?

  1. 빈 배열 넣기

    useEffect( 함수, [] )

  2. 아무것도 넣지 않기 ( 기본 형태 )

    useEffect( 함수 )

2번 기본 형태의 useEffect는 컴포넌트가 처음 생성되거나, props가 업데이트되거나, state가 업데이트될 때 effect 함수가 실행됨을 앞서 배웠다.

반면 1번 빈 배열을 useEffect의 두 번째 인자로 사용하면, 이때에는 컴포넌트가 처음 생성될 때만 effect 함수가 실행된다. 이것이 언제 필요할까?

대표적으로 처음 단 한 번, 외부 API를 통해 리소스를 받아오고 더 이상 API 호출이 필요하지 않을 때 사용할 수 있다.

스크린샷 2022-12-07 17 08 58

스크린샷 2022-12-07 17 10 32

📌 컴포넌트 내에서의 Ajax 요청

목록 내 필터링을 구현하기 위해서는 다음과 같은 두 가지 접근이 있다.

  1. 컴포넌트 내에서 필터링: 전체 목록 데이터를 불러오고, 목록을 검색어로 filter 하는 방법
  2. 컴포넌트 외부에서 필터링: 컴포넌트 외부로 API 요청을 할 때, 필터링 한 결과를 받아오는 방법 (보통, 서버에 매번 검색어와 함께 요청하는 경우가 이에 해당)

각각의 장단점은 무엇일까? 지금은, 우리가 storageUtil.js를 이용해 외부 API를 직접 구현했지만(LocalStorage API를 이용), 이는 서버 요청으로 대체할 수 있다. 만일 서버에서 수십만 개의 명언을 제공한다고 가정해 보자. 다음의 표는 HTTP를 이용한 서버 요청을 가정할 때, 두 방식의 차이점을 설명한다.

스크린샷 2022-12-07 14 03 33

Ajax요청을 보내보자
useEffect(() => {
  fetch(`http://서버주소/proverbs?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setProverbs(result);
    });
}, [filter]);
Ajax 요청이 매우 느릴 경우?

모든 네트워크 요청이 항상 즉각적인 응답을 가져다주는 것은 아니다. 외부 API 접속이 느릴 경우를 고려하여 로딩페이지의 구현은 필수적이다.

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

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

fetch 요청 전후로 setIsLoading을 설정해주어 보다 나은 UX를 구현할 수 있다.

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

스크린샷 2022-12-07 17 10 47

Comments