고대 중국에 이런 말이 전해진다고 한다.
‘나는 듣고 잊는다. 나는 보고 기억한다. 나는 행하고 이해한다.’
나는 CS를 전공하지 않았다. 부족한 부분이 많기에 기회가 될 때마다 궁금한 것들을 찾아보고 꾸준히 읽으려 노력한다. 하지만 읽을 때는 이해가 된 것 같아도 막상 그 주제에 대해 상대방에게 이야기하거나 설명하려면 종종 어려움을 겪는다.
‘내가 기억하는 이게 맞나? 확실한가?’
듣기만 해서는 돌아서면 잊어버리고, 보고 나서야 조금 기억하는 수준이니, 나는 꾸준히 글을 쓰고, 침대 위 곰돌이에게 설명하는 습관을 길러 보다 정확히 그 주제에 대해 이해하려고 노력해야겠다.
앞으로 궁금했던 주제들, 내가 이해하고 싶은 것들에 대해 미흡하지만 정리하는 포스팅들을 올릴 예정이다.
스스로 학습한 것 이기에 오류나 잘못된 것이 있다면 정정할 수 있도록 얘기해주시면 고맙겠다. 그리고 나와 비슷한 이들에게 조금이라도 도움이 되었으면 좋겠다.
그래서 선택한 오늘의 주제는 예전부터 한번 정리해보고 싶었던 클로저.
클로저는 무언인가
클로저는 내부함수가 외부함수의 맥락(context)에 접근가능 한 것을 뜻한다. MDN에서는 함수와 함수가 선언된 어휘적 환경의 조합이라고도 나와있다.
자바스크립트는 객체나 문자열 등 무엇인가 생겨날 때 메모리가 할당되고, 더 이상 사용되지 않을 때는 자동으로 메모리를 반환하는데 이 과정을 가비지컬렉션이라고 하며 이런 작업을 수행하는 가비지컬렉터(garbage collector)가 있다.
클로저는 내부함수가 외부함수의 지역변수 등에 접근할 수 있고 그 지역변수를 사용하는 내부함수가 사라질 때까지 소멸되지 않는다. 따라서 가비지컬렉터가 해당 외부함수의 지역변수를 메모리에서 해제하지 않기에 계속 접근이 가능하다.
전역변수를 설정하지 않고 전역변수처럼 사용하는 방법이 있나?
얼마전 위와 같은 질문을 받았다. 전혀 생각해본적도 없는 질문이었다.
가만히 생각해보니 클로저를 사용하면 될 것 같았다. 내부함수에서 계속 접근할 수 있으니 전역변수처럼 사용할 수 있지 않을까.
위 질문에 대한 대답이 될 수 있는 클로저를 나름대로 설명해보려 한다.
poo 함수를 보면 내부에 count라는 지역변수가 선언되어 있다. 그리고 poo 의 지역변수인 count에 1을 더하는 함수인 poopoo 를 리턴한다.
pooper 에는 poo 의 리턴값이자 내부함수인 poopoo 가 할당되었다. 이제 pooper 를 실행하면 어떻게 될까?
poo의 지역변수인 count가 계속 메모리에 상주하며 출력이 되고 있다. 그렇다면 전역변수로 count를 만들면 어떨까.
pooper를 실행하면 전역변수인 count 가 1씩 증가하고 있다. 클로저를 사용했을 때와 동일한 결과를 얻을 수 있다.
프라이빗 메소드를 흉내내보자
이번엔 클로저로 프라이빗 메소드를 흉내내보자.
foo 함수의 지역변수로 privateCount 가 있고 changeCount 라는 함수가 있다. 둘 다 foo의 외부에서는 접근할 수 없다. 대신 foo 가 리턴하는 3개의 함수 (increment, decrement, value)를 통해서만 접근이 가능하다. 세 함수가 같은 환경을 공유하는 클로저이기 때문이다.
그렇다면 카운터 함수를 2개를 만들면 서로 독립성을 유지할까? 왠지 같은 곳을 참조하고 있으니 상관관계가 있진 않을까?
counter 함수를 2개 만들었다. counter1 에서 increment 메소드를 2번 호출하고 decrement 메소드를 1번 호출했다. counter1.value의 값은 1로 privateCounter가 현재 1이라는 것을 알 수 있다. 그렇다면 counter2.value의 값은 얼마일까?
counter2.value는 0이다.
각 클로저는 고유의 클로저를 통해 privateCounter 변수를 다른 버전을 참조하기 때문에 서로 독립성을 유지할 수 있다. 하나의 클로저에서 변수 값을 변경하더라도 다른 클로저의 값에는 영향을 주지 않는다.
MDN에는 이런 방식으로 클로저를 사용해 객체지향 프로그래밍의 정보 은닉과 캡슐화 같은 이점을 얻을 수 있다고 설명하고 있다.
아래는 캡슐화(encapsulation)에 대한 짧은 설명이다.
객체의 필드(속성), 메소드를 하나로 묶고, 실제 구현 내용을 외부에 감추는 것을 말한다.
외부 객체는 객체 내부의 구조를 얻지 못하며 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있다.
필드와 메소드를 캡슐화하여 보호하는 이유는 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하는데 있다.
출처: http://webclub.tistory.com/156 [Web Club]
정리
- 클로저는 자신을 감싸고 있는 바깥 함수의 context(일반적으로 변수에 접근한다고 하는 설명이 많다)에 접근할 수 있는 내부의 함수를 말한다.
- 클로저는 서로독립적인 환경을 갖는다.
- 가비지컬렉션에서 정리되어야 할 것들이 메모리에 상주하므로 메모리 누수에 신경써야 한다.
그렇다 클로저는 이런 것이었다.