나홀로 리액트 스터디 0.2

Jade·2023년 12월 11일
0

React

목록 보기
2/7
post-thumbnail

🍐 보고 또 보기 - 클로저

'함수와 함수가 선언된 어휘적 환경(렉시컬 스코프)의 조합'
선언된 어휘적 환경은 변수가 코드 내부 어디에서 선언되었는가를 말하는 것이라고 생각하면 된다.
(*클로저가 생성될 때마다 선언적 환경이 기억된다는 것은 생성될 때마다 비용이 발생하는 것이라고도 할 수 있음.
클로저 사용 시 적절한 스코프에 가둬두지 않는다면 성능에 악영향을 미칠 수 있음.)

지금 다니는 회사의 기술 면접을 볼 때 제일 멘탈을 털었던 질문이 '리액트에서 클로저가 사용된 곳은 어디일까요?' 라는 질문이었다.
질문이 어려워서 그랬다기 보다 클로저 개념은 알고 있었으나 그래서 이게 어디서 사용되는지 몰랐다는 사실이 당황스러웠다.
클로저를 제대로 이해하지 못했기 때문이 크겠지...

리액트에서 함수형 컴포넌트와 훅이 등장한 이래로 클로저와 뗄레야 뗄 수 없는 사이가 되었다.
클로저는 함수가 선언된 환경, 함수 레벨의 스코프를 이용해 어떤 작업을 할 수 있다는 개념이다.

전역 스코프는 누구나 접근이 가능하고, 누구나 수정이 가능한 범위이기 때문에
리액트에서는 상태를 저장할 때 별도로 관리하는 클로저 내부에서만 접근할 수 있다.
리액트에서 대표적으로 클로저를 사용하고 있는 것이 useState라는 걸 예상할 수 있다. (그치만 면접볼 당시의 나는 몰랐다 🥲)

function Component () {
  const [state, setState] = useState()
  
  function handleClick() {
    setState((prev) => prev + 1)
  } 
}

위 코드 스니펫에서 useState의 호출은 위에서 끝났지만, handleClick 내에서 사용하고 있는 setState는 최신의 state 값을 기억하고 있다.
useState 내부에서 클로저가 활용되었기 때문인데, useState가 반환한 내부의 함수 setState는 useState 호출이 끝났음에도 자신이 선언된 useState의 내부 환경(ex: state가 저장된 곳)을 기억하기 때문에 state 값을 사용할 수 있게 된다.

클로저는 함수형 프로그래밍의 중요한 개념인 '부수 효과가 없고, 순수해야한다'는 목적을 달성하기 위해 적극적으로 사용된다.


🍐 setTimeout(() => {},0)이 정확하게 0초 뒤에 실행되지 않는 이유

회사에서 받았던 코드 리뷰 중 setTimeout을 이용해 어떤 로직이 두 번 이뤄지는 것을 막는 패턴을 사용할 경우, setTimeout이 즉시 실행됨을 보장하지 않으므로 기기 환경 등에 따라 원하는 대로 동작하지 않을 수 있다는 내용이 있었다.
당시에는 그런가보다... 하고 throttle, debounce에 대한 새로운 지식을 얻은 것에 중점을 두고 넘어갔었다.
리액트 딥 다이브를 읽다보니 호출 스택과 이벤트 루프, 태스크 큐에 의해 이런 현상을 설명한 부분이 있었다.

이벤트 루프는 호출 스택에 실행 중인 코드가 있는지, 그리고 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인하는 역할을 한다.
호출 스택이 비었으면 태스크 큐에서 대기 중인 작업이 있는지 확인하고 있다면 실행 가능한 오랜된 것부터 순차적으로 꺼내와 실행하게 된다. (태스크 큐가 빌 때까지 실행)
setTimeout과 같은 비동기 함수, fetch 기반의 네트워크 요청을 처리하는 건 태스크 큐가 할당되는 별도의 스레드인데, 이 별도의 스레드에서 태스크 큐에 작업을 할당해 처리하는 건 브라우저나 node.js의 역할이다.
자바스크립트 코드의 실행은 싱글 스레드에서 이루어지지만 이런 외부 웹 API 등이 자바스크립트 코드 외부에서 실행되고, setTimeout의 콜백 등이 태스크 큐로 들어간다.
이벤트 루프는 호출 스택이 비었고, 콜백 실행이 가능한 때가 왔을 때 이것을 꺼내서 수행한다.
그러므로 setTimeout 100ms 등으로 입력해둔다고 해도, 원하는 때에 제대로 실행됨을 보장하지는 않게 된다. (최소 지연 시간만을 보장함)

코드 리뷰 대상이었던 복잡한 중첩 이벤트를 제어하기 위해서는 throttle, debounce 함수를 사용할 수 있는데
throttle은 콜백으로 전달된 함수가 반복적으로 호출되는 경우 대기시간마다 한 번씩만 호출되도록 하는데, 사용자가 따라잡을 수 있는 속도보다 빠르게 발생하는 속도 제한 이벤트에 유용하다.
debounce는 콜백이 마지막으로 호출된 이후 대기 시간이 경과할 때까지 실행을 연기하도록 해준다.
대표적으로 실수로 제출 버튼을 두 번 클릭하는 것을 방지해주는 역할을 할 수 있다.

profile
키보드로 그려내는 일

0개의 댓글