JavaScript | 클로저란 무엇인가?
👨💻 JavaScript | 클로저란 무엇인가?
오늘은 자바스크립트에서 클로저라는 개념에 대해서 이해하고 왜 사용하는지에 대하여 알아보겠습니다.
우리가 학습할 내용을 정리해보자면 아래와 같습니다.
- 클로저의 개념
- 클로저는 언제 사용되어지는가?
- 클로저를 왜 사용하는가?
1. 클로저의 개념
"생활코딩"에서 클로저에 대한 정의를 다음과 같이 설명하고 있습니다.
클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다. |
참고사이트
자바스크립트에서는 특정 함수 내부에 또다른 함수를 선언하는것이 가능한데요, 이것을 '내부함수' 라고 부르며 이것을 통해 구현한 것이 '클로저' 입니다.
일단 내부함수가 무엇인지 알아보도록 하겠습니다. 아래의 예제를 보시면 innerFunc이라는 함수 안에서 익명함수를 return문을 통해 반환하는 것을 보실 수 있습니다.
이와같이 함수 내부에 또다른 함수를 선언한것을 의미하며 위에 코드의 실행결과로는 아래와 같습니다.
여기서 흥미로운 사실은 내부함수는 외부함수의 지역변수(위코드에서는 text변수)에 접근이 가능하다는 점입니다.
일반적으로 우리가 알고있는 함수의 지역변수는 함수의 라이프사이클과 함께하며, 본인의 역할을 다 하고 종료되었을때 가지고 있던 지역변수도 같이 사라지게 됩니다.
하지만, 내부함수를 통해 접근한 외부함수의 지역변수들의 값은 외부함수의 실행이 끝나서 소멸된 이후에도 외부에서 저장한 내부함수를 통해 접근이 가능합니다. 말로 설명하니 조금 어려운것 같으니 예제를 통해 알아보도록 하겠습니다.
아래의 예제에서는 외부함수의 매개변수로 특정 이름(name)을 받아와 새로운 문자열(text)을 만들었습니다. 그리고 이 문자열을 내부함수를 통해 console에 출력을 하게 하였습니다.
위의 14~16줄을 보면 외부함수를 실행시키는 부분을 보실 수 있습니다. 그리고 이 외부함수는 16줄 이후에서 종료되어 사라지게 되어지죠. 실행된 각 외부함수들은 내부함수들을 반환하여 test1~3 변수에 각각 저장되어지게 됩니다.
여기서 각 test1~3변수에 저장이 된 주체는 내부함수기 때문에 종료되어 소멸된 외부함수의 지역변수들 또한 소멸이 되어야 할것 같지만, 20~22번줄을 실행한 결과가 아래와같이 나오는 것을 보실 수 있습니다.
클로저는 내부함수와 밀접한 관계를 가지고 있는 주제입니다. 내부함수는 외부함수의 지역변수에 접근 할 수 있는데 위의 결과처럼 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근 할 수 있습니다. 이러한 메커니즘을 클로저라고 합니다.
요약해서 클로저란 내부함수가 외부함수의 지역변수에 접근 할 수 있고, 외부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸될 때까지 소멸되지 않는 특성을 의미합니다.
참고사이트
2. 클로저는 언제 사용되어지는가?
클로저는 내부함수와 밀접한 관계가 있다고 위에서 설명을 드렸는데요, 여기서 내부함수는 외부함수의 지역변수를 가져다 사용이 가능하다 말씀드렸었습니다. 사실 내부함수는 외부함수의 지역변수 뿐 아니라 매개변수값도 참조해서 사용이 가능한데요, 이러한 기능은 창의적인 방법으로 활용될 수 있습니다.
아래의 내부(private)변수예제는 더글라스 크락포드(Douglas Crockford)에 의해 처음 시연되었습니다.
클로저와 객체(Dictionary)를 이용해서 마치 클래스의 Getter 함수, Setter함수와 같은 기능을 하도록 구현이 가능합니다. 이렇게 작성하게 된다면 그저 외부함수 지역변수의 값을 읽어오는것 뿐 아니라 그 값을 변경하는것이 가능해집니다.
또 다른 예제로는 반복문 클로저 예제가 있습니다. 이 예제는 단순히 0~9의 값을 setTimeout 함수를 이용해 출력하는 함수를 구현하는 것입니다. 만약 클로저를 고려하지 않고 이를 구현하라고 한다면 아래와 같이 작성을 하게 될것입니다.
하지만 이렇게 작성된 코드의 결과는 우리가 예상했던 0~9의 값을 출력하는 것이 아니라 10의 값을 반복해서 찍어주게 됩니다.
이유즉슨, setTimeout 함수에 인자로 넘어간 익명함수는 모두 0.1초 뒤에 호출이 되어지게 될텐데 그 시간 사이에 반복문이 전부 돌아 i값을 10으로 만들어 버렸기 때문입니다.
이를 우리가 원했던 0~9의 값이 출력되도록 하기 위해서는 클로저를 이용하여 매 반복시 증가되는 i값을 매개값으로 받는 외부함수 하나를 생성하고 그안에서 i값을 받아 setTimeout을 실행하는 내부함수를 만들어 주면 됩니다.
이렇게 해주게 되면, 외부함수를 반복횟수만큼 실행한 상황이 만들어지고 각 외부함수 매개값으로 i값을 넘겨주었기 때문에 이를 내장함수가 참조해서 사용할 수 있게 되어지는 것이죠.
이를 실행하게 되면 우리가 예측한대로 잘 출력이 되어집니다.
참고사이트
3. 클로저를 왜 사용하는가?
자바스크립트에서는 함수가 선언될 때 자신이 접근할 수 있는 범위를 정하고 기억하고 있는데 이것을 렉시컬 스코프라고 합니다. 그리고 이런 렉시컬 스코프에 의해 외부 함수의 환경을 기억하고 있는 내부 함수가 클로저입니다.
여기서 "lexical"이란, 어휘적 범위 지정(lexical scoping) 과정에서 변수가 어디에서 사용 가능한지 알기 위해 그 변수가 소스코드 내 어디에서 선언되었는지 고려한다는 것을 의미합니다. 단어 "lexical"은 앞에서 정의한 의미를 가지며, 중첩된 함수는 외부 범위(scope)에서 선언한 변수에도 접근할 수 있습니다.
클로저는 클래스와 꽤나 비슷한 구조를 가지고 있습니다. 그렇기 때문에 클래스 개념이 없는(ES6에서는 있습니다.) 자바스크립트에서는 클래스를 대신하여 쓰이기도 합니다.
그렇다면 객체지향적 개발을 위해서 클로저를 무조건 사용하는것이 좋을까요?
그에대한 대답은 "적절한 상황에 사용하되 메모리할당을 해제시켜주자" 라고 답변드리고 싶습니다.
클로저는 각자의 환경을 가집니다. 이환경을 기억하기 위해서 외부함수자체를 소멸시키지 않고 기억하기 때문에 당연히 메모리 또한 소모되어질겁니다. 그렇기 때문에 클로저를 생성해놓고 참조를 제거하지 않는 것은 C++에서 동적할당으로 객체를 생성해놓고 메모리할당을 해제하지 않는것과 비슷합니다.
즉, 클로저를 통해 내부 변수를 참조후 사용이 끝나면 참조를 제거하는것이 메모리 효율에 좋다고 볼 수 있습니다.
그렇기에 위와같이 답변을 드릴수 있겠네요 : )
참고사이트
📌 서브제목
.
'Javascript > 이론' 카테고리의 다른 글
JavaScript | 스코프와 호이스팅 (0) | 2020.06.07 |
---|