Jieunny์˜ ๋ธ”๋กœ๊ทธ

S2) Unit 9. [React] Effect Hook ๋ณธ๋ฌธ

CodeStates/learning contents

S2) Unit 9. [React] Effect Hook

Jieunny 2023. 2. 2. 14:36

๐Ÿ“ฃ  Side Effect(๋ถ€์ˆ˜ ํšจ๊ณผ)

โœ”๏ธ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์–ด๋–ค ๊ตฌํ˜„์ด ํ•จ์ˆ˜ ์™ธ๋ถ€์— ์˜ํ–ฅ์„ ๋ผ์น˜๋Š” ๊ฒฝ์šฐ ํ•ด๋‹น ํ•จ์ˆ˜๋Š” Side Effect๊ฐ€ ์žˆ๋‹ค๊ณ  ๋งํ•œ๋‹ค.

โœ”๏ธ ๋ฆฌ์•กํŠธ์—์„œ๋Š” ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ fetch๋ฅผ ์‚ฌ์šฉํ•ด API ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•ด DOM์„ ์ง์ ‘ ์กฐ์ž‘ํ•  ๋•Œ Side Effect๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๊ณ  ๋งํ•œ๋‹ค.

 

๐Ÿ“ฃ  Pure Function(์ˆœ์ˆ˜ ํ•จ์ˆ˜)

โœ”๏ธ ์˜ค์ง ํ•จ์ˆ˜์˜ ์ž…๋ ฅ๋งŒ์ด ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ์ฃผ๋Š” ํ•จ์ˆ˜

โœ”๏ธ ํ•จ์ˆ˜์˜ ์ž…๋ ฅ์ด ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฐ’์ด ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๊ฒฝ์šฐ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ผ๊ณ  ๋ถ€๋ฅผ ์ˆ˜ ์—†๋‹ค.

โœ”๏ธ ์ž…๋ ฅ์œผ๋กœ ์ „๋‹ฌ๋œ ๊ฐ’์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.

function upper(str) {
  return str.toUpperCase(); // toUpperCase ๋ฉ”์†Œ๋“œ๋Š” ์›๋ณธ์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (Immutable)
}

upper('hello') // 'HELLO'

โžฐ ์ˆœ์ˆ˜ ํ•จ์ˆ˜์—๋Š” ๋„คํŠธ์›Œํฌ ์š”์ฒญ๊ณผ ๊ฐ™์€ Side Effect๊ฐ€ ์—†๋‹ค.

โžฐ ์–ด๋–ค ์ „๋‹ฌ ์ธ์ž๊ฐ€ ์ฃผ์–ด์งˆ ๊ฒฝ์šฐ, ํ•ญ์ƒ ๋˜‘๊ฐ™์€ ๊ฐ’์ด ๋ฆฌํ„ด๋จ์„ ๋ณด์žฅํ•œ๋‹ค -> ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ํ•จ์ˆ˜

 

๐Ÿ“ฃ  React์˜ ํ•จ์ˆ˜ ์ปดํฌ๋„ŒํŠธ

โœ”๏ธ AJAX ์š”์ฒญ(fetch API)์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜, LocalStorage ๋˜๋Š” ํƒ€์ด๋จธ(setTimeout)์™€ ๊ฐ™์€ React์™€ ์ƒ๊ด€์—†๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ฆฌ์•กํŠธ ์ž…์žฅ์—์„œ๋Š” ๋ชจ๋‘  Side Effect ์ด๋‹ค.

โœ”๏ธ ๋ฆฌ์•กํŠธ์—์„œ๋Š” Side Effect๋ฅผ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ Hook์ธ Effect Hook์„ ์ œ๊ณตํ•œ๋‹ค.

 

๐Ÿ“ฃ  Effect Hook

โœ”๏ธ ์ปดํฌ๋„ŒํŠธ๋‚ด์—์„œ Side effect๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” Hook

โœ”๏ธ useEffect(ํ•จ์ˆ˜) : ์ธ์ž๋กœ ๋ฐ›์€ ํ•จ์ˆ˜ ๋‚ด์—์„œ Side effect๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค.

โžฐ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ํ›„ ์ฒ˜์Œ ํ™”๋ฉด์— ๋ Œ๋”๋ง

โžฐ ์ปดํฌ๋„ŒํŠธ์— ์ƒˆ๋กœ์šด props๊ฐ€ ์ „๋‹ฌ๋˜๋ฉฐ ๋ Œ๋”๋ง

โžฐ ์ปดํฌ๋„ŒํŠธ์— ์ƒํƒœ(state)๊ฐ€ ๋ฐ”๋€Œ๋ฉฐ ๋ Œ๋”๋ง

โœ”๏ธ ์œ„์™€ ๊ฐ™์ด ๋งค๋ฒˆ ์ƒˆ๋กญ๊ฒŒ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง ๋  ๋•Œ Effect Hook์ด ์‹คํ–‰๋œ๋‹ค.

 

๐Ÿšจ Hook ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

โžฐ ์ตœ์ƒ์œ„์—์„œ๋งŒ Hook์„ ํ˜ธ์ถœํ•œ๋‹ค.

โžฐ React ํ•จ์ˆ˜ ๋‚ด์—์„œ Hook์„ ํ˜ธ์ถœํ•œ๋‹ค.

// ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ์ƒ์„ฑ๋˜๊ฑฐ๋‚˜, props๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜๊ฑฐ๋‚˜, state๊ฐ€ ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰
useEffect(() => {
	console.log(๋ช‡ ๋ฒˆ ํ˜ธ์ถœ๋ ๊นŒ์š”?)
})

// ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฒ˜์Œ ์ƒ์„ฑ๋  ๋•Œ๋งŒ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰
useEffect(() => {
	console.log(๋ช‡ ๋ฒˆ ํ˜ธ์ถœ๋ ๊นŒ์š”?)
},[])

// dep์ด ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰
useEffect(() => {
	console.log(๋ช‡ ๋ฒˆ ํ˜ธ์ถœ๋ ๊นŒ์š”?)
},[dep])

 

๐Ÿ“ฃ  ์กฐ๊ฑด๋ถ€ Effect ๋ฐœ์ƒ

โœ”๏ธ useEffect์˜ ๋‘๋ฒˆ์งธ ์ธ์ž๋Š” ๋ฐฐ์—ด

โžฐ ๋ฐฐ์—ด์€ ์กฐ๊ฑด์„ ๋‹ด๊ณ  ์žˆ์œผ๋ฉฐ, ์–ด๋–ค ๊ฐ’์˜ ๋ณ€๊ฒฝ์ด ์ผ์–ด๋‚  ๋•Œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

โžฐ ํ•ด๋‹น ๋ฐฐ์—ด์—๋Š” ์–ด๋–ค ๊ฐ’์ด ๋“ค์–ด๊ฐ„๋‹ค -> ์ข…์†์„ฑ ๋ฐฐ์—ด์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

useEffect(ํ•จ์ˆ˜, [์ข…์†์„ฑ1, ์ข…์†์„ฑ2, ...])

โžฐ ๋ฐฐ์—ด ๋‚ด์˜ ์ข…์†์„ฑ1 ๋˜๋Š” ์ข…์†์„ฑ2์˜ ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ, ์ฒซ๋ฒˆ์งธ ์ธ์ž์ธ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

โžฐ ๋ฐฐ์—ด ๋‚ด์˜ ์–ด๋–ค ๊ฐ’์ด ๋ณ€ํ•  ๋•Œ๋งŒ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

โžฐ ๋นˆ ๋ฐฐ์—ด์„ ๋„ฃ์œผ๋ฉด ์ฒ˜์Œ ๋‹จ ํ•œ๋ฒˆ, ์™ธ๋ถ€ API๋ฅผ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ›์•„์˜ค๊ณ  ๋” ์ด์ƒ API ํ˜ธ์ถœ์ด ํ•„์š”ํ•˜์ง€ ์•Š์„ ๋•Œ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ“ฃ  ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ์˜ Ajax ์š”์ฒญ

โœ”๏ธ Data Fetching : ํ•„ํ„ฐ๋ง

1๏ธโƒฃ ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ํ•„ํ„ฐ๋ง : ์ „์ฒด ๋ชฉ๋ก ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ๋ชฉ๋ก์„ ๊ฒ€์ƒ‰์–ด๋กœ filter ํ•˜๋Š” ๋ฐฉ๋ฒ•

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

โžฐ ์ฒ˜์Œ ํ•œ๋ฒˆ๋งŒ ์™ธ๋ถ€ API์—์„œ ๋ช…์–ธ ๋ชฉ๋ก์„ ๋ฐ›์•„์˜ค๊ณ , filter ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•œ๋‹ค.

 

2๏ธโƒฃ ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€์—์„œ ํ•„ํ„ฐ๋ง : ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€๋กœ API ์š”์ฒญ์„ ํ•  ๋•Œ, ํ•„ํ„ฐ๋ง ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋ฐฉ๋ฒ•(์„œ๋ฒ„์— ๋งค๋ฒˆ ๊ฒ€์ƒ‰์–ด์™€ ํ•จ๊ป˜ ์š”์ฒญํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ•ด๋‹น)

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]);	// 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>;
}

โžฐ ๊ฒ€์ƒ‰์–ด๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

๐Ÿ“ฃ  ์žฅ, ๋‹จ์ 

  ์žฅ์  ๋‹จ์ 
์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ HTTP ์š”์ฒญ ๋นˆ๋„ ๊ฐ์†Œ ๋ธŒ๋ผ์šฐ์ €(ํด๋ผ์ด์–ธํŠธ)์˜ ๋ฉ”๋ชจ๋ฆฌ ์ƒ์— ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๊ฒŒ ๋˜๋ฏ€๋กœ, ํด๋ผ์ด์–ธํŠธ์˜ ๋ถ€๋‹ด์ด ๋Š˜์–ด๋‚œ๋‹ค.
์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„ํ„ฐ๋ง ๊ตฌํ˜„ ํ•„์š”X ๋นˆ๋ฒˆํ•œ HTTP ์š”์ฒญ
์„œ๋ฒ„๊ฐ€ ํ•„ํ„ฐ๋ง์„ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ๋ถ€๋‹ด์„ ๊ฐ€์ ธ๊ฐ„๋‹ค.

 

๐Ÿ“ฃ  Ajax ์š”์ฒญ์ด ๋Š๋ฆด ๊ฒฝ์šฐ

โœ”๏ธ ์™ธ๋ถ€ API ์ ‘์†์ด ๋Š๋ฆด ๊ฒฝ์šฐ๋ฅผ ๊ณ ๋ คํ•ด์„œ, ๋กœ๋”ฉ ํ™”๋ฉด์˜ ๊ตฌํ˜„์€ ํ•„์ˆ˜์ ์ด๋‹ค.

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]);

โžฐ fetch ์š”์ฒญ ์ „ํ›„๋กœ setIsLoading์„ ์„ค์ •ํ•ด์ค€๋‹ค.