[ JavaScript ] 클로저(Closure)

exceed_96·2024년 2월 2일

JavaScript

목록 보기
7/18
post-thumbnail

JavaScript에 대한 개념을 공부하다 보면 클로저(Closure)라는 개념에 대해서 한 두번은 들어봤을 것이다. 대체 클로저란 무엇이고 어떤식으로 동작하는지 알아보자.

클로저를 쉽게 이해하기 위해서는 실행컨텍스트(Excutation Context)개념에 대해서 먼저 알아두고 공부하는게 좋다.
실행컨텍스트(Excutation Context)란 무엇일까?



1. 클로저(Closure)란 무엇일까?

클로저는 자식 함수에서 부모 함수의 Scope에 대한 접근을 제공하는 개념이다.

사실 위 설명만 보면 어떤 말인지 이해가 가기 힘들다.

예시를 통해서 어떤 개념인지 알아보자



2. 클로저(Closure) 예시

2.1 클로저 예시1

function func1() {
  function func2() {
    function func3() {
      let shape = "squre";
      console.log(fruit, color, shape);
    }
    let color = "red";
    func3();
  }
  let fruit = "banana";
  func2();
}

func1();

위와같이 함수 안에 함수를 정의하면 자식함수에서 부모함수에 정의한 변수에 접근이 가능할까?

위와같이 자식함수에서 부모함수에 있는 변수에 접근이 가능하다.

그럼 어떻게 자신의 Scope에 있는 변수가 아니고 인수로 전달해준것도 아닌데 어떻게 접근이 가능한걸까?

해답은 클로저(Closure)에 있다.

개발자 도구 -> "Sources"항목에 가서 확인해보자
여기서 주의깊게 볼 점은 Scope영역에서Closure라는 값이 생기고 해당 정의로는 부모함수들의 Scope가 정의된걸 확인할 수 있다.

즉, 함수를 함수안에 적용하면 안에 있는 함수(자식함수)는 부모함수의 Scope에 접근할 수 있는 것이다.

여기서 중요한 점은 부모함수가 실행 컨텍스트에서 사라진 후에도 자식함수는 부모함수에 접근이 가능하다는 것이다.

여기서 접근이 가능 하다는건 거창한 의미는 아니고 해당 부모함수가 가지고 있는 Scope에 접근이 가능한 것이다.


위 예시는 클로저가 발생하는 예시를 살펴본 것이고 이제는 좀 쓸만한 클로저 예시를 알아보자



2.2 클로저 예시2

function addCalculator(initial) {
  function add(number) {
    return initial + number;
  }
  return add;
}

const addSample1 = addCalculator(1);
console.log(addSample1(1));
console.log(addSample1(2));

const addSample2 = addCalculator(2);
console.log(addSample2(1));
console.log(addSample2(2));

위 코드는 "addCalculator"함수에 초기값을 인수로 전달하고 해당 함수는 "add"를 반환하는 함수이다.

즉, 이 예시는 함수가 실행컨텍스트에서 사라지는 경우를 눈으로 플로우를 따라가도 보이는 케이스이다.

이 경우에서는 어떻게 동작할까?

위와같이 "addSample1"의 경우 초기값으로 정의해준 1이 전달되어서 2,3이 출력되었고

"addSample2"의 경우 초기값으로 정의해준 2가 전달되어서 3,4가 출력된걸 확인할 수 있다.

9번째 줄에서 break를 걸고 Scope를 확인해보면 Closure로 부모함수("addCalculator")가 정의되어 있는걸 확인할 수 있다.

즉, 실질적으로 "addCalculator"함수는 실행 컨텍스트에서 종료되었지만 Scope는 메모리에 남아 자식함수에서 접근이 가능한 것이다.

자식함수가 만들어지는 시점에 해당 함수의 부모함수가 가지고 있는 Scope를 자식함수가 동봉해서 가지고 있는 것이다.

그래서 언제든지 호출하면 이에 접근할 수 있는게 클로저(Closure)인 것이다.



3. 클로저 어떤경우에 사용해야 하는걸까?

JavaScript는 기본적으로 캡슐화데이터 은닉을 제공하지 않는다.

하지만 클로저를 사용한다면 이를 비슷하게 구현할 수 있다.

function counter() {
    let count = 0;

    return function() {
        count++;
        console.log(count);
    };
}

const myCounter = counter();
myCounter(); // 1
myCounter(); // 2

위와같이 "count"변수를 외부전역에 선언하는게 아닌 함수 내부에 선언해서 변수를 보존하고 외부의 접근을 제한할 수 있다는 장점이 있다.

만약, 하나의 프로젝트에서 "count"변수를 전역으로 지정해주고 그 위, 아래로 코드가 무수히 많다고 생각해보자.

그렇다면 자신이든 혹은 같이 프로젝트를 하는 타인에 의해서 해당 변수가 쉽게 조작할 수 있을 것이다.

이를 방지하기 위해서 위와같이 데이터를 은닉하고 캡슐화를 하는 것이다.


function createPerson(name) {
    let age = 0;

    return {
        getName: function() {
            return name;
        },
        getAge: function() {
            return age;
        },
        increaseAge: function() {
            age++;
        }
    };
}

const person = createPerson("John");
console.log(person.getName()); // John
console.log(person.getAge());  // 0
person.increaseAge();
console.log(person.getAge());  // 1

또한 클로저를 이용하면 클래스의 private변수처럼 객체의 일부를 외부에 공개하지 않고 메서드를 반환할 수 있다.



4. 클로저 사용 시 주의할 점

실행 컨텍스트가 종료되어도 해당 부모함수 실행 컨텍스트 Scope에 접근할 수 있다는건 해당 Scope가 어딘가에 남아있으니까 가능한것이다.

그럼 어디에 남아있을까?

바로 메모리다.

클로저Lexical Scope를 유지하므로, 해당 클로저가 종료되지 않는 이상 메모리에 계속 남아있게 된다.

즉 메모리 누수가 날 수 있는 것이다.

따라서 클로저의 사용은 적절하게 제거해줘야 한다.



5. 마치며

사실 클로저라는 개념은 모든 프로그래밍 언어를 통틀어 봐도 일반적인 사용법은 아니라고 생각한다.

JavaScript에서는 자유롭게 사용 가능하지만 다른언어에서 외부 변수를 내부로 가져온다는건 쉽게 생각할 내용이 아니라고 생각한다.

클로저를 잘 이용하면 아주 강력한 무기가 될거 같은데 이 무기를 코드에 휘두르는게 아닌 내 자신한테 휘둘러서 문제를 발생시키는 모습을 보면 앞으로 클로저에 대해 깊은 고민과 사용을 반복적으로 해야할거 같다.



6. Reference

https://www.youtube.com/watch?v=bwwaSwf7vkE
https://www.youtube.com/watch?v=LL0DGc5pg7A
https://1yoouoo.tistory.com/28
https://lulu-developmentlog.tistory.com/263

profile
개발진행형

0개의 댓글