๐Ÿ“…2024. 03. 29 75์ผ์ฐจ


React

useEffect ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ํ™œ์šฉํ•ด์„œ, ์‹คํ–‰๋ฏผ๊ฐ๋„ ์กฐ์ ˆ

import React, { useState, useEffect } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";
let AppCallCount = 0;
let SubCallCount = 0;

const Sub = ({appNo}) => {
	SubCallCount++;
	console.log(`Sub ${SubCallCount}๋ฒˆ ์‹คํ–‰๋จ`);

	const [no, setNo] = useState(0);
	const [no2, setNo2] = useState(0);
	
	useEffect(() => {
		console.log('effect 1 : ์ตœ์ดˆ์— ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰');
	},[]); // props : ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ๊ฐ’์„ ์ „๋‹ฌํ• ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ
	
	useEffect(() => {
		console.log('effect 2 : ๋ถ€๋ชจ์˜ appNo๊ฐ€ ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ์‹คํ–‰');
	},[appNo]);
	
	useEffect(() => {
		console.log('effect 3 : ๋‚˜(Sub)์˜ no๊ฐ€ ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ์‹คํ–‰');
	},[no]);
	
	useEffect(() => {
		console.log('effect 4 : appNo ํ˜น์€ no๊ฐ€ ๋ฐ”๋€”๋•Œ๋งˆ๋‹ค ์‹คํ–‰');
	},[appNo, no]);
	
	useEffect(() => {
		console.log('effect 5 : ๋งค๋ฒˆ ์‹คํ–‰');
	});

	return (
		<>
			<button onClick={() => setNo(no + 1)}>Sub ๋ฒ„ํŠผ(no) : {no}</button>
			<button onClick={() => setNo2(no2 + 1)}>Sub ๋ฒ„ํŠผ(no2) : {no2}</button>
		</>
	);
};

const App = () => {
	AppCallCount++;
	console.log(`App ${AppCallCount}๋ฒˆ ์‹คํ–‰๋จ`);

	const [no, setNo] = useState(0);

	return (
		<>
			<div style={{ border: "5px solid red", padding: 10 }}>
				App no : {no}
				
				<Sub appNo={no}/>
				<hr />
				<button onClick={() => setNo(no + 1)}>App ๋ฒ„ํŠผ : {no}</button>
			</div>
		</>
	);
};
  • useEffect ์‹คํ–‰ ํ™•์ธ

์ž…๋ ฅ๊ฐ’ ์†Œ์ˆ˜ ํŒ๋ณ„

//์ƒ๋žต
// ์†Œ์ˆ˜ ํŒ๋ณ„๊ธฐ(๋„˜๊ฒจ์ง„ ๊ฐ’์ด ์†Œ์ˆ˜์ธ์ง€ ์•„๋‹Œ์ง€)
function isPrimeNumber(num) {
  if (num <= 1) return false;
  for (let i = 2; i < num; i++) {
    if (num % i == 0) {
      return false;
    }
  }
  return true;
}
  • ์†Œ์ˆ˜ ํŒ๋ณ„ ํ•จ์ˆ˜ : 1์ดํ•˜ ๊ฑฐ์ง“ ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ๋‚˜๋จธ์ง€ 0 ๋˜๋Š”๊ฑฐ ๊ฑฐ์ง“์œผ๋กœ ์†Œ์ˆ˜ ์ฐพ๊ธฐ
// 1๋ถ€ํ„ฐ n๊นŒ์ง€ ์ˆ˜ ์ค‘์—์„œ ์†Œ์ˆ˜๋งŒ ๋ฐฐ์—ด๋‹ด์•„์„œ ๋ฆฌํ„ด
function getPrimeNumbers(maxValue) {
  const primeNumbers = [];

  for (let i = 1; i <= maxValue; i++) {
    if (isPrimeNumber(i)) {
      primeNumbers.push(i);
    }
  }

  return primeNumbers;
}
  • 1๋ถ€ํ„ฐ n๊นŒ์ง€ ์ˆ˜ ์ค‘์—์„œ ์†Œ์ˆ˜๋งŒ ๋ฐฐ์—ด์— ๋‹ด์•„์„œ ๋ฆฌํ„ด์‹œ์ผฐ๋‹ค.
function getPrimeNumbersCount(maxValue) {
  return getPrimeNumbers(maxValue).length;
}
  • ์ž…๋ ฅํ•œ ๊ฐ’์˜ ์†Œ์ˆ˜ ๊ฐฏ์ˆ˜ ์นด์šดํŒ… ํ•จ์ˆ˜
const App = () => {
	const [inputedNo,setInputedNo] = useState(0);
	const primeNumberCount = getPrimeNumbersCount(inputedNo);
	
	const onSubmit = (e) => {
		e.preventDefault();
		
		const form = e.target;
		
		form.number.value = form.number.value.trim();
		
		if(form.number.value.length == 0){
			alert('์ˆซ์ž ์จ');
			form.number.focus();
			return;
		}
		
		const number = form.number.valueAsNumber;
		form.number.focus();
		
		setInputedNo(number);
		
		// alert(`๋„ค๊ฐ€ ์ž…๋ ฅํ•œ ์ˆซ์ž๋Š” ${number} (์ด)๋‹ค.`);
	}
	
	return (
		<>
			<form onSubmit={onSubmit}>
				<input type="number" name="number" placeholder="์ˆซ์ž ์ž…๋ ฅํ•ด" defaultValue="0"/>
                  
				<button type="submit">ํ™•์ธ</button>
				<div>MAX : {inputedNo}</div>
				<hr/>
				<div>์†Œ์ˆ˜์˜ ๊ฐฏ์ˆ˜ : {primeNumberCount}</div>
				<hr/>
			</form>
		</>
	);
};
//์ƒ๋žต
  • ์ธํ’‹์ฐฝ์— ์ˆซ์ž ์ž…๋ ฅ ๋„˜๊ฒจ์ฃผ๊ณ  UI์— ์ž…๋ ฅํ•œ ์ˆซ์ž์™€, ์†Œ์ˆ˜์˜ ๊ฐฏ์ˆ˜ ๋‚˜์˜ค๊ฒŒ~~

์†Œ์ˆ˜ ํŒ๋ณ„์— useEffect ์‚ฌ์šฉ Var.

import React, { useState, useEffect } from "https://cdn.skypack.dev/react@18";

// inputedNo ๊ฐ’์ด ๋ณ€๊ฒฝ ๋˜์—ˆ์„ ๋•Œ๋งŒ ์žฌ๋žœ๋”๋ง 
	useEffect(() => {
		const primeNumberCount  = getPrimeNumbersCount(inputedNo);
		setPrimeNumberCount(primeNumberCount);
	},[inputedNo]);
	
  • primeNumberCount๋Š” ๊ณ„์† ์žฌ ๋žœ๋”๋ง ๋  ํ•„์š”๊ฐ€ ์—†๋‹ค. input๊ฐ’์ด ๋“ค์–ด์˜ฌ ๋•Œ๋งŒ ์žฌ ๋žœ๋”๋ง ํ•˜๋ฉด ๋œ๋‹ค.
  • useEffect ์จ์„œ ์ฆ๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ primeNumberCount๊ฐ€ ์žฌ ๋žœ๋”๋ง ๋˜์ง€ ์•Š๋Š”๋‹ค. ํŠน์ • ๊ฐ’(inputedNo)์ด ์—…๋ฐ์ดํŠธ ๋  ๋•Œ๋งŒ ์žฌ๋žœ๋”๋ง

๋ฆฌ์•กํŠธ(React), 1๋ถ€ํ„ฐ ์ž…๋ ฅ๊ฐ’๊นŒ์ง€์˜ ์†Œ์ˆ˜๊ฐฏ์ˆ˜๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๋กœ์ง ๊ตฌํ˜„์— useEffect ์ ์šฉ


์†Œ์ˆ˜ ํŒ๋ณ„์— useMemo ์‚ฌ์šฉ Var.

import React, { useState, useMemo } from "https://cdn.skypack.dev/react@18";

const primeNumberCount = useMemo(() => getPrimeNumbersCount(inputedNo), [inputedNo]);
  • useMemo ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์–ต

๋ฆฌ์•กํŠธ(React), 1๋ถ€ํ„ฐ ์ž…๋ ฅ๊ฐ’๊นŒ์ง€์˜ ์†Œ์ˆ˜๊ฐฏ์ˆ˜๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๋กœ์ง ๊ตฌํ˜„์— useMemo์ ์šฉ


useCallback ํ™œ์šฉ

import React, { useState, useCallback } from "https://cdn.skypack.dev/react@18";
import ReactDOM from "https://cdn.skypack.dev/react-dom@18";

let SubCallCount = 0;

function Sub({ no1, no2, calculateFunc }) {
	SubCallCount++;
	console.log(`Sub์ด ${SubCallCount}๋ฒˆ ์‹คํ–‰`);

	return (
		<div style={{ border: "10px solid black", padding: 10 }}>
			์ž…๋ ฅ : {no1}, {no2}
			<br/>
			๊ฒฐ๊ณผ : {calculateFunc(no1,no2)}
		</div>
	);
}

const MemoizedSub = React.memo(Sub);
// const calculateFunc = (a, b) => a + b;

let AppCallCount = 0;
const App = () => {
	AppCallCount++;
	console.log(`App์ด ${AppCallCount}๋ฒˆ ์‹คํ–‰`);

	const [no1, setNo1] = useState(0);
	const [no2, setNo2] = useState(0);
	
	// const calculateFunc = useCallback((a,b) => a + b + no1,[]);
	// ์œ„์™€ ๊ฐ™์€ ๊ฒฝ์šฐ์—” ์ตœ์ดˆ์— ํ•œ๋ฒˆ๋งŒ no1์ด ๋ฐ”๋€Œ์–ด๋„ ๋ฐ˜์˜ ๋˜์ง€ ์•Š์Œ, ์ด์ „ ๋ฒ„์ „์˜ ํ•จ์ˆ˜๋ฅผ ๊ธฐ์–ต ์ค‘
	
	const calculateFunc = useCallback((a,b) => a + b + no1,[no1]);
	// no1์˜ ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์„œ ๋ฆฌํ„ด
	
	return (
		<>
			<button onClick={() => setNo1(no1 + 1)}> ๋ฒˆํ˜ธ1 : {no1}</button>
			<hr />
			<button onClick={() => setNo2(no2 + 1)}> ๋ฒˆํ˜ธ2 : {no2}</button>
			<hr />
			<MemoizedSub no1={10} no2={20} calculateFunc={calculateFunc} />
		</>
	);
};

์˜ค๋Š˜์˜ ๊ฐœ๋…

  • props : ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ๊ฐ’์„ ์ „๋‹ฌํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์†์„ฑ

useMemo

  • useMemo๋Š” ๋ฆฌ์•กํŠธ์—์„œ ์ปดํฌ๋„ŒํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™” ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ›…์ด๋‹ค.

useMemo์—์„œ memo๋Š” memoization์„ ๋œปํ•˜๋Š”๋ฐ ์ด๋Š” ๊ทธ๋Œ€๋กœ ํ•ด์„ํ•˜๋ฉด โ€˜๋ฉ”๋ชจ๋ฆฌ์— ๋„ฃ๊ธฐโ€™๋ผ๋Š” ์˜๋ฏธ์ด๋ฉฐ ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋žจ์ด ๋™์ผํ•œ ๊ณ„์‚ฐ์„ ๋ฐ˜๋ณตํ•ด์•ผ ํ•  ๋•Œ, ์ด์ „์— ๊ณ„์‚ฐํ•œ ๊ฐ’์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•จ์œผ๋กœ์จ ๋™์ผํ•œ ๊ณ„์‚ฐ์˜ ๋ฐ˜๋ณต ์ˆ˜ํ–‰์„ ์ œ๊ฑฐํ•˜์—ฌ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค.

์‰ฝ๊ฒŒ ๋งํ•ด ๋™์ผํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœํ•ด์•ผํ•œ๋‹ค๋ฉด ์ฒ˜์Œ ๊ฐ’์„ ๊ณ„์‚ฐํ•  ๋•Œ ํ•ด๋‹น ๊ฐ’์„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ด ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ๊บผ๋‚ด์„œ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋ฆฌ์•กํŠธ์—์„œ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๋Š” ๋ Œ๋”๋ง => ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ํ˜ธ์ถœ => ๋ชจ๋“  ๋‚ด๋ถ€ ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”์˜ ์ˆœ์„œ๋ฅผ ๊ฑฐ์นœ๋‹ค.

์ปดํฌ๋„ŒํŠธ ์ตœ์ƒ๋‹จ์—์„œ useMemo๋ฅผ ํ˜ธ์ถœ

Memoization์ด๋ž€?

ํ•œ ๋ฒˆ ์—ฐ์‚ฐํ•ด๋ณธ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์–ตํ•ด๋‘๊ณ , ๋‹ค์‹œ ๋™์ผํ•œ ์ž…๋ ฅ์ด ๋“ค์–ด์˜ค๋ฉด ๊ธฐ์–ตํ•ด๋‘” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

1+1 ์€ ๋ฌด์—‡์ธ๊ฐ€?
(๊ณ„์‚ฐ์ค‘...)
(๊ณ„์‚ฐ์™„๋ฃŒ) => 2
๋‹ค์‹œ ํ•œ๋ฒˆ๋” 1+1์€ ๋ฌด์—‡์ธ๊ฐ€?
(์ „์ด๋ž‘ ๋™์ผํ•˜๋„ค? ๊ธฐ์–ตํ•ด๋’€๋˜๊ฑฐ ๋ฐ”๋กœ ๋ฐ˜ํ™˜) 2
์œ„ ๋ฐฉ์‹์ฒ˜๋Ÿผ ์—„์ฒญ ๋ณต์žกํ•˜๊ฑฐ๋‚˜ ์„ฑ๋Šฅ์„ ์žก์•„๋จน๋Š” ํ•จ์ˆ˜๋ผ๋ฉด ์ด๋Ÿฌํ•œ ์„ฑ๋Šฅ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

์ด ์ฝ”๋“œ๋Š” ์ƒ์„ฑ(create) ํ•จ์ˆ˜์ธ computeExpensiveValue(a, b) ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
useMemo๋Š” ์˜์กด์„ฑ์ธ [a, b]๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ์—๋งŒ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’๋งŒ ๋‹ค์‹œ ๊ณ„์‚ฐํ•œ๋‹ค.
์ฆ‰, [a, b]๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด ์ด์ „๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ์ด๋Ÿฌํ•œ ์ตœ์ ํ™”๋Š” ๋ชจ๋“  ๋ Œ๋”๋ง ์‹œ์˜ ๊ณ ๋น„์šฉ ๊ณ„์‚ฐ์„ ๋ฐฉ์ง€ํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

a์™€ b์˜ ์ƒํƒœ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋Š” ์žฌ๋ Œ๋”๋ง ๋œ๋‹ค.
๊ทธ ๋•Œ ์ „๋ถ€ ๋ Œ๋”๋ง์ด ๋˜๊ธฐ์— ๊ณ„์‚ฐ๋„ ๋‹ค์‹œํ•˜์—ฌ ๋ Œ๋”๋ง๋œ๋‹ค.
ํ•˜์ง€๋งŒ a์™€ b๊ฐ€ ์ด์ „ ๊ฐ’๊ณผ ๋™์ผํ•˜๋‹ค๋ฉด ๊ตณ์ด ์—ฐ์‚ฐํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.
๊ทธ๋ž˜์„œ ์ด useMemo๋ฅผ ์“ฐ๋Š” ๊ฒƒ์ด๋‹ค.

๋งŒ์•ฝ useMemo์˜ ๋‘๋ฒˆ์งธ ์ธ์ž์ธ ์˜์กด์„ฑ ๊ฐ’์— ๋นˆ ๋ฐฐ์—ด์ด ๋“ค์–ด๊ฐ„๋‹ค๋ฉด ๋งค ๋ Œ๋”๋ง๋•Œ๋งˆ๋‹ค ๊ณ„์‚ฐ์„ ํ•˜๊ฒŒ ๋œ๋‹ค.
์ด๋Ÿด ๊ฒฝ์šฐ useEffect์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ useEffect์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ(side effects)๋Š” useEffect์—์„œ ํ•˜๋Š” ์ผ์ด์ง€ useMemo์—์„œ ํ•˜๋Š” ์ผ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

useCallback์ด๋ž€?

  • UseCallback ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜, ์ฆ‰ ์ด๋ฏธ ์ƒ์„ฑ๋œ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฆฌ์•กํŠธ ํ›…์ด๋‹ค.

useMemo ๋Š” ํŠน์ • ๊ฒฐ๊ณผ๊ฐ’์„ ์žฌ์‚ฌ์šฉ ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ˜๋ฉด, useCallback ์€ ํŠน์ • ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • useCallback ์‚ฌ์šฉ๋ฐฉ๋ฒ•
const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])

useCallback ์˜์กด์„ฑ ๋ฐฐ์—ด์— ์žˆ๋Š” ์ƒํƒœ๋‚˜ props๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ๋‹ค์‹œ ์ƒ์„ฑ๋˜์ง€ ์•Š๋Š”๋‹ค.

์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ ๋„˜๊ธด ํ•จ์ˆ˜๋ฅผ ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ ๋„˜๊ธด ์˜์กด์„ฑ ๋ฐฐ์—ด๋‚ด์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๊ธฐ ์ „๊นŒ์ง€ ์ €์žฅํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€