-
자바스크립트의 클로저(Closure)프론트엔드/JavaScript 2020. 9. 15. 05:07
클로저(closure)란?
클로저란 함수를 일급객체로 취급하는 함수형 언어(하스켈, 스칼라 등)에서 쓰이는 일종의 테크닉이다.
"클로저는 함수와 그 함수가 선언됐을 때 렉시컬 환경과의 조합이다"라는 유명한 말이 있다.
그런데 해당 내용으론 이해하기가 힘들고, 중첩된 함수에서 외부 함수는 이미 종료됐지만, 외부 함수에 있는 내부 함수는 그대로 살아있는 상황을 말한다.
이해하기 쉬우면 코드를 보면 더 편하다.
<body> <script> function outer(){ var free_variable = 10; return function closure(){ return free_variable; } } inner = outer(); console.log(inner()); // 10 출력 </script> </body>
위 코드는 외부 함수에서 반환값으로 내부 함수를 반환하거나, 외부 함수에서 내부 함수를 호출해준 경우다.
이때 잘 보면 내부함수 inner()를 호출했는데 10이 출력됐다.
근데 잘 생각해보면 좀 이상하다.
왜냐면 외부 함수는 내부 함수를 리턴하면서 종료됐다.
근데 내부 함수는 이때 외부 함수가 가진 변수 'free_variable'을 가져와서 반환한다.
즉 외부 함수의 역할과 외부 함수가 가진 변수는 return이 끝나면서 더이상 사용할 수 없게 됐다.
그런데 return의 결과값으로 실행된 내부 함수는 외부 함수의 변수를 버젓이 가져다 쓰고 있다.
클로저의 원인
클로저의 가장 근본적인 원인은 자바스크립트가 함수의 스코프를 정할 때 렉시컬 스코핑을 사용하기 때문이다.
무슨 말이냐면 자바 스크립트는 함수의 범위가 지정될 때 이미 그 범위와 어떤 변수를 참조할지 이미 다 정해져있다는 뜻이다.
언어에 따라 다른데 자바 스크립트는 함수가 실행될 때 그 범위가 지정되는 것이 아니라 선언될 때 이미 범위가 지정되어있다.
그래서 내부 함수의 경우 이미 선언할 때부터 즉 내부함수를 부르기 위해 외부 함수를 부르기 이전부터 이미 외부 함수의 어떤 변수를 쓸지 미리 참조하고 있었다는 뜻이다.
간단히 말하면 미래에 쓰일 것을 미리 알고 변수를 가져가 빌려쓰고 있었다는 것이다.
그래서 클로저를 쓰게 되면 외부 함수의 종료에도 불구하고 내부 함수는 완전히 종료되지 않고 메모리에 남아서 언제라도 쓸 수 있게 준비하고 있다.
클로저의 조건은 다음과 같다.
* 함수가 중첩되어 있어야 한다. 즉 외부 함수 안에 내부 함수가 있는 형태여야 한다.
* 외부 함수는 내부함수를 반환해줘야 한다.
* 내부 함수는 외부 함수로의 파라미터를 값을 받거나 외부 함수의 변수를 참조해야 한다.
* 이때 내부 함수가 참조하는 외부 함수의 변수를 자유 변수 (free variable)이라 한다.인터넷에는 내부 함수가 익명이어야만 클로저 기능을 한다고 써있는 경우도 있는데, 익명 아니어도 함수 반환이 가능하기 때문에 굳이 내부 함수가 익명이어야 할 이유는 없다.
클로저의 쓰임새
앞서 말했듯이 클로저는 항상 내부 메모리에서 쓰이길 기다리고 있다고 했다.
실제로 이로 인해 클로저는 유용성이 꽤 크다.
1. 전역 변수 사용의 억제 & 대체
클로저를 알기 전에 함수를 이용해서 어떤 데이터를 저장시켜주기 위해선 항상 전역 변수를 썼다.
일반적으로 함수는 쓰고나면 휘발되고 함수가 가진 지역 변수는 휘발되기 때문이다.
그렇다면 클로저는 어떨까?
<body> <script> function count(){ var free_variable = 0; return function plus(){ return free_variable++; // 자유 변수를 1 증가시켜줌 } } var counter = count(); console.log(counter()); // 0 출력 console.log(counter()); // 1 출력 console.log(counter()); // 2 출력 console.log(counter()); // 3 출력 console.log(counter()); // 4 출력 </script> </body>
위 코드는 내부 함수를 실행할 때마다 외부 함수의 변수를 1씩 증가시켜주는 코드다.
일반적으로 클로저 알기 전에는 함수 밖 전역 변수를 사용해서 1씩 증가된 숫자를 변수에 저장된다.
하지만 클로저의 내부 함수는 항상 대기 중이고 내부 함수가 참조하는 자유 변수는 덕분에 증발하지 않는다.
그래서 변수는 바뀐 숫자를 계속 갱신하면서 저장하게 된다.
덕분에 혼란을 줄 수 있는 전역 변수를 대신해서 사용함으로 전역 변수를 억제해줄 수 있는 역할을 한다.
2. 정보의 은닉
<body> <script> function sports(sports){ var sp = sports; return { mysports: function(){ return "내가 제일 좋아하는 스포츠는 "+sp+"입니다."}, bestsports: function(){ return "내가 제일 잘하는 스포츠는 "+sp+"입니다."} } } var me = new sports("축구"); console.log(me.mysports()); // 내가 제일 좋아하는 스포츠는 축구입니다 console.log(me.bestsports()); // 내가 제일 잘하는 스포츠는 축구입니다 </script> </body>
위는 클로저로 구현한 프라이빗 변수다.
이렇게 내부 함수를 통해 함수를 수시로 불러올 수 있다.
그리고 외부 함수가 가진 변수를 밖으로 드러내지 않을 수 있다.
이처럼 클로저를 사용하면 은닉이 가능하다.
클로저의 남용
클로저는 위와 같이 전역 객체도 대신하고 프라이빗 변수의 역할을 대신해줄 수 있다.
프로그래밍을 할 때 편리하고 안전하게 코드 설계가 가능하다.
다만 가장 큰 단점이 있다.
바로 클로저는 메모리를 계속 잡아먹는다는 것이다.
클로저는 위에서 말한대로 종료되지 않고 대기 상태이므로 클로저가 많아지면 그만큼 메모리를 계속 잡아먹고 있는 것이다.
그래서 클로저는 너무 남용하면 리소스를 낭비할 가능성이 있다.
'프론트엔드 > JavaScript' 카테고리의 다른 글
기본자료형/객체 비교하기, 타입 확인하기 (0) 2020.09.19 자료, 객체의 복사에 대해서 (0) 2020.09.19 자바스크립트 Object 객체 (0) 2020.09.14 자바스크립트의 상속 (inheritance) (0) 2020.09.14 자바스크립트 this의 정의 (0) 2020.09.14