[JS] 디바운스 어-썸하게 쓰기!

young_pallete·2021년 8월 31일
0

JavaScript

목록 보기
5/5

시작하며 🌈

2시간을 삽질~하다가 결국 원리를 어느정도 이해할 수 있었네요.
사실 아시는 분들은 그냥 지나치셔도 무방하지만, 저같은 HTML 개발자에게는 꽤나 신선했던 충격이었어요.

쉽게 말하자면, 다음과 같은 분들께서 이 글을 보신다면 좋을 것 같습니다!

  • 나는 전역변수로 timer을 설정하지 않으면 작동하지 않아서 해야 했다!
  • 나는 지금 디바운스에 대한 원리를 이해하지 못하겠다!

네, 저도 이중 하나에 속했읍니다...😅😅
일단 그렇다면, 같이 알아가볼까요?! 🖐

본론 📖

일단 저는 원래 디바운스를 다음과 같이 썼답니다.
말 그대로 전역변수로 timer을 넣었죠!

let timer;
export default function debounce(callback, delay) {
  return (...args) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      console.log(args);
      callback(...args);
    }, delay);
  };
}

결과적으로 이렇게 해도 잘 동작합니다!
그렇다면 문제가 무엇일까요?

저같은 경우는 디바운스가 여러 군데에서 호출이 동시에 일어날 때, 결국 마지막 하나만 동작하는 효과를 얻었습니다. 마치 리덕스의 takeLatest같이 말이죠!

그래서 만약 여러 개를 동작시킬 수 있으려면, timer을 이렇게 클로저로 관리를 해야 했어요!

export default function debounce(callback, delay) {
  let timer;
  return (...args) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      console.log(args);
      callback(...args);
    }, delay);
  };
}

하지만... 동작하지 않는 이유는 뭘까... 🤔

이렇게 클로저로 timer을 관리한 후
저는 다음과 같이 debounce를 호출하고 있었어요!

    onEdit: post => {
      debounce(setItem, 2000)(getLocalPostKey(this.state.id), { ...post });
    },

하지만 잘 동작하지가 않았죠.

않이... 말은 debounce라는데... 왜 내껀...😂😂

그래서, 2시간 동안 고민하고 자책하며 원인을 분석한 결과, 이건 클로저 문제야!라고 생각하게 됐어오!

왜 이게 클로저 문제에요?! timer 잘 기억시켜놨잖아요!😨😨

잠깐 설명을 드리고 가죠. 일단, 클로저란 무엇일까요!
함수를 리턴한 함수의 환경을, 리턴된 함수가 기억하고 있는 것을 의미하죠!

그렇다면, 일단 맨 처음 코드를 다시 볼까요?!

    onEdit: post => {
      debounce(setItem, 2000)(getLocalPostKey(this.state.id), { ...post });
    },

여기서 문제는 말이죠! onEdit을 호출할 때 함수가 호출되는 순간에 대한 거였어요!

아니, 호출 잘 되고 있잖아?!

맞아요. 잘 호출되고 있어요.
하지만 중요한 건, 현재 호출되는 부분이 어디냐!에요.

현재는 누가 호출하고 있죠? 함수가 호출하고 있어요.
그런데 이 함수는, 앞으로 onEdit이 호출될 때마다 새롭게 함수가 만들어줄 거에요.

그렇다면? timer라는 것도 호출될 때마다 각자의 함수에서 새롭게 만들어질 거고, 결과적으로 모든 게 다 독립적으로 움직여지는 거죠!
왜냐하면, 같은 블록에서 존재하는 게 아니라, 새로 호출한 함수 내에서 호출되었기 때문이죠!

(참 설명이 어렵네요... 틀린 부분이 있다면 지적해주시면 감사합니다!😂😂)

따라서 우리는 함수에서 호출할 것이 아니라, debounce 내에서 동작할 함수를 넣어주어 debounce를 호출시켜줘야 하는 거에요!

    onEdit: debounce(
      post => setItem(getLocalPostKey(this.state.id), { ...post }),
      2000,
    ),

결과적으로 이렇게 하면, onEdit으로 호출할 때마다 이제 debounce라는 함수가 호출되는 곳에서 동작하기 때문에 클로저가 참조하는 렉시컬 환경이 같아져, 같은 timer을 공유하게 됩니다.

결과를 살펴볼까요?!

어썸하군요! 🌈🎉

마치며 👏

결국에는 디바운스도 이번 프로젝트를 통해 더욱 잘 알 수 있게 됐어요!
역시 사람은 무언가 부딪혀보고, 좌절을 느낄 때 더욱 성장하는 듯 합니다.
그렇다면, 더 좋은 글로 뵐게요. 이상!

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉

0개의 댓글