어쩌다보니 iOS 개발자

클로저와 렉시컬 스코프란? 본문

React Native 개발

클로저와 렉시컬 스코프란?

엔디엘(no Dream no Life) 2026. 2. 13. 12:26

1) 스코프와 “외부 스코프”

 

  • 스코프(scope): 변수를 “어디서 접근할 수 있는지”의 범위
  • 외부 스코프(outer scope): 지금 함수 기준으로 바깥에 있는 스코프
function outer() {
  let x = 10;
  function inner() {
    console.log(x); // inner 입장에선 x가 "외부 스코프 변수"
  }
}

 


 

2) 렉시컬 스코프(Lexical Scope)란?

 

한 줄로:

 

함수의 스코프(외부 변수 참조 기준)는 “호출 위치”가 아니라 “작성 위치”로 결정된다.

 

예시:

let a = "global";

function test() {
  console.log(a);
}

function run() {
  let a = "run";
  test();
}

run(); // "global"

"run"이 아니라 "global"?

 

  • test전역에서 작성
  • 그래서 test가 변수를 찾을 때 기준이 되는 바깥은 전역 스코프로 “고정”
  • 호출을 run에서 했든 어디서 했든 상관 없음
  • → 이 규칙이 렉시컬 스코프

 


 

3) 클로저(Closure)란?

 

클로저는 “특별한 문법”이 아니라 현상/특성이야.

 

한 줄 정의:

 

함수가 자신이 작성된 위치의 “외부 스코프(렉시컬 환경)”를 기억(참조 유지)해서, 나중에도 그 변수에 접근할 수 있는 것.

 

핵심은 함수 자체를 기억한다가 아니라

그 함수가 만들어질 당시의 “변수 환경(렉시컬 환경)”을 참조한다는 점.

 


 

4) 클로저 원리(왜 outer가 끝났는데 변수는 살아있나)

 

대표 예:

function outer() {
  let x = 10;

  return function inner() {
    console.log(x);
  };
}

const f = outer();
f(); // 10

여기서 일어나는 일:

 

  1. outer() 실행 → x=10이 들어있는 “환경(렉시컬 환경)” 생성
  2. inner 함수 생성
    • 이때 inner는 내부적으로 [[Environment]] 같은 슬롯에
    • “내가 작성된 위치의 바깥 환경(outer의 환경)”을 참조로 저장
  3.  
  4. outer 실행이 끝나도
    • inner가 그 환경을 계속 참조하고 있으니
    • GC가 지우지 못함
  5.  
  6. 그래서 f() (= inner 호출) 시에도 x를 찾을 수 있음

 

즉:

 

“함수(inner)가 외부 환경을 참조하고 있어서 그 환경이 살아남는 것”
(outer 함수를 붙잡는 게 아님)

 


 

5) f1 / f2 예시가 말해주는 핵심

function outer() {
  let x = 0;
  return function () {
    x++;
    console.log(x);
  };
}

const f1 = outer();
const f2 = outer();

f1(); // 1
f1(); // 2
f2(); // 1

왜 f2는 1부터 시작해?

 

  • outer()는 호출될 때마다 새 렉시컬 환경을 만든다
  • f1은 환경 A의 x를, f2는 환경 B의 x를 잡고 있음
  • → 값이 같아 보여도 “서로 다른 변수 공간”을 잡고 있는 거야

 


 

6) 왜 클로저를 사용하는가?

 

클로저는 실전에서 엄청 많이 쓰여.

 

 

(1) 상태 유지 (state 유지)

function makeCounter() {
  let c = 0;
  return () => ++c;
}
const counter = makeCounter();
counter(); // 1
counter(); // 2

함수 호출이 끝나도 c가 유지됨.

 

 

(2) 데이터 은닉(캡슐화, private 변수)

function createUser() {
  let password = "secret";
  return {
    check(p) { return p === password; }
  };
}

외부에서 password 직접 접근 불가.

 

 

(3) 함수 팩토리(설정값 고정한 함수 만들기)

const multiply = x => y => x * y;
const double = multiply(2);
double(5); // 10

 

(4) 콜백/비동기에서 값 보존

 

이벤트 핸들러, setTimeout, Promise 콜백 등에서 “그때의 문맥”을 유지.

 


 

7) 렉시컬 스코프 ↔ 클로저 관계 한 문장

 

렉시컬 스코프는 “바깥이 누구인지(작성 위치 기준)”를 고정하는 규칙이고,
그 고정된 바깥 환경을 함수가 계속 참조해서 살아남는 현상이 클로저다.

 

 

  • 렉시컬 스코프(규칙) 때문에
  • 함수는 “작성된 위치의 외부 환경”을 기준으로 변수를 찾고
  • 그 환경 참조가 남아 있으면
  • → 클로저가 된다

 


 

8) React Native/React에서 특히 중요한 이유(짧게)

 

  • RN은 JS Thread가 바쁘면 UI도 영향을 받음
  • 클로저는 많이 쓰이지만 stale closure(오래된 상태를 캡처) 같은 버그가 생기기 쉬움
  • (예: useEffect, setTimeout 콜백에서 과거 state를 보는 문제)

 

 

Comments