본문 바로가기
Javascript

[Javascript] Closure

by kellis 2020. 10. 21.

1) 클로저란?

함수가 선언된 렉시컬 스코프를 기억해서, 함수가 스코프 밖에서 실행되어도 해당 스코프에 접근할 수 있도록 해주는 것을 클로저라고 말합니다. 함수 안에 함수가 있는 형태로 외부(outer) 함수가 실행 후 종료되어도 내부(inner) 함수에서 outer 함수의 지역변수를 참조할 수 있는 것이 특징입니다. 더 정확히 말하자면 outer 함수는 종료된다고 할 지라도 inner 함수가 실행되는 상태이면 outer 함수는 닫히지 못합니다. 

 

클로저는 일반적으로 아래와 같은 조건을 충족합니다.

  • 내부 함수는 익명 함수로, 외부 함수의 return 값으로 사용
  • 반환된 내부 함수는 외부 함수의 실행컨텍스트에서 실행됨
  • 내부 함수에서 사용되는 변수는 외부 함수의 변수 스코프에 있어야 함

 

코드를 예로 들어 자세히 살펴보겠습니다. 

<script>
    var counter = (function(){
        var count = 0;
 
        return function(){
            ++count;
            document.getElementById("clickCount").innerHTML = count;
        }
    })();
</script>
 
<html>
    <button onclick="counter()">Increase Count</button>
    <div>
        Click count is <span id="clickCount"> 0 </span> times.
    </div>
</html>

버튼을 클릭하면 숫자를 증가시키는 코드입니다. 만약 위처럼 클로저를 사용하지 않았다면, 전역 변수로 clickCount를 생성하여 onclick 이벤트가 발생할 때마다 clickCount span 태그 내 값을 변경해주어야 했을 것이고, 이런 전역 변수가 많아지면 전역 스페이스가 오염되었을 것입니다.

 

2) 클로저를 사용한 자바스크립트 캡슐화 

위에서 보았듯이 클로저를 이용하면 전역 변수가 전역 네임스페이스를 오염시키지 않도록 만들어줄 수 있다고 했습니다. 또 다른 클로저를 이용한 이점에는 private 메서드 혹은 변수를 작성할 수 있도록 해준다는 점입니다. 기본적으로 자바스크립트는 자바와 달리 private 접근 제한 기능을 제공하지 않습니다. 그래서 클로저를 이용하여 마치 private처럼 동작하도록 작성할 수 있습니다. 

private 메서드가 중요한 이유는 객체지향의 장점인 정보 은닉과 캡슐화(코드에 대한 접근을 제한)를 제공한다는 점뿐만 아니라, 마찬가지로 불필요한 메서드가 전역 네임 스페이스를 혼란스럽게 만들지 않도록 도와준다는 점입니다. 

 

코드를 예로 살펴보겠습니다. 

 

[자바스크립트 캡슐화]

var counter = (function() {
    var count = 0;
 
    function operation(value) {
        count += value;
    }
 
    return {
        increase: function() {
            operation(1);
        },
        decrease: function() {
            operation(-1);
        },
        getCount: function() {
            return count;
        }
    }
})();
 
console.log(counter.getCount());    // result : 0
counter.increase();
counter.increase();
counter.increase();
console.log(counter.getCount());    // result : 3
counter.decrease();
console.log(counter.getCount());    // result : 2
  • private : count, operation() 
  • counter  함수 내의 익명 함수 내 세 개의 퍼블릭 함수(increase(), decrease(), getCount())만 접근 가능
  • 위 코드에서 클로저는 세 개 함수 increase(), decrease(), getCoune()이며, 같은 렉시컬 환경을 공유하기 때문에 count 값이 유지된다. 

이런 식으로 클로저를 사용하는 것을 모듈 패턴이라고 합니다. 

3) 주의할 점

(1) 변수의 공유 

아래 코드를 먼저 살펴보겠습니다. 

 

[직접 참조]

function adder(){ 
    var num= 0;
    return {
        singleAdder: function(){
            num+=1;
            console.log("num :"+num);                                                                     
        },
        doubleAdder: function(){
            num+=2;
            console.log("num :"+num);
        }
    };
}
var out = adder(); 
var out2 = adder(); 
out.singleAdder();  // result : 1
out.doubleAdder();  // result : 3
out2.singleAdder(); // result : 1
out2.doubleAdder(); // result : 3
  • 외부 함수의 변수는 값을 복사해 전달하는 것이 아니라 직접 참조 : adder의 singleAdder와 doubleAdder는 num을 공유
  • 외부 함수 실행시 내부 함수가 반환되므로, 외부 함수의 리턴 값이 여러 개이면 클로저가 여러 개 생성되고 각 클로저마다 외부 함수의 변수는 다른 주소 값을 가짐 : singleAdder과 doubleAdder는 별개의 변수 => 따라서 클로저가 남용되면 메모리 측면에서 매우 비효율적 

 

(2) Memory Leak

전통적인 방식의 서버 사이드 렌더링 애플리케이션에서는 페이지가 이동될 때 변수의 라이프사이클이 끝나기 때문에 이러한 고려를 할 필요가 없었습니다. 그러나 SPA에서는 클로저를 닫아주지 않으면 클로저가 계속해서 메모리에 쌓이게 되고 Memory leak이 발생할 수 있습니다. 따라서 클로저 변수를 더 사용하지 않는다면 null을 할당해주어 메모리에서 제거될 수 있도록 해 주어야 합니다. 

 

p.s 내부 함수를 return하지 않아도 클로저를 생성하는 경우가 존재하는데, 이는 아래와 같습니다. 

  • setTimeout, setInterval 등과 같이 비동기적으로 호출되는 경우
  • addEventListener와 같이 이벤트의 콜백 함수로 활용되는 경우

 

클로저는 위에서 언급했듯이  많은 장점들을 가지고 있고, 이를 통해 코드를 더욱더 쉽고 간결하게 만들어 줍니다. 또한 자바스크립트에서 가장 많이 이용되는 이벤트 및 비동기식 호출에서 기본적으로 클로저가 생성되기 때문에 클로저를 잘 알지 못하면 이러한 코드를 작성하기 어렵습니다. 무엇보다도 클로저의 단점(메모리를 소모하며, 클로저마다 새로운 스코프를 생성한다는 점에서 성능상의 이슈)이 애플리케이션에 치명적인 문제를 일으킬 수 있기 때문에, 클로저가 어떻게 동작하는지 잘 이해해 두는 것이 중요합니다. 

 

 

 

[references]

렉시컬 스코프

 

http://wiki.sys4u.co.kr/pages/viewpage.action?pageId=10584555

페이지 … SYS4U OPEN WIKI 기타등등 뉴스클리핑 2019 19-7월 배너의 맨 끝으로 배너의 맨 처음으로 메타 데이터의 끝으로 건너뛰기 정화 최님이 작성, 7월 19, 2019에 최종 변경 메타 데이터의 시작으로 �

wiki.sys4u.co.kr

4 Javascript Design Patterns You Should Know

 

JavaScript Design Patterns | DigitalOcean

Every developer strives to write maintainable, readable, and reusable code. Code structuring becomes more important as applications become larger. Design patterns prove crucial to solving this challenge - providing an organization structure for common

www.digitalocean.com

모듈 패턴

 

댓글