JS

[JavaScript] 실용적으로 공부하는 클로저(Closure)

Junly_21 2023. 9. 21. 14:02

클로저가 잘 이해가 되지 않는 와중에 (특히나 이해가 안되기보단 와닿지 않았다) 어떤 의의를 가진 것인지 잘 나타내는 영상이 있어서 참고하였다. 영상

let cnt = 0;
function cntPlus() {
  cnt = cnt + 1;
}

console.log(cnt); //0출력
cntPlus();
console.log(cnt); //1출력

cnt를 선언하고 cntPlus라는 함수를 작성했다.

 

 

 

그런데 코드를 이렇게 작성하고 작업하다보면 의도치 않게 다음과 같은 상황이 발생할 수 있다.

let cnt = 0;
function cntPlus() {
  cnt = cnt + 1;
}

console.log(cnt);
cntPlus();
console.log(cnt);

//수많은 코드들로 작업함
cnt = 100; //의도치 않게 이름이 겹치게 선언해버림
cntPlus();
console.log(cnt); //101로 바뀌어버린다

cnt라는 변수를 cntPlus라는 함수를 통해 1씩만 증가되게 하고싶었는데 의도치 않게 cnt값을 직접 접근하여 바꾸는 일이 발생해버릴 수 있다.

그렇기에 우리는 cnt라는 변수에 직접 접근할 수 없도록 closure라는 개념을 활용한 코드를 짜야 하는 것이다.

 

 

 

 

우선 함수로 한번 감싸 지역변수로 만들어준다.

 

function closure() {
  let cnt = 0;
  function cntPlus() {
    cnt = cnt + 1;
  }
}

//console.log(cnt); 접근 불가
closure.cntPlus();
console.log(closure.cnt);

이렇게 실행하면 되지 않을까? 싶었지만

closure 내부에서 선언된 cnt와 cntPlus는 전역에서는 접근이 불가능하다.

 

 

 

따라서 cnt변수를 외부에서 접근할 수 있도록, return을 활용하여 외부 스코프에 이를 저장시켜야한다.

 

function closure() {
  let cnt = 0;

  function cntPlus() {
    cnt = cnt + 1;
  }

  return {
    cntPlus
  };
}

let test = closure();
test.cntPlus();

이렇게 실행시 정상적으로 cnt는 1로 증가한다. 그런데 cnt를 console.log로 찍어서 확인하는것 역시 불가능한 상황이다.

 

 

 

여기서 똘똘한 사람은 '아 그럼 console.log도 closure내부의 다른 함수로 빼주고 리턴하면 되겠구나' 라고 생각할 수 있다.

 

function closure() {
  let cnt = 0;

  function cntShow() {
    console.log(cnt);
  }

  function cntPlus() {
    cnt = cnt + 1;
  }

  return {
    cntPlus,
    cntShow,
  };
}

let test = closure();
test.cntShow(); //0
test.cntPlus();
test.cntShow(); //1

이렇게 하면 우리가 클로저를 통해

건드리고 싶지 않은 변수는 일반적으로 접근하여 값을 고칠 수 없게 만드는

 은닉화 라는것을 실습할 수 있게 된다

 

 

 

추가)) JS활용 연습

방금 위의 코드를 익명함수를 활용해 변형시켜 볼 수도 있다.

 

function closure() {
  let cnt = 0;

  function cntPlus() {
    cnt = cnt + 1;
  }

  return {
    cntPPlus: cntPlus, //cntPPlus: cntPlus()로 수정하면 코드가 제대로 동작하지 않는다.
    cntShow: function () {
      //console.log(cnt);
      return cnt;
    },
  };
}

let test = closure();
console.log(test.cntShow()); //0
test.cntPPlus();
console.log(test.cntShow()); //1

주석처리된 cntPPlus: cntPlus()로 수정하면 코드가 제대로 동작하지 않는다.

 

  1. cntPlus는 함수를 나타내는 참조(reference)를 저장하는 속성(property)이어야 한다. cntPlus()는 함수를 호출한 결과를 반환하는 것이므로 함수 자체를 저장하는 것이 아니라 함수를 호출한 결과를 저장하게 된다.
  2. 함수를 호출한 결과를 저장하면, 나중에 해당 함수를 실행하려 할 때 문제가 발생한다. 객체의 속성으로 저장된 함수를 실행하려면 ()를 사용해야 하는데, 이때 cntPPlus()가 아닌 cntPPlus로 접근해야 한다.
  3. 그렇다고 test.cntPPlus를 실행해버리면 실제로 cntPlus을 실행하는게 아니라 그저 참조하는 것일 뿐임. ['add(1,2)'를 실행하는 것처럼 실행은 'cntPPlus()'로 해야함
  4. 따라서 cntPPlus: cntPlus로 함수 자체를 속성으로 저장하고, 필요할 때 test.cntPPlus()로 함수를 실행해야한다.