모래블로그

[JavaScript] 클로저(Closure) 본문

Language/JavaScript

[JavaScript] 클로저(Closure)

별모래 2024. 3. 14. 14:11
728x90

 

클로저 (Closure)

클로저는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 말한다.


 

내부함수와 외부함수

function outer() {
    var title = 'closure';
    function inner() {
        alert(title); // closure
    }
    inner();
}
outer();

 

위의 예제를 보면 outer 이라는 함수 안에 inner 함수가 정의되어 있다.

여기서 outer 함수를 외부함수,  inner 함수를 내부함수라고 한다.

내부 함수는 외부함수 안에서만 유효하기 때문에 외부함수 밖에서 내부함수에 접근할 수 없다는 것이 기본 맥락이다.

 

위에서 더 살펴봐야할 부분은 변수 title 이 선언된 위치이다.

현재 내부함수는 inner 에서 title 변수를 사용하고 있는데, 내부함수에 선언된 title 변수가 존재하지 않으므로 자신을 둘러싼 외부함수에서 title 변수를 찾게되는 것이다. outer 함수에서 선언된 title 변수가 내부함수 inner의 title 값으로 쓰이게 된다.

 

즉, 내부함수에서 외부함수의 지역변수에 접근할 수 있다는 의미이고, 이러한 매커니즘을 클로저 라고 한다.

 

 

클로저의 특징

클로저는 독특한 특징을 가지고 있다.

바로 외부함수의 실행이 끝나서 외부함수가 소멸된 이후에도 내부함수가 외부함수의 변수에 접근할 수 있다는 점이다.

function outer() {
    var title = 'closure';
    return function() {
        alert(title); // closure
    }
}

var inner = outer();
inner();

 

위의 예제에서, outer 함수는 alert 을 띄우는 내부함수를 return 하고 있다.

8행에서 outer 함수를 호출하고, 그 결과가 변수 inner 에 담긴다.

9행에서 outer 함수는 실행이 끝났기 때문에 이 함수의 지역변수는 소멸되는 것이 자연스럽지만, inner 함수를 실행했을 때 closure 가 출력된다.

그리고 이것은 외부함수의 지역변수 title이 소멸되지 않았다는 것을 의미한다.

 

 

클로저 사용 예제

1. 정보 은닉화 효과 내기(private method)

function test(title) {
    return {
        get_title : function() {
            return title;
        },
        set_title : function(_title) {
            title = _title
        }
    }
}

var a = test('closure');
var b = test('javascript');

alert(b.get_title()); // javascript
alert(a.get_title()); // closure

 

똑같은 외부함수 test 를 공유하고 있는 a와  b 의 get_title 결과는 서로 각각 다르다. 그것은 외부함수가 실행될 때마다 새로운 지역변수를 포함하는 클로저가 생성되기 때문에 a, b는 서로 완전히 독립된 객체가 된다. 

set_title 함수를 이용하여 a의 title 값을 "클로저" 라고 변경하여도 b에는 아무런 영향이 가지 않고, b의 title 값은 그대로 "javascript" 가 된다.

 

각 클로저는 그들 고유 클로저의 변수를 참조하기 때문에 하나의 클로저에서 변수 값을 변경해도 다른 클로저의 값에는 영향을 주지 않는다.

 

이러한 코드 패턴의 장점은 외부에서 title 값이 무작위로 바뀌는 것을 방지할 수 있다는 점이다.

title 값을 변경하기 위해서는 각 클로저 버전에서 set_title 메소드를 통해서만 변경이 가능하기 때문이다.

 

자바스크립트는 기본적으로 private한 속성을 제공하지 않는다.

그래서 실제로 클로저가 private method 기능을 제공하는 것이 아니라, 클로저를 통해서 이러한 효과를 낼 수 있는 것이다.

 

 

2. 변경된 최신 상태를 적용하기 

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 100);
}

 

위의 코드는 100ms 뒤에 0부터 9까지 콘솔에 찍히는 코드를 작성한 것이다.

콘솔에는 0부터 9까지 찍힐 것이라고 예상했지만 결과를 보면 10이라는 숫자만 10번 찍힌다.

그 이유는 100ms 시간동안 이미 반복문이 종료되어 i가 10이 되었고, 이후 setTimeout 비동기 함수의 console.log(i)가 실행되었기 때문이다. 

 

 

위의 예제를 클로저를 이용하여 해결해보자.

for (var i = 0; i < 10; i++) {
    function outer(j) {
        setTimeout(function () {
            console.log(j);
        }, 100);
    }
    outer(i);
}

 

위의 코드는 내부함수가 생을 마감한 외부함수에 접근할 수 있다는 클로저의 특징을 이용하여, 콘솔 결과가 0부터 9까지 찍히는 것을 확인할 수 있다.

 

반복문이 실행될 때마다 outer 함수가 실행되고 있으며, 인자로 i 값을 전달하고 있다.

outer 함수 내부에는 setTimeout 함수가 있고, setTimeout 함수는 외부함수인 outer의 지역변수 (여기선 j)에 접근할 수 있다.

j는 outer 함수의 i 인자를 가지므로 각각의 j는 0부터 9가 된다는 것을 알 수 있다.

 

 

 


참조

https://haileychoi15.medium.com/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%81%B4%EB%A1%9C%EC%A0%80-closure-da20640cb95b

https://opentutorials.org/course/743/6544

 

728x90

'Language > JavaScript' 카테고리의 다른 글

[JavaScript] 프로토타입  (1) 2024.03.12
[JavaScript] ECMAScript와 JavaScript  (0) 2024.02.19
[JavaScript] Generator(제너레이터)  (0) 2024.02.16
[JavaScript] 동기, 비동기  (0) 2024.02.15
[JavaScript] 구조 분해 할당  (0) 2024.02.14