상세 컨텐츠

본문 제목

React(리액트) 티키타카 23 (useDebounce Hook 1)

React

by e7e 2025. 3. 11. 09:10

본문

React에서 의외로 꽤나 필요성이 높은 훅 useDebounce당.

프론트 프레임워크에선 ajax(api 요청)를 많이 쓰다보니,

실시간으로 ajax요청을 남발하면  서버에 부하가 옴팡지게 많이 걸려서,

성능상 문제가 발생하게 된당.

요따구 상황에서 쓰면 좋은 거이 useDebounce당.

 

이상하다, 항상 말이란 건 느끼미가 부족하당. 공감력이 떨어진당.

 

괜찮당. 먼저 debounce가 무엔지 알아보고, 다음 댄스 스텝을 밟아보장.

 

참고로 debounce란 말은 하드웨어서 나왔는데.  스위치가 순간적으로

 on/off를 반복하게 되는 걸 bouncing이라 하고 (오 머리 아프겠당),

그걸 못하게 하는 거시  debounce당.~~ 그렇당~ 필요할 것은 느끼미당.

 

우린 소프트웨어적인 debounce를 누느로 공감해보장.

아래 소스를  복사 붙여넣기로, 먼저 실행 결과를 보고, 소스를 다시 보장.

[style은 괘니 있어 보일라공 넣은 것으니 관심 밖으로 버리장!]

 

아래와 같은 html 소스를  별도로 만들어서 , 별도로 확인하는 게 눈에 까시처럼 

귀찮고, 그냥 React Vite 프로젝트 안에서 확인하고 싶다면 public 폴더에

원하는파일명.html 식으로 파일을 작성한 다음, npm run dev

브라우져 주소 표시줄에  http//localhost:vite개발서버포트번호/원하는파일명.html

http://localhost:5173/debounce.html

식으로도  확인 가능함을 당신도 알아버렸당. (참고 하장.)

 

debounce.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>디바운스 원리 이해</title>
    <style>
      #ov::before {
        content: "____원래값: ";
        color: brown;
      }
      #dv::before {
        content: "디바운스값: ";
        color: blueviolet;
      }

      input:focus {
        border-width: 0px;
        outline: 0;
        font-weight: bolder;
        color: blueviolet;
      }
      input::placeholder {
        color: skyblue;
        font-size: 1.2em;
      }
    </style>
  </head>
  <body>
    <h1>E7E deBounce 원리 체킁</h1>
    <hr />
    <input
      type="text"
      value=""
      autofocus
      onkeyup="fCheck(event)"
      placeholder="여기에 써줄꺼징"
    />
    <p id="ov" style="border: 1px solid black"></p>
    <p id="dv" style="border: 1px solid black"></p>
    <script>
      const ov = document.querySelector("#ov");
      const dv = document.querySelector("#dv");
      function fCheck(e) {
        ov.innerHTML = e.target.value;
        useDebounce(e.target.value, 500); // 호출
      }

      // 사실 별게 없어용!~~
      function useDebounce(text, time) {
        if (window.timerId) clearTimeout(timerId);
        window.timerId = setTimeout(() => {
          dv.innerHTML = text;
        }, time);
      }
    </script>
  </body>
</html>

위 소스에서 window.timerId전역변수 var timerId를 선언한 것과 같은 으미당.

괘니 있어 보일라쿵 변칙 댄스를 추어보았당.  

script부분만 보면 길지 않은 소스이니, 잠시 시간 내서 본다면, 이해를 못할 이유가 없당.

 

 

 

실행해 보면 보면 

느끼미란 놈이 찾아온다.

 

단순 Delay 구만!

그건 아이당. 오해당

(소스 탐구)

 

 

 

 

 

 

사용자가 입력을  연속으로 하고 있을 때는 타이머가 clear되어 입력이 안되다가

사용자 입력이 잠시 멈추면 그때  입력이 된다.

여기선 0.5초이상의 멈춤이 있어야 한당.

 

그럼 이제 React에서 useDebounce를 사용하는 실례를 해보장.

 

vite 프로젝트를 생성했다고 치공, 아래 모듈을 먼저 설치하장.

[useDebounce를 쓰고 싶다면 use-debounce 설치해야 한당.♥]

npx create-vite@latest .
npm i use-debounce

 

사전 필요한 작업을 했다 치공. (이제 시작시 정리해야 할 파일들을 알꼬당!)

아래와 같이 App.jsx를 수정하공, npm run dev로 개발 서버를 구동하장.

 

App.jsx

import { useState } from "react";
import { useDebounce } from "use-debounce";

function App() {
  const [text, setText] = useState("");
  const [deVal] = useDebounce(text, 500);

  const e7eChg = (e) => {
    setText(e.target.value);
  };
  return (
    <>
      <h1>띠이작</h1>
      <input type="text" value={text} onChange={e7eChg} />
      <p> text 변화 {text}</p>
      <p>deVal 변화 {deVal}</p>
    </>
  );
}

export default App;

당근 이전에 만들었던 debounce.html과 같은 결과를 얻었을 거당.

하지만 이번엔 너무도 쉽게 얻었당.(거의 거저당.) 아래 한줄이 뽀인또당.

const [deVal] = useDebounce(text, 500);
// 이후 text를 써야 할 자리에 deVal을 쓰면 끝

 

그라믄 언제 쓰면 좋은강?, 언제 써야 하는강?, 왜 쓰는강?, 

 

이제 실제 사용 이해를 위해, 더미데이터와  ajax 페이크 코드가 들어간 아래 코드로

App.jsx를 덮어쓰고, 흐름이 간단하니 눈으로 후욱 판단하장.

참으로 많이 사용되는 형태의 코드당.

 

App.jsx

import { useEffect, useRef, useState } from "react";

// 더미 데이터
const dummyData = [
  "하오서연",
  "하오승권",
  "하오혜정",
  "하우범준",
  "하우상우",
  "하우지용",
  "하아원빈",
  "하아예림",
  "하아유림",
  "하어동준",
];

// 아작스 페이크
function nameSch(schWord) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(dummyData.filter((name) => name.includes(schWord)));
    }, 10);
  });
}

function App() {
  const [schText, setSchText] = useState("");
  const [schNames, setSchNames] = useState([]);
  const cntRef = useRef(0);

  console.log("re-rendering :",cntRef.current++);

  const e7eChg = (e) => {
    setSchText(e.target.value);
  };

  useEffect(() => {
    const getNames = async () => {
      const newNames = await nameSch(schText);
      setSchNames(newNames);
    };
    getNames();
  }, [schText]);

  return (
    <>
      <h1>껌색</h1>
      <input type="text"  value={schText} onChange={e7eChg} />
      <ul>
        {schNames.length ? (
          schNames.map((name, i) => <li className="list-decimal"  key={i}>{name}님</li>)
        ) : (
          <h1>없엉</h1>
        )}
      </ul>
    </>
  );
}

export default App;

실행 후 F12를 누른 다음 input text에 글자를 쓸 때마다 검색이 되면서 

console.log("re-rendering :",cntRef.current++);

라인 덕분에 re-rendering 글자가 아주 마니 찍힐 거당.

페이크 코드가 몇번 불리는가를 보는 것이 더 정확한 이야기가 되겠지만

그냥 재미를 추구하는 내 맘이당.~~

 

위 버젼을 useDebounce 훅을 넣어서 쪼메만 고쳐보장.

///// 추가 ,  //// 수정 ,  ////  수정  있는 라인에 주목하고 주목하장.

 

App.jsx

import { useEffect, useRef, useState } from "react";
import { useDebounce } from "use-debounce";

// 더미 데이터
const dummyData = [
  "하오서연",
  "하오승권",
  "하오혜정",
  "하우범준",
  "하우상우",
  "하우지용",
  "하아원빈",
  "하아예림",
  "하아유림",
  "하어동준",
];

// 아작스 페이크
function nameSch(schWord) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(dummyData.filter((name) => name.includes(schWord)));
    }, 10);
  });
}

function App() {
  const [schText, setSchText] = useState("");
  const [schNames, setSchNames] = useState([]);
  const [deText] = useDebounce(schText,500);             ////// 추가
  const cntRef = useRef(0);

  console.log("re-rendering :",cntRef.current++);

  const e7eChg = (e) => {
    setSchText(e.target.value);
  };

  useEffect(() => {
    const getNames = async () => {
      const newNames = await nameSch(deText);           ///// 수정
      setSchNames(newNames);
    };
    getNames();
  }, [deText]);                                        //// 수정

  return (
    <>
      <h1>껌색</h1>
      <input type="text"  value={schText} onChange={e7eChg} />
      <ul>
        {schNames.length ? (
          schNames.map((name, i) => <li className="list-decimal"  key={i}>{name}님</li>)
        ) : (
          <h1>없엉</h1>
        )}
      </ul>
    </>
  );
}

export default App;

이전과 달리 사용자가 입력을 잠시 멈추었을 때만 ajax 검색이 이루어져

console.log("re-rendering :",cntRef.current++);

에 의해 찍히는 로그 횟수가 엄청나게 줄었음을 알 수 있당.

이제 당신은  그 가치를 그냥 무기력하게 느껴버린당. 언제, 왜 쓰는지?  알고야 말았당.

아래는 참고용 나의 느끼미 결과당.~~

짧지만 으미가 있는 여정이었당. 여기서 끝? 아직 맘이 정리가 안되었당.

두고 보장!~~ ㅋㅋ

 


 그리움이 두통으로 찾아 왔다.

난 버즈가 되어 노래를 불러준다.

 

제발 가라고...아주 가라고...

타이레놀이 그리움 까시와 싸우고 있다.

예의바른 그리움이라 거절하기도 어렵당.

 

금수저가 열 받을 땐 그늘이 

금수저가 추울땐  핫팩이 되고팠다.

그게 까시의 씨아시 될 줄이야

 

까시야 날 버려..., 제발.....

아니 내가 까시를 잘라 버릴까...

 

내가 좋아하는 스타일이다.

슬픈 가사를 신나게.. 그리고 즐겁게...

 

그리움의 까시가 찌른 곳을 찌르고

또 찌르면.. 찌르르  찌르르

이거시 바로 그 고통의 끝에 꼭꼭 숨겨둔다던...

 

린의 간드러지게 살짜기 달리는

그 보이는 음색이 그 만큼 야속하더~라!!~~

 

 

https://www.youtube.com/watch?v=nkzwOp0kWxo

 

관련글 더보기