클로저(Closures)

Yeong·2023년 5월 1일

자신이 선언될 당시의 환경을 기억하는 함수
"함수 안에 함수를 선언한 환경에서의 관계" 를 의미

함수안에 함수를 선언한 환경은 내부 함수에서 외부 함수로 지역변수를 접근할 수 있지만 외부 함수의 실행이 끝나고 외부 함수가 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근할 수 있는 것을 의미합니다.

이게 어떻게 가능하냐면!
💡 자바스크립트는 함수를 리턴하고 리턴되는 함수가 클로저를 형성하기 때문에 접근이 가능합니다.
✴️ 클로저는 반환된 내부함수가 자신이 선언되었을때의 환경(Lexical Environment)에서의 스코프를 기억하기 때문에 접근이 가능합니다.✴️

const closureComponent = () => {
	// 외부 함수 영역
	const outerFunc = () => {
		let x = 1000;

		// 내부 함수 영역
		let innerFunc = () => {
			console.log(x); 
        // 내부 함수에서 외부 함수로 지역 변수의 접근을 한다.
		};
		return innerFunc;
	};

	// 외부 함수를 실행합니다.
	const callInnerFunc = outerFunc();

	// 외부 함수의 실행이 끝나더라도 변수에 접근이 가능 합니다.
	callInnerFunc(); // 1000
};
export default closureComponent;

왜 클로저를 사용해야할까?

  1. 정보의 접근 제한(Private Mode)
    자바스크립트 내에서는 다른 언어와 달리 메소드를 Private Method 형태로 선언 할 수 있는 기능을 제공하지 않습니다. 그렇기에 이와 비슷한 형태로 정보의 접근을 제한하는 캡슐화를 클로저 개념을 이용하여 구성 할 수 있습니다.

💡 Private Method 란?

  • 같은 클래스 내부의 특정 메소드에서만 해당 메소드를 호출이 가능한 것을 의미합니다.
  • 이를 사용하면 코드의 제한적인 접근만을 제공한다는 장점이 있습니다.
  • 전역 네임 스페이스를 관리하여 불 필요한 메서드가 공용 인터페이스를 혼란스럽게 만들지 않도록 합니다
import { useEffect } from 'react';

/**
 * 클로저를 이용한 Private Method 구현하기 
 * @returns 
 */
const closure1PrivateMethod = () => {
	useEffect(() => {
		console.log(counter.value()); // logs 0
		counter.increment();
		counter.increment();
		console.log(counter.value()); // logs 2
		counter.decrement();
		console.log(counter.value()); // logs 1
	}, []);

	/**
	 * 숫자를 늘리고 줄이는 함수
	 */
	const counter = (() => {
		// 외부의 함수의 변수를 선언합니다.
		let count = 0;

		// 전달 받은 값을
		const changeBy = (value: number) => {
			count += value;
		};

		return {
			// 값을 증가합니다.
			increment: (): void => {
				return changeBy(1);
			},

			// 값을 줄입니다.
			decrement: () => {
				return changeBy(-1);
			},

			// 값을 출력합니다.
			value: () => {
				console.log(count);
				return count;
			},
		};
	})();

	return <div></div>;
};
export default closure1PrivateMethod;
  1. 상태유지
    💡 클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수를 사용 할 수 있기에 이러한 특성을 이용하여 특정 데이터를 스코프 안에 두고 계속 최신 상태로 유지하여 사용합니다.
    💡 즉, 현재 상태를 기억하고 있다가 상태가 변경되면 그것을 최신 상태로 유지하는 기능으로 사용합니다.
    💡 React내에서는 상태관리로 사용되는 useState가 이를 의미합니다.
import { useState } from 'react';

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

	const onClickBtnHandler = () => {
		setCount(count + 1);
	};

	return (
		<div>
			<div>
				<button type='button' onClick={onClickBtnHandler}>
					클로저 테스트
				</button>
			</div>
			<div>
				<input type='text' value={count} />
			</div>
		</div>
	);
};
export default closure2React;
  1. 전역 변수의 사용 억제-모듈화에 유리하다.
    💡 클로저 함수를 각각의 변수에 할당하면 각자 독립적으로 값을 사용하고 보존할 수 있다.
    이와 같이 함수의 재사용성을 극대화 함수 하나를 독립적인 부품의 형태로 분리하는 것을 모듈화라고한다.
    💡 클로저를 통해 데이터와 메소드를 묶어다닐 수 있기에 클로저는 모듈화에 유리하다.
import { useEffect } from 'react';

const closure3Modlues = () => {
	useEffect(() => {
		counter.increment();
		counter.increment();
		counter.increment();
		console.log(counter.value()); // 3
		counter.decrement();
		counter.decrement();
		counter.decrement();
		console.log(counter.value()); // 0
	}, []);

	/**
	 * 숫자를 세는 클로저를 이용한 모듈
	 * @returns
	 */
	const counter = (() => {
		let count = 0;

		return {
			// 값을 증가합니다.
			increment: () => {
				return (count += 1);
			},

			// 값을 줄입니다.
			decrement: () => {
				return (count -= 1);
			},

			// 값을 출력합니다.
			value: () => {
				return count;
			},
		};
	})();

	return (
		<></>
	);
};

export default closure3Modlues;

추가 예제

 function makeAdder(x) {
      var y = 1;
      return function(z) {
        y = 100;
        return x + y + z;
      };
    }

    var add5 = makeAdder(5);
    var add10 = makeAdder(10);
    //클로저에 x와 y의 환경이 저장됨

    console.log(add5(2));  // 107 (x:5 + y:100 + z:2)
    console.log(add10(2)); // 112 (x:10 + y:100 + z:2)
    //함수 실행 시 클로저에 저장된 x, y값에 접근하여 값을 계산

이 예제에서 단일 인자 x를 받아서 새 함수를 반환하는 함수 makeAdder(x)를 정의했다. 반환되는 함수는 단일인자 z를 받아서 x와 y와 z의 합을 반환한다.

본질적으로 makeAdder는 함수를 만들어내는 공장이다. 이는 makeAdder함수가 특정한 값을 인자로 가질 수 있는 함수들을 리턴한다는 것을 의미한다. 위의 예제에서 add5, add10 두 개의 새로운 함수들을 만들기 위해 makeAdder함수 공장을 사용했다. 하나는 매개변수 x에 5를 더하고 다른 하나는 매개변수 x에 10을 더한다.

add5와 add10은 둘 다 클로저이다. 이들은 같은 함수 본문 정의를 공유하지만 서로 다른 맥락(어휘)적 환경을 저장한다. 함수 실행 시 add5의 맥락적 환경에서 클로저 내부의 x는 5 이지만 add10의 맥락적 환경에서 x는 10이다. 또한 리턴되는 함수에서 초기값이 1로 할당된 y에 접근하여 y값을 100으로 변경한 것을 볼 수 있다. (물론 x값도 동일하게 변경 가능하다.)
이는 클로저가 "리턴된 후에도 외부함수의 변수들에 접근 가능"하다는 것을 보여주는 포인트이며 클로저에 단순히 값 형태로 전달되는것이 아니라는 것을 의미한다.

0개의 댓글