React.js

[React] ๋ฆฌ์•กํŠธ Hooks (3) - useRef: ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

hyun0907 2024. 6. 26. 23:20

๐Ÿ“Œ useRef๋ž€?

ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ useRef๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ref Object๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ref Object์˜ ํ˜•ํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{ current: value }
  • ์ธ์ž๋กœ ๋„ฃ์–ด์ค€ ์ดˆ๊ธฐ๊ฐ’์ด ์ €์žฅ์ด ๋ฉ๋‹ˆ๋‹ค.
  • ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์–ธ์ œ๋“  ์›ํ•˜๋Š” ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง์ด ๋˜์–ด๋„ ์–ธ๋งˆ์šดํŠธ ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๊ฐ’์„ ๊ณ„์† ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ์–ธ์ œ useRef๋ฅผ ์‚ฌ์šฉํ•˜๋‚˜์š”?

1. ref๋Š” state์™€ ๋น„์Šทํ•˜๊ฒŒ ๊ฐ’์„ ์ €์žฅํ•ด๋‘๋Š” ์ €์žฅ ๊ณต๊ฐ„์œผ๋กœ ์‚ฌ์šฉ์ด ๋ฉ๋‹ˆ๋‹ค.

state๊ฐ€ ๋ณ€๊ฒฝ์ด ๋˜๋ฉด ๋ฆฌ๋ Œ๋”๋ง์ด ๋˜๊ณ  ํ•จ์ˆ˜๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ๋ณ€์ˆ˜๋“ค์„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์›ํ•˜์ง€ ์•Š๋Š” ๋ Œ๋”๋ง ๋•Œ๋ฌธ์— ๊ณค๋ž€ํ•ด์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.

ref๊ฐ€ ๋ณ€ํ™”ํ•ด๋„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ณ€์ˆ˜๋“ค์˜ ๊ฐ’์ด ์œ ์ง€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ state์˜ ๋ณ€ํ™”๋กœ ์ธํ•ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง์ด ๋˜์–ด๋„ ref์˜ ๊ฐ’์€ ์œ ์ง€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

2. ref๋ฅผ ํ†ตํ•ด DOM ์š”์†Œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ input ์š”์†Œ๋ฅผ ํด๋ฆญํ•˜์ง€ ์•Š์•„๋„ focus๋ฅผ ์ฃผ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ํ™”๋ฉด์ด ๋ณด์—ฌ์กŒ์„ ๋•Œ, ์ž๋™์ ์œผ๋กœ ํฌ์ปค์Šค๊ฐ€ ๋˜๊ฒŒ๋” ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ธฐ๋Šฅ์€ javascript์˜ Document.querySelector()์™€ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ํ•ฉ๋‹ˆ๋‹ค.

 

๐Ÿ“š ref.current

๋จผ์ € ํ˜„์žฌ state(count) ๊ฐ’์„ ๋ณด์—ฌ์ฃผ๋ฉด์„œ ๋ฒ„ํŠผ์„ ํด๋ฆญ์‹œ setState(setCount)๋ฅผ ์ด์šฉํ•ด state ๊ฐ’์„ 1์”ฉ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

const App = () => {
  const [count, setCount] = useState(0);

  const increaseCountState = () => {
    setCount(count + 1);
  };
  return (
    <div>
      <h1>State: {count}</h1>
      <button onClick={() => increaseCountState()}>State +1</button>
    </div>
  );
};

export default App;

 

๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ state ๊ฐ’์ด ๋ณ€ํ™”ํ•˜๋Š” ๊ฒƒ์„ ๋ฐ”๋กœ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ณ„์† ๋ Œ๋”๋ง์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

์ด์ œ useRef๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  const countRef = useRef(0);
  console.log(countRef);

 

ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ App ์ปดํฌ๋„ŒํŠธ ์•ˆ์— ๋„ฃ์–ด ์ฝ˜์†”์„ ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

countRef๋Š” current๋ผ๋Š” ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ current ์•ˆ์—์„œ๋Š” ์ฒ˜์Œ์— ์„ค์ •ํ•ด๋‘” 0์ด๋ผ๋Š” ์ดˆ๊ธฐ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

countRef.current๋กœ ์ด ๊ฐ’์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. state๋ฅผ ํ™œ์šฉํ•œ ๋ฐฉ์‹ ๊ทธ๋Œ€๋กœ ํ•˜๋‹จ์— ref ๊ฐ’์„ 1์”ฉ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฒ„ํŠผ์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

import React, { useEffect, useState, useRef } from "react";
import "./App.css";

const App = () => {
  const [count, setCount] = useState(0);
  const countRef = useRef(0);

  const increaseCountState = () => {
    setCount(count + 1);
    console.log("State: ", count + 1);
  };

  const increaseCountRef = () => {
    countRef.current += 1;
    console.log("Ref: ", countRef.current);
  };

  return (
    <div>
      <h1>State: {count}</h1>
      <h1>Ref: {countRef.current}</h1>
      <button onClick={() => increaseCountState()}>State +1 </button> // ๋ Œ๋”๋ง์ด ๋ฉ๋‹ˆ๋‹ค.
      <button onClick={() => increaseCountRef()}>Ref +1</button> // ๋ Œ๋”๋ง์ด ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
    </div>
  );
};

export default App;

 

Ref + 1 ๋ฒ„ํŠผ์„ ์•„๋ฌด๋ฆฌ ๋ˆŒ๋Ÿฌ๋„ ํ™”๋ฉด์— ์ˆซ์ž๊ฐ€ ๋Š˜์–ด๋‚˜์ง€ ์•Š์ง€๋งŒ, State + 1 ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ™”๋ฉด์—์„œ ์ˆซ์ž๊ฐ€ ref์˜ current ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. state๊ฐ€ ๋ณ€๊ฒฝ์ด ๋˜์—ˆ์„ ๋•Œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ์ „์ฒด ํ™”๋ฉด์ด ๋‹ค์‹œ ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค.

 

๋งค์šฐ ์ž์ฃผ ๋ฐ”๋€Œ๋Š” ๊ฐ’์„ state๋กœ ๋ณด๊ด€ํ•œ๋‹ค๋ฉด ์ด ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ Œ๋”๋ง์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ref์— ๋ณด๊ด€ํ•œ๋‹ค๋ฉด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๋ Œ๋”๋ง์ด ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ข‹์€ ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

๐Ÿ“š ref VS var

์ด๋ฒˆ์—๋Š” ref์™€ var๋ฅผ ์˜ฌ๋ ค์ฃผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

const App = () => {
  const countRef = useRef(0);
  let countVar = 0;

  const increaseRef = () => {
    countRef.current += 1;
    console.log("ref: ", countRef.current);
  };

  const increaseVar = () => {
    countVar += 1;
    console.log("var: ", countVar);
  };

  return (
    <div>
      <h1>Ref: {countRef.current}</h1>
      <h1>Var: {countVar}</h1>
      <button onClick={increaseRef}>Ref +</button>
      <button onClick={increaseVar}>Var +</button>
    </div>
  );
};
export default App;

 

 

์ด๋ ‡๊ฒŒ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด๋„ ref์™€ ๋ณ€์ˆ˜ ๋ชจ๋‘ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ™”๋ฉด์—์„œ ๋ณผ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ Œ๋”๋ง์„ ๋‹ด๋‹นํ•˜๋Š” ๋ฒ„ํŠผ์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

const App = () => {
  const [renderer, setRenderer] = useState(0);
  const countRef = useRef(0);
  let countVar = 0;

  const doRendering = () => {
    setRenderer(renderer + 1);
  };

  const increaseRef = () => {
    countRef.current += 1;
    console.log("ref: ", countRef.current);
  };

  const increaseVar = () => {
    countVar += 1;
    console.log("var: ", countVar);
  };

  return (
    <div>
      <h1>Ref: {countRef.current}</h1>
      <h1>Var: {countVar}</h1>
      <button onClick={setRenderer}>๋ Œ๋”๋ง</button>
      <button onClick={increaseRef}>Ref +</button>
      <button onClick={increaseVar}>Var +</button>
    </div>
  );
};
export default App;

 

ref์™€ var ๊ฐ’์„ ๋Š˜๋ฆฌ๋Š” ๋ฒ„ํŠผ์„ ๊ฐ๊ฐ ๋‘๋ฒˆ์”ฉ ํด๋ฆญํ•˜๊ณ  ๋ Œ๋”๋ง ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด

 

 

Ref ๊ฐ’๋งŒ ๋‹ค์‹œ ๊ทธ๋ ค์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Var์˜ ๊ฐ’์€ 0์œผ๋กœ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.

์ด์œ ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง์ด ๋œ๋‹ค๋Š” ๊ฒƒ = ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœ = ํ•จ์ˆ˜ ๋‚ด๋ถ€์— ์žˆ๋Š” ๋ณ€์ˆ˜๊ฐ€ ์ดˆ๊ธฐํ™”

๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€์ˆ˜๋Š” ๊ณ„์† ์ดˆ๊ธฐํ™”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ref์˜ ๊ฐ’์€ ์ƒ์• ์ฃผ๊ธฐ(๋งˆ์šดํŠธ ์‹œ์ ๋ถ€ํ„ฐ ์–ธ๋งˆ์šดํŠธ ์‹œ์ ๊นŒ์ง€)๋ฅผ ํ†ตํ•ด ์œ ์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ Œ๋” ์ด์ „์— ๊ฐ€์ง€๊ณ  ์žˆ๋˜ ๊ฐ’์—์„œ ๋ณ€๊ฒฝ์ด ๋ฉ๋‹ˆ๋‹ค. ๋ณ€์ˆ˜๋Š” ๋‹ค์‹œ 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์‹œ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ณ€๊ฒฝ์ด ๋ฉ๋‹ˆ๋‹ค.

 

 

๐Ÿ“š useRef ๋ Œ๋”๋งํ•  ๋•Œ ํ™œ์šฉํ•˜๊ธฐ

๊ทธ๋Ÿผ ์ด์ œ, count ๊ฐ’์„ 1์”ฉ ์ฆ๊ฐ€์‹œ์ผœ์ฃผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ํ•˜๋‹จ์— ๋ Œ๋”๋ง์ด ๋œ ํšŸ์ˆ˜๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

useEffect ์‚ฌ์šฉ?

์ ‘๊ทผ ๋ฐฉ์‹

1. useState๋กœ renderCount๋ผ๋Š” ์ƒˆ๋กœ์šด state๋ฅผ ๋งŒ๋“ค๊ธฐ

2. useEffect๋Š” ๋งค๋ฒˆ ๋ Œ๋”๋ง์ด ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋˜๋‹ˆ ํ•จ๊ป˜ setRenderCount(renderCount +1) ์‹คํ–‰์‹œํ‚ค๊ธฐ

import React, { useEffect, useState, useRef } from "react";
import "./App.css";

const App = () => {
  const [count, setCount] = useState(1);
  const [renderCount, setRenderCount] = useState(1);

  useEffect(() => {
  console.log("๋ Œ๋”๋ง");
  setRenderCount(renderCount + 1);
  });

  return (
    <div>
      <h1>Count: {count}</h1>
      <h1>๋ Œ๋”๋ง: {renderCount}</h1>
      <button onClick={() => setCount(count + 1)}>count +</button>
    </div>
  );
};
export default App;

 

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋Š์ž„์—†์ด ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

์™œ useEffect๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌดํ•œ ๋ฐ˜๋ณต์ด ๋ ๊นŒ?

1. ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด setCount๋กœ ์ธํ•ด count ๊ฐ’์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ Œ๋”๋ง์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— useEffect๋„ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

2. useEffect ๋‚ด๋ถ€์—๋„ renderCount๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, renderCount ๊ฐ’์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

3. state ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์‹œ useEffect๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค ... ๋ฐ˜๋ณต

 

useRef ์ด์šฉํ•˜๊ธฐ

๊ทธ๋ž˜์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด useRef๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ Œ๋”๋ง ์ˆ˜๋ฅผ renderCount.current๋กœ ๋ณด์—ฌ์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

const App = () => {
  const [count, setCount] = useState(1);
  const renderCount = useRef(1);

  useEffect(() => {
    renderCount.current = renderCount.current + 1;
    console.log("๋ Œ๋”๋ง ์ˆ˜: ", renderCount.current);
  });

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>count +</button>
    </div>
  );
};
export default App;

 

์˜ค๋ฅ˜ ์—†์ด ์ฝ˜์†”์—์„œ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, Count๋Š” 1์ธ๋ฐ ๋ Œ๋”๋ง ์ˆ˜๋Š” 2๋ผ๊ณ  ํ‘œ์‹œ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 

์ด์œ ๋Š” ๋จผ์ € renderCount๋ฅผ useRef(1)์„ ํ†ตํ•ด 1๋กœ ์ดˆ๊ธฐํ™” ํ–ˆ๊ณ , ํ™”๋ฉด์„ ์ฒ˜์Œ ๋ Œ๋”๋งํ•˜๋ฉด์„œ renderCount.current + 1์„ ํ•˜๋ฉด์„œ 2๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด์ œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•  ๋•Œ๋งˆ๋‹ค ์ฝ˜์†”์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋ฐ”๋€ ๋ Œ๋”๋ง ์ˆ˜๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ useRef๋Š” ๋ณ€ํ™”๋Š” ๊ฐ์ง€ํ•ด์•ผ ํ•˜์ง€๋งŒ, ๋ Œ๋”๋ง์„ ๋ฐœ์ƒ์‹œํ‚ค๋ฉด ์•ˆ ๋˜๋Š” ๊ฐ’์„ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

์ฐธ๊ณ  ์ž๋ฃŒ

https://www.youtube.com/watch?v=VxqZrL4FLz8&list=PLZ5oZ2KmQEYjwhSxjB_74PoU6pmFzgVMO&index=3