클로저

왕지호·2022년 11월 8일
0

오늘은 클로저에 대해 알아보자!

클로저
mdn 정의

  • "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.”
  • 즉, 클로저는 "외부 함수의 변수에 접근할 수 있는 내부 함수"

클로저 함수의 특징

//화살표 함수를 이용한 간단한 덧셈 함수
const add = (x,y) => x + y;
add(5, 7) // 12

//함수 호출(invocation)이 두번 발생
const add = x=> y => x + y;
adder(5)(7) // 12

클로저에는 2가지 특징이 있다!

  1. 함수를 리턴하는 함수
const add = x=> y => x + y;
adder(5)(7) // 12

typeof adder(5) //'function'

adder(5) // y => x+y
  • adder(5)의 리턴 값이 함수의 형태임을 의미한다.
    즉, adder 함수를 리턴하는 함수이다.

위의 화살표 함수로 작성된 함수 표현식을 function 키워드를 사용해서 표현해보자

const adder = function (x) {
  return function (y) {
    return x+y;
  }
}
  • 클로저 함수는 위와 비슷한 모양을 가진다.
  • 함수를 리턴하는 함수가 클로저의 형태를 만든다.'
  1. 내부 함수는 외부 함수에 선언된 변수에 접근 가능하다
  • 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분된다
  • 클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(closure; 폐쇄) 데에 있다.
  • 따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요하다.

  • 의의 이미지에서는 변수 x와 y가 선언된 곳이 각기 다르다
  • x가 선언된 함수는 바깥쪽에 있으니 '외부 함수’
  • y가 선언된 함수는 보다 안쪽에 있으니 '내부 함수’
  • 따라서, 이 클로저 함수는 스코프가 분리되어 있다.

흠... 그렇다면 외부함수나 내부 함수 둘 다 서로 접근이 가능할까...?

아쉽지만 아니다!

Q. 외부 함수는 y에 접근이 가능한가?
→ no, 바깥 스코프에서는 안쪽 스코프로의 접근이 불가능하다.
Q. 내부 함수는 x에 접근이 가능한가?
→ yes, 안쪽 스코프는 바깥 스코프에서 선언된 변수에 접근이 가능하다.

그럼 클로저는 어떤 방식으로 활용 가능할까?

클로저의 활용

데이터를 보관하는 함수

  • 일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수가 없다.
  • 이와 다르게, 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장된다 (어휘적 환경을 메모리에 저장하기 때문에 가능한 일)
  • 변수 add5 에는 클로저를 통해 리턴한 함수가 담겨 있다. add5 는 재미있게도, adder함수에서 인자로 넘긴 5라는 값을 x 변수에 계속 담은 채로 남아있다. 외부 함수의 실행이 끝났음에도!

클로저 모듈 패턴

  • 클로저를 이용해 내부 함수를 단 하나 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만든다.

  • '외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'라는 규칙에 의해, 어떤 경우에도 value는 직접 수정이 불가능하다. 대신, 리턴하는 객체가 제공하는 메서드를 통해 value 값을 간접적으로 조작할 수 있다.
  • 이것이 바로 정보의 접근 제한 (캡슐화)!

왜 이런식으로 하는 것일까...?

만일 스코프로 value 값을 감싸지 않았더라면, value 값은 전역 변수여야만 했을 것이다. 하지만 makeCounter라는 함수가 value 값을 보존하고 있기 때문에, 전역 변수로 따로 만들 필요가 없어졌다. 전역 변수가 좋지 않은 이유는, 전역 변수는 다른 함수 혹은 로직 등에 의해 의도되지 않은 변경을 초래하기 때문.
이를 side effect라고 한다. side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있다. 따라서 이에 따른 오류로부터 보다 안전하게 값을 보호할 수 있다.

클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다!

모듈화

  • 위의 이미지에서 볼 수 있듯이, makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 된다. 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고, 각각의 값을 보존할 수 있다.
  • 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 한다.

클로저를 통해 데이터와 메서드를 같이 묶어서 다룰 수 있다. 즉, 클로저는 모듈화에 유리하다!

const makeCounter = () => {
	let value = 0;
	
	return {
		increase: () => {
			value = value + 1
		},
		decrease: () => {
			value = value - 1
		},
		getValue: () => value
	}
}

const counter1 = makeCounter();

counter1.increase(); //1
counter1.increase(); //1+1 =2
counter1.decrease(); //2-1 =1
counter1.decrease(); //1-1 =0
counter1.increase(); //1
counter1.getValue(); // 1
  • 클로저 모듈 패턴의 예시
    변수 value는 직접 수정하는 것이 불가능하고, 리턴하는 객체가 제공하는 메서드를 통해서만 조작이 가능하다. 이렇게 캡슐화를 함으로써 변수 value를 makeCounter 함수로 보존해서 전역 변수로 인한 side effect를 방지할 수 있다.
profile
개발 공부하는 코린이!

0개의 댓글