React 에서 볼 수 있는 클로저(Closure)

katanazero·2022년 2월 20일
4

react

목록 보기
13/15

React 에서 볼 수 있는 클로저(Closure)

JS 스터디를 진행하면서, 스터디원이 클로저가 많이 사용이 되는가? 라는 질문을 하였습니다.
저는 JS 에서 중요한 개념이고, 우리가 의도를 했든 안했든 사용이 되어진다라고 이야기를 해줬습니다.

하지만 팀원은 여전히 갸우뚱? 하는 모습을 보여..
React 를 공부하셨다니 React 를 예시로 설명을 해드렸습니다.

이거는 그 설명한 정리한 내용입니다.


클로저란?

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

클로저는 주변 상태(렉시컬 환경)에 대한 참조와 함께 번들로 묶인(묶음) 기능의 조합입니다. 즉, 클로저를 사용하면 내부 함수에서 외부 함수의 범위에 접근할 수 있습니다.
-> 쉽게 정리하자면, 클로저는 함수에 렉시컬 환경을 기억하여 외부 범위에서도 접근이 가능한걸 말한다.

function makeFunc() {
  var name = "Mozilla";
  
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // displayName() uses variable declared in the parent function
  }
  
  return displayName;
}

var name = "kkkk";
const myFunc = makeFunc();
myFunc();

myFunc() 를 호출하면, 여전히 Mozilla 라고 출력이 정상적으로 되는게 확인이 가능하다. 이게 클로저다.
displayName() 함수는 여전히 makeFunc() 스코프를 기억하고 있다.(이러한 렉시컬 환경 조합은 함수 선언시에 결정이 된다)


클로저 in React

import React, { useEffect, useState } from 'react';

const ClosureInReact = () => {
  const [count, setCount] = useState(1);

  const incrementCount = () => {
    // closure
    setCount(count + 1);
  };

  useEffect(() => {
    const timer = setTimeout(() => {
      incrementCount();
    }, 1000);
    
    return () => {
      clearTimeout(timer);
    };
  }, [incrementCount]);
  
  // timer stoped 2
  // useEffect(() => {
  //     const timer = setTimeout(() => {
  //         incrementCount();
  //     }, 1000);
  //
  //     return () => {
  //         clearTimeout(timer);
  //     };
  // }, []); // empty dependency array

  // timer stoped 2
  // useEffect(() => {
  //     const timer = setInterval(() => {
  //         incrementCount();
  //     }, 1000);
  //
  //     return () => {
  //         clearInterval(timer);
  //     };
  // }, []); // empty dependency array
  
  return <div>{`Timer started: ${count}`}</div>;
};

export default ClosureInReact;

매초마다 증가하는 타이머를 작성하였다.


incrementCount() 함수를 살펴보면, count 상태를 변경하는데 이는 클로저다.

import React, { useCallback, useEffect, useState } from 'react';

const ClosureInReact = () => {
    const [count, setCount] = useState(1);

    const incrementCount = useCallback(() => {
        setCount(count + 1);
    }, []); // empty dependency array

    // const incrementCount = useCallback(() => {
    //     setCount(prevCount => prevCount + 1);
    // }, [count]);

    useEffect(() => {
        const timer = setTimeout(() => {
            incrementCount();
        }, 1000);

        return () => {
            clearTimeout(timer);
        };
    }, [incrementCount]);

    return <div>{`Timer started: ${count}`}</div>;
};

export default ClosureInReact;

useCallback() 훅을 보면, 하나는 빈배열이고 하나는 count 라는 의존성을 정의하였다.
만약 빈배열로 처리를 한다면, 카운트가 2 에서 멈추는걸 목격할거다. 클로저를 이해했다면 왜 그런지 이해가 될거다.

useEffect() 훅이 incrementCount 에 변경을 감지해야하는데, 이전 함수 레퍼런스와 동일하기 때문에 훅을 실행하지 않는다. 그래서 값이 2 에서 멈춰있는다.

import React, { useCallback, useEffect, useState } from 'react';

const ClosureInReact = () => {
    const [count, setCount] = useState(1);

    const incrementCount = useCallback(() => {
         setCount(count + 1);
    }, [count]);

    useEffect(() => {
        const timer = setTimeout(() => {
            incrementCount();
        }, 1000);

        return () => {
            clearTimeout(timer);
        };
    }, []); // empty dependency array
  
    // useEffect(() => {
    //     const timer = setInterval(() => {
    //         incrementCount();
    //     }, 1000);
    //
    //     return () => {
    //         clearInterval(timer);
    //     };
    // }, []); // empty dependency array

    return <div>{`Timer started: ${count}`}</div>;
};

export default ClosureInReact;

이번에는 useEffect() 훅에 의존성 배열을 빈배열로 정의하였다. 이것도 2 에서 타이머가 멈춘다.
setTimeout() 이 아닌, setInterval() 로 실행해도 마찬가지다.
해당 함수가 기억하는 값은 여전히 1 이기 때문이다. 의존성 배열에 incrementCount 를 추가해주면 해결이 가능하다.
useEffect() 훅 실행시점에 incrementCount() 함수는 count 가 1 인 렉시컬 환경을 기억하고 있다. 이후에 count 가 업데이트 되어 함수가 새로 변경이 되었어도, useEffect() 훅에서는 처음 1 을 기억하는 함수를 실행하기 때문에 카운트가 증가를 하지 않는다 또는 더 이상 useEffect() 훅이 실행 되지 않아 멈춰있는거다.


정리

  • 클로저는 JS 에서 중요한 개념이며, 알게모르게 우리는 클로저를 접하고 있다.
  • React 에서는 Hooks 에서 클로저를 사용하고 있다.
  • Hook 사용 시, 의존성 배열을 주의해야한다.
profile
developer life started : 2016.06.07 ~ 흔한 Front-end 개발자

1개의 댓글

comment-user-thumbnail
2022년 8월 30일

감사합니다

답글 달기