클로저는 "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성 된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다." 라고 한다. 여기서의 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"이다. 특이하게도 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 한다.
클로저 함수: 클로저는 외부함수의 컨텍스트에 접근할 수 있는 내부함수를 뜻한다. 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있다.
클로저 사용 예시: 클로저를 통해 커링(currying, 함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어 각각 인자를 받게 하는 방법), 클로저 모듈(변수를 외부 함수 스코프 안쪽에 감추어, 변수가 함수 밖에서 노출되는 것을 막는 방법) 등의 패턴을 구현할 수 있다.
클로저의 단점: 일반 함수였다면 함수 실행 종료 후 가비지 컬렉션(참고 자료: MDN '자바스크립트의 메모리 관리') 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 된다. 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문이다. 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있다. 자바스크립트는 가비지 컬렉션을 통해 메모리 관리를 한다. 객체가 참조 대상이 아닐 때, 가비지 컬렉션에 의해 자동으로 메모리 할당이 해제된다.
클로저 함수의 특징
- 함수를 리턴하는 함수
- 외부 함수의 변수에 접근가능한 내부 함수
클로저의 활용
데이터를 보존하는 함수
일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없다. 이와 다르게, 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장된다. (어휘적 환경을 메모리에 저장하기 때문에 가능한 일이다.)
정보의 접근 제한(캡슐화)
클로저 모듈 패턴
const makeCounter = () => {
let value = 0;
return {
increase: () => {
value = value + 1;
},
decrease: () => {
value = value - 1;
},
getValue: () => value;
}
}
const counter1 = makeCounter();
counter1; // {increase : f, decrease : f, getValue : f}
클로저를 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만든다. makeCounter 함수는 increase, decrease, getValue메소드를 포함한 객체 하나를 리턴한다. 따라서, counter1은 객체이다.
'외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'는 규칙에 의해, 어떤 경우에도 `value`는 직접 수정이 불가능하다. 대신, 리턴하는 객체가 제공하는 메소드를 통해 `value` 값을 간접적으로 조작할 수 있다. 이것이 바로 정보의 접근 제한 (캡슐화) 이다.
왜 이렇게 하는 것일까요? 만일 스코프로 value 값을 감싸지 않았더라면, value 값은 전역 변수여야만 했을 것이다. 하지만 makeCounter라는 함수가 value 값을 보존하고 있기 때문에, 전역 변수로 따로 만들 필요가 없다.
전역 변수가 좋지 않은 이유는, 전역 변수는 다른 함수 혹은 로직 등에 의해 의도되지 않은 변경을 초래하기 때문이다. 이를 side effect라고 한다. side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있다. 따라서 이에 따른 오류로부터 보다 안전하게 값을 보호할 수 있다.
클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있다.
모듈화
const counter1 = makeCounter();
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue(); // 1
const counter2 = makeCounter();
counter2.decrease();
counter2.decrease();
counter2.decrease();
counter2.getValue(); // -3
makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 된다. 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고, 각각의 값을 보존할 수 있다.
이와 같이 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 한다.
클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있다. 즉, 클로저는 모듈화에 유리하다.
'TIL > Code States' 카테고리의 다른 글
Code States 15일차 - DOM (0) | 2021.08.06 |
---|---|
Code States 14일차 - Spread/Rest 문법 (0) | 2021.08.05 |
Code States 13일차 - 스코프 (0) | 2021.08.04 |
Code States 13일차 - 원시 자료형과 참조 자료형 (0) | 2021.08.04 |
Code States 12일차 - CSS 중급 (0) | 2021.08.03 |