프로그래머스 데브코스 프론트엔드
📝 1일차 공부 내용 정리 노션
TIL 작성 후 클로저에 대한 추가적인 공부가 필요한 것 같아 정리하였습니다.


클로저(Closure)란?

MDN 문서에 따르면 함수와 함수가 선언된 어휘적 환경(lexical environment)의 조합이다.
무슨 말인지 이해가 안 될 것이다. 조금 단순하고 가볍게 이해를 시작해보자.

자바스크립트는 함수 안에서 또 다른 함수를 선언할 수 있는데 이를 내부 함수라고 한다.

function outer() {
    function inner(){
        var title = 'coding everybody'; 
        alert(title);
    }
    inner();
}
outer();
}

위 코드에서 함수 outer의 내부에는 함수 inner가 정의되어 있다. 함수 inner를 내부함수라고 한다.

이제 주목할 점은 내부함수가 외부함수의 지역변수에 접근할 수 있다는 점이다.

function outer() {
    var title = 'coding everybody';  
    function inner(){        
        alert(title);
    }
    inner();
}
outer();
}

위 코드의 결과로 alert 창에 coding everybody가 출력된다.
이는 내부함수 inner에서 외부함수인 outer의 지역 변수에 접근할 수 있음을 보여준다.

내부함수는 외부함수의 지역변수에 접근할 수 있는데 외부함수의 실행이 끝나고 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있다. 이러한 메커니즘이 바로 클로저다.

function outer() {
    var title = 'coding everybody';  
    return function(){        
        alert(title);
    }
}
inner = outer();
inner();
}


이전 코드가 조금 변경되었다.
outer()가 호출되고 익명 함수가 변수 inner에 담긴다. inner()가 실행될 때 outer 함수는 실행이 끝났기 때문에 이 함수의 지역 변수인 title은 소멸될 것으로 생각할 수 있다. 하지만 alert 창에 coding everybody가 출력된 것은 외부함수의 지역변수 title이 소멸되지 않은 것을 의미한다.

클로저는 내부함수가 외부함수의 지역변수에 접근할 수 있고, 외부함수가 이미 반환되었어도 외부함수 내의 변수는 이를 필요로 하는 내부함수가 하나 이상 존재하는 경우 계속 유지된다.

 

Lexical environment (어휘적 환경)

이제 클로저의 개념에 대해 어느 정도 알 것 같으니 아래 문장이 무슨 뜻인지 이해해 보자.

클로저는 함수와 함수가 선언된 어휘적 환경(lexical environment)의 조합이다.

어휘적 환경(lexical environment)을 알기위해서는 실행 컨텍스트(execution context)에 대해 알아야 하는데 실행 컨텍스트에 대해 잘 모른다면 실행 컨텍스트에 대한 글을 읽고 오자.

위 글을 읽었다면 이제 Lexical environment가 무엇인지 알았을 것이다.

간단히 정리하자면 함수가 호출 될 때 함수의 실행 컨텍스트가 생성됐다가 실행이 끝나면 실행 컨텍스트가 종료된다. 그리고 이 실행 컨텍스트가 생성될 때 함수의 lexical environment도 생성된다. lexical environment에는 함수의 지역변수 정보들과 이 함수의 상위 스코프에 대한 정보가 들어있다.

 


글로 잘 이해가 안된다면 코딩앙마의
자바스크립트 중급 강좌 #11을 보는 것을 추천한다. lexical environment에 대해 더 잘 이해할 수 있을 것이다.

 

다음 예제를 한 번 보자.

function makeAdder(x) {
    return function(y){
        return x + y;
    }
}
const add3 = makeAdder(3);
console.log(add3(2)); // 5

const add10 = makeAdder(10);
console.log(add10(5)); // 15
console.log(add3(1)); // 4
}

1. 최초 실행시 makeAdder 함수와 add3은 전역 Lexical 환경에 들어간다.

전역 Lexical 환경
makeAdder: function
add3: 초기화 x

2. const add3 = makeAdder(3);

위 라인이 실행될 때 makeAdder가 실행되고 Lexical 환경이 만들어진다. 전달 받은 x의 값이 들어간다.

makeAdder Lexical 환경
x: 3

함수의 Lexical 환경에는 넘겨 받은 매개변수와 지역변수들이 저장된다. 

전역 Lexical 환경의 add3은 함수가 실행됐으니 리턴하는 함수가 된다.

전역 Lexical 환경
makeAdder: function
add3: function

3. console.log(add3(2));

이 라인이 실행되면 function(y) { return x + y; } 이 함수가 실행되는데 이때 또 Lexical 환경이 만들어진다.

익명 함수 Lexical 환경
y: 2

이제 x+y를 해야 하는데 처음에는 익명 함수 Lexical 환경에서 x를 찾는다. y는 있지만 x가 없으므로 참조하는 외부 Lexical 환경으로 가서 x를 찾는다. 

 

정리하면 makeAdder의 내부함수는 y를 가지고 있고 상위 함수인 makeAdder의 매개변수 x에 접근할 수 있다. 

add3 함수가 생성된 이후에도 상위 함수인 makeAdder의 x에 접근 가능하다. 

이런 것을 클로저(Closure), 함수와 그 함수의 렉시컬 환경의 조합이라고 한다.

 

const add10 = makeAdder(10);라인에서 makeAdder(10)이 호출되어도 add3에는 아무런 변화도 없다.

add10과 add3은 서로 다른 환경을 가지고 있는 것이다.

 

클로저의 필요성 - 은닉화

이러한 클로저는 정보의 은닉화, 상태 유지, 전역 변수 사용 억제 등의 장점이 있다.

정보의 은닉화에 대한 예시 코드를 보자.

function makeCounter() {
	let num = 0;
    
    return function (){
        return num++;
    };
}
let counter = makeCounter();

console.log(counter()); // 0
console.log(counter()); // 1
console.log(counter()); // 2
}

counter에는 makeCounter 함수가 리턴하는 익명 함수가 들어간다.

익명 함수는 숫자를 반환하는데 이 숫자는 외부함수의 변수이다.

 

실행하면 0, 1, 2로 값이 증가하는 것을 볼 수 있다.

내부함수에서 외부함수의 변수 num에 접근하고 있다. 생성된 이후에도 계속 기억하고 있다.

 

이 숫자들은 수정하는 것이 불가능하고 오직 counter를 증가시키고 반환받는다.

이게 바로 은닉화다. 갑자기 num을 99로 바꾸거나 100씩 증가시키는 것은 불가능하다.

 

착각하기 쉬운 클로저

다만 클로저를 사용할 때 오해하지 말아야 할 점은 아래 코드처럼 함수를 반환하는 것 자체가 클로저인 것이 아니다.

function outer() {
    let name = 'kyle';
    if (true) {
      let city = 'seoul';
      return function inner() {
        console.log(city);
      };
    }
}

클로저는 내부에 선언된 함수가 외부함수의 지역변수를 사용했을 때만 클로저라고 선언된다.
inner 함수에 클로저를 사용하고 싶으면 아래와 같이 name변수를 사용해주면 된다.

function outer() {
    let name = 'kyle';
    if (true) {
      let city = 'seoul';
      return function inner() {
        console.log(city);
       console.log(name);
      };
    }
}

 

정리

클로저함수와 그 함수의 렉시컬 환경의 조합을 의미한다.
함수가 생성될 당시의 외부 변수를 기억하고 생성 이후에도 그 변수에 계속 접근이 가능하다. 외부 함수의 실행이 끝나 소멸된 이후에도 내부 함수가 외부 함수의 변수에 접근 가능하다.


잘못된 점이 있다면 알려주시면 감사하겠습니다.

 

참고 자료

 

복사했습니다!