Javascript | 클로저

Jae ·2021년 8월 4일
0

Javascript

목록 보기
11/14

캡슐화 : 데이터 간 변동을 막기 위해서, 각 정보의 접근을 제한한다.
모듈화 : 함수 하나를 완전히 독립적인 부품 형태로 분리한다.

"함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다."

여기서 주목할 만한 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"입니다. 특이하게도 자바스크립트는 함수가 호출되는 환경과 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 합니다. "외부함수의 변수에 접근할 수 있는 내부함수"를 클로저 함수로 부르는 이유도 그렇습니다. 아직은 무슨 의미인지 잘 와닿지 않으리라 생각됩니다. 각 강의를 통해 이에 대해 차근차근 배워봅시다.

✅ Achievement Goals

  • 클로저 함수의 정의와 특징에 대해서 이해할 수 있다
  • 클로저가 갖는 스코프 범위를 이해할 수 있다
  • 클로저를 이용해 유용하게 쓰이는 몇 가지 코딩 패턴을 이해할 수 있다

클로저 함수 특징

이 함수를 조금 다른 방식으로 구현해봅시다. 아래와 같이 화살표를 두 번 사용합니다.

앞서 본 코드와는 다르게, 함수의 호출(invocation) 이 두 번 발생합니다.

이 말은 곧 adder(5) 의 리턴값이 곧 함수의 형태임을 의미합니다.

즉, adder는 함수를 리턴하는 함수입니다. 화살표 함수로 작성된 함수 표현식을 function 키워드를 사용해서 표현해봅시다.

클로저 함수는 위와 비슷한 모양을 갖고 있습니다. 이를 통해 알아볼 수 있는 첫번째 특징은, 클로저 함수는 "함수를 리턴하는 함수" 라는 점입니다. 함수를 리턴하는 함수가 클로저의 형태를 만듭니다.

한편, 클로저는 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분됩니다. 클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(closure; 폐쇄) 데에 있습니다. 따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요합니다.

앞서 본 예제에서는 변수 x와 y가 선언된 곳이 각기 다릅니다. x가 선언된 함수는 바깥쪽에 있으니 '외부 함수'라고 부릅시다.

그리고 y가 선언된 함수는 보다 안쪽에 있으니 '내부 함수'라고 부릅시다. 따라서, 이 클로저 함수는 스코프가 분리되어 있습니다.

  • Q. 외부 함수는 y에 접근이 가능한가요?
    → no, 바깥 스코프에서는 안쪽 스코프로의 접근이 불가능합니다.
  • Q. 내부 함수는 x에 접근이 가능한가요?
    → yes, 안쪽 스코프는 바깥 스코프에서 선언된 변수에 접근이 가능합니다.

이를 통해 알아볼 수 있는 클로저 함수의 두번째 특징은 "내부 함수는 외부 함수에 선언된 변수에 접근 가능하다"는 점입니다.

일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없습니다. 이와 다르게, 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장됩니다. (어휘적 환경을 메모리에 저장하기 때문에 가능한 일입니다)

변수 add5 에는 클로저를 통해 리턴한 함수가 담겨 있습니다. add5 는 재미있게도, adder함수에서 인자로 넘긴 5라는 값을 x 변수에 계속 담은 채로 남아있습니다. 외부 함수의 실행이 끝났음에도 말이죠!

따라서 add5함수는 다음과 같이 사용할 수 있습니다.

별 거 아닌것 처럼 보이지만, 보다 실용적인 사용도 가능합니다.

다음은 클로저를 이용해 HTML 문자열을 만드는 예제입니다.

예제에서 divMaker 함수는 'div'라는 문자열을 tag 라는 변수에 담아두고 있으며, anchorMaker 함수는 'a'라는 문자열을 tag에 담아두고 있습니다.

클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 해줍니다.

클로저를 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만듭니다.

makeCounter를 실행하여 변수에 담아봅시다.

makeCounter 함수는 increase, decrease, getValue메소드를 포함한 객체 하나를 리턴합니다. 따라서, counter1은 객체입니다.

'외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'는 규칙에 의해, 어떤 경우에도 value는 직접 수정이 불가능합니다. 대신, 리턴하는 객체가 제공하는 메소드를 통해 value 값을 간접적으로 조작할 수 있습니다.

이것이 바로 정보의 접근 제한 (캡슐화) 입니다.

왜 이렇게 하는 것일까요? 만일 스코프로 value 값을 감싸지 않았더라면, value 값은 전역 변수여야만 했을 것입니다. 하지만 makeCounter라는 함수가 value 값을 보존하고 있기 때문에, 전역 변수로 따로 만들 필요가 없습니다.

전역 변수가 좋지 않은 이유는, 전역 변수는 다른 함수 혹은 로직 등에 의해 의도되지 않은 변경을 초래하기 때문입니다. 이를 side effect라고 합니다. side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있습니다. 따라서 이에 따른 오류로부터 보다 안전하게 값을 보호할 수 있습니다.

클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있습니다.

makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 됩니다. 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고, 각각의 값을 보존할 수 있습니다.

이와 같이 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 합니다.

클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있습니다. 즉, 클로저는 모듈화에 유리합니다.

0개의 댓글