클로저(closure)

oYJo·2025년 4월 1일

JavaScript

목록 보기
49/52

클로저

  1. https://ko.javascript.info/closure
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

✔️식별자(Identifier) vs 변수(Variable)

  • 식별자 : 변수 포함해서 함수, 클래스, 객체 속성 등을 이름으로 구별하는 요소
    = 변수, 함수, 클래스 등을 식별하는 이름 자체
  • 변수 : 데이터를 저장하는 메모리 공간

클로저 : 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우, A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상

  • 내부 함수가 외부 함수의 변수를 기억하여 사용할 수 있는 기능
  • 함수가 생성될 때 환경 기억하고 생성된 이후에도 환경에 접근할 수 있는 기능을 의미
    = 함수 + 함수가 선언될 당시의 환경
function countWithoutClosure(){
	let count = 0;
	
	return count;
}

console.log(countWithoutClosure()); // 0
console.log(count); 
// ReferenceError : count is not defined
  1. 함수가 실행되면, 함수 내부에 선언된 변수는 메모리 상에 올라간다 let count = 0;
  2. return문 만나면 함수 종료, 가비지 컬렉터가 let count = 0 회수한다
  3. 변수에 접근 가능하려면, 변수가 메모리 상에 존재해야 하는데 함수 밖에서는 이미 변수가 제거되어서 참조할 수 없다

❗️함수 바깥에서도 실시간 확인하고 싶다면?
1. let count = 0 을 전역으로 뺀다
→ 하지만 전역 변수는 어플리케이션에 계속 남아있기에 메모리 누수를 초래한다, 가독성도 낮다
2. 클로저 사용
함수 선언문 바깥에서도 스코프 접근 가능하다

    function countWithClosure(){
      let count = 0;
      return{
        increase : functIOn(){
        count++;
        return count;
      },
        decrease : function(){
          count--;
          return count;
        },
          getCount : function(){
            return count;
          },
    };
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increase();
console.log(counter.getCount()); // 1
counter.decrese();
console.log(counter.getCount()); // 0

counter 변수에 createCounter() 결과 저장
increase(), decrease(), getCount() 호출

❓함수 안에 또 함수를 return 하는 이유?
❓클로저 함수를 호출해서 꼭 변수에 할당하는 이유?
❓함수 바깥에서 스코프에 접근한다는 의미?

클로저 함수는 함수를 return
return count가 아니라 return function increase ~~
근데 return function increase ~~ 안을 보면

count++;
return count;

즉, 상위 스코프 count 변수가 메모리 상에 없으면 실행할 수 없는 함수

클로저에 의해 return 될 때
함수 실행에 필요한 상위 스코프도 함께 메모리로 가져오는 것 countWithClosure()
리액트가 이걸 대신 만들어주는 것(내부적으로)

❓왜 자바스크립트에서는 모든 함수가 클로저인가?
[[Environment]] 프로퍼티와 렉시컬 환경

❗️몇몇 언어에서는 없기도 하는데 자바스크립트에서는 클로저가 된다
BUT 예외 존재
new Function 문법
https://ko.javascript.info/new-function


클로저 조건

  1. 함수 안에 또 다른 함수가 선언되어야 한다
  2. 내부 함수가 외부 함수의 변수를 참조하고 있어야 한다
  3. 외부 함수의 실행이 끝난 후에도 내부 함수가 반환되어 사용된다

= 데이터를 해당 데이터에서 작동하는 함수와 연관시킬 수 있다

❗️자바스크립트는 결국 코드 대부분 이벤트 기반
→ 사용자가 이벤트를 발생시키면 코드는 콜백(이벤트에 대한 응답으로 실행되는 단일 함수)으로 연결된다

자바에는 메서드 private 선언이 가능 : 같은 클래스 내 다른 메서드에서만 호출 가능

하지만 예전 자바스크립트에는 private메서드 선언이 없기에 클로저를 사용한 것


클로저를 사용하여 개인 메서드 에뮬레이션(emulation)

✔️시뮬레이션(Simulation) vs 에뮬레이션(Emulation)
시뮬레이션(Simulation) : 완전 동일하게는 아닌, 비슷하게 일을 흉내낸다
에뮬레이션(Emulation) : 완전히 똑같은 방법으로 일을 흉내낸다

예제

function outerFunction(outerVariable) {
  return function innerFunction(innerVariable) {
      console.log(outerVariable, innerVariable);
  };
}

const closureFunc = outerFunction("Hello");
closureFunc("World");  // Hello, World

outerFunction이 실행된 후에도 innerFunctionouterVariable을 기억하고 있어, 이후에도 접근이 가능하다

목적

  1. 데이터 은닉과 캡슐화
    • 클로저를 사용하면 외부에서 직접 접근할 수 없는 변수를 유지할 수 있다 = 전역 변수를 줄이고, 보안성을 높이는 효과가 있다

      ❓왜 전역 변수 사용을 지양해야 하나?
      어느 디렉토리에서나 접근 가능하기 때문이다
      브라우저 : window / Node.js : global
      최상위 객체에 직접적인 조작을 가하면 유지보수 등에 문제 있다

  1. 상태 유지
    • 클로저를 사용하면 특정 상태를 저장하고 계속 사용할 수 있다 ex) 카운터 함수를 만들 때 유용하다
      function createCounter() {
          let count = 0;  // 상태를 유지하는 변수
          return function () {
              count++;
              console.log(count);
          };
      } 
      
      const counter = createCounter();
      counter();  // 1
      counter();  // 2
      counter();  // 3
      
      countcreateCounter()가 실행될 때 생성되었지만, 반환된 내부 함수에서 계속 참조할 수 있어 상태를 유지한다
  2. 이벤트 핸들러 & 비동기 프로그래밍(콜백함수)에서 활용

✔️콜백 함수, 프로미스(Promise)

✔️콜백 함수 : 비동기 처리를 위한 하나의 패턴
비동기 작업이 완료되었을 때 메인 스레드에서 실행할 함수
loadScript(script, callback)를 호출할 때, 함께 호출할 callback 함수가 준비되어 있어야 한다
loadScript를 호출하기 이전에 호출 결과로 무엇을 할지 미리 알고 있어야 한다


❗️콜백 헬(콜백 지옥) : 가독성이 안 좋은 코드, 여러 개의 비동기 처리를 한번에 처리하면서 생긴 문제
→ 해결방법 : 프로미스 등장

✔️프로미스(Promise) : ES6 비동기 처리를 위한 또 다른 패턴
언제 처리 할 지를 명시할 수 있어서 유용하다
프라미스를 이용하면 흐름이 자연스럽다
loadScript(script)로 스크립트를 읽고, 결과에 따라 그다음(.then)에 무엇을 할지에 대한 코드를 작성하면 된다

let promise = new Promise(function(resolve, reject) {
// executor (제작 코드, '가수')
});

장점

  1. 변수 은닉 (Encapsulation)
    • 외부에서 직접 접근할 수 없는 변수를 만들 수 있어 보안성이 향상된다
  2. 전역 변수 사용 최소화
    • 불필요한 전역 변수를 줄여 코드의 안정성을 높인다
  3. 상태 유지 가능
    • 함수 실행 이후에도 특정 데이터를 계속 유지할 수 있어 유용하다
  4. 비동기 처리에 활용 가능
    • JavaScript의 setTimeout, addEventListener 등에서 유용하게 사용된다

단점

  1. 메모리 누수 가능성
    • 클로저가 유지하는 변수는 메모리에 계속 남아 있기 때문에 불필요한 메모리 공간 차지할 수 있다
    • 해결 방법: 필요 없는 클로저를 null로 설정하여 제거한다
  2. 디버깅 어려움
    • 클로저 내부의 변수를 추적하는 것이 어려울 수 있다
  3. 과도한 사용 시 성능 저하
    • 클로저를 많이 사용하면 메모리 사용량 증가로 인해 성능 문제가 발생할 수 있다

✔️모듈(module)
여러 기능들에 관한 코드가 모여있는 하나의 파일

✔️일반 스크립트 vs 모듈
모듈은 항상 엄격모드(use strict) 모드로 실행된다
자기만의 스코프가 있다
동일 모듈이 여러 곳에서 사용되더라도 최초 호출 시 단 한 번만 실행된다

퀴즈

❓어디에서 클로저가 이루어질까?

function makeAdd(x) {
  return function(y){
    return x +y;
  };
}

const add5 = makeAdd(5);
const add10 = makeAdd(10);

console.log(add5(2));
profile
Hello! My Name is oYJo

0개의 댓글