클로저(Closure) 이해하기: 함수가 스코프를 기억하는 이유

hogu__giriboy·2026년 3월 9일

기록

목록 보기
3/12

1. 클로저가 등장하는 이유

클로저는 갑자기 등장한 개념이 아니다.
보통 하나의 의문에서 시작된다.

다음 코드를 보자.

function outer() {
  const message = "hello";

  function inner() {
    console.log(message);
  }

  return inner;
}

const fn = outer();
fn();

코드의 실행 흐름을 보면 다음과 같다.

outer 실행
→ inner 반환
→ outer 종료
→ fn 실행

여기서 중요한 질문이 하나 생긴다.

outer() 함수는 이미 실행이 끝났다.
그런데 inner() 함수는 message 변수에 계속 접근한다.

즉 이런 의문이 생긴다.

이미 종료된 함수의 변수에 어떻게 접근할 수 있을까?

보통 함수가 끝나면
그 함수 내부의 변수도 사라지는 것이 정상이다.

하지만 위 코드에서는
message가 계속 유지된다.

이 현상을 설명하기 위해 등장하는 개념이 클로저(Closure)다.


2. 렉시컬 스코프와 클로저

클로저를 이해하려면 먼저 렉시컬 스코프를 떠올려야 한다.

JavaScript에서는
함수의 스코프가 호출 위치가 아니라 선언 위치에 의해 결정된다.

예제를 보자.

function outer() {
  const a = 10;

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

  inner();
}

outer();

여기서 inner() 함수는
outer() 함수 안에서 선언되어 있다.

그래서 inner()
자신이 선언된 스코프에 있는 변수 a에 접근할 수 있다.

이 규칙이 바로 이전 글에서 다뤘던 렉시컬 스코프다.

구조를 보면 이렇게 된다.

global scope
⎿ outer
    ⎿ inner

하위 스코프는
항상 상위 스코프의 변수에 접근할 수 있다.

여기까지는 사실 클로저가 아니다.

단순히 스코프 규칙일 뿐이며 이전 글의 복습에 가깝다.

클로저는 조금 다른 상황에서 등장한다.

외부 함수는 종료됐지만 내부 함수가 외부 변수를 계속 사용하는 상황이다.

즉 핵심 상황은 다음과 같다.

outer 함수는 이미 끝났지만
inner 함수는 여전히 outer의 변수를 사용한다.

이때 JavaScript는
외부 스코프를 그대로 유지한다.

이 상태를 클로저라고 한다.


3. 클로저의 개념

보통 클로저는 다음처럼 설명된다.

클로저는 함수와 그 함수가 선언될 당시의 렉시컬 스코프가 결합된 구조다.

즉 클로저는 단순히 함수 하나를 의미하는 것이 아니다.

구조적으로 보면 다음과 같다.

함수
+
함수가 선언될 당시의 스코프
=
클로저

이 말은 JavaScript에서 함수는 실행될 때만 동작하는 것이 아니라
자신이 선언된 환경을 기억한다는 의미다.

그래서 내부 함수는
자신이 선언된 위치의 스코프에 계속 접근할 수 있다.


4. 클로저의 동작 구조

이제 처음 던졌던 질문으로 다시 돌아가 보자.

function outer() {
  const message = "hello";

  function inner() {
    console.log(message);
  }

  return inner;
}

const fn = outer();
fn();

코드의 흐름은 다음과 같다.

outer 실행
→ inner 반환
→ outer 종료
→ fn 실행

일반적으로 함수가 끝나면
그 함수 내부 변수는 메모리에서 제거된다.

하지만 위 코드에서는
message가 계속 유지된다.

그 이유는 inner 함수가
message 변수에 계속 접근해야 하기 때문이다.

그래서 JavaScript 엔진은
outer의 렉시컬 환경을 바로 제거하지 않는다.

대신 inner 함수와 함께 유지한다.

구조적으로 보면 이렇게 된다.

inner 함수
+
outer의 스코프(message)

이처럼 함수와 외부 스코프가 함께 유지되는 상태
클로저(Closure)라고 한다.


5. 클로저 활용 패턴

클로저는 단순히 개념으로만 존재하는 것이 아니라
실제 코드에서 특정 패턴을 만들기 위해 자주 사용된다.

대표적인 활용 방식은 크게 두 가지다.

상태 유지 (State)

클로저를 사용하면 함수 내부 상태를 유지할 수 있다.

function counter() {
  let count = 0;

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

const increase = counter();

increase(); // 1
increase(); // 2
increase(); // 3

이 코드에서 중요한 점은 다음과 같다.

  • count 변수는 counter 함수 내부에 존재한다
  • 외부에서는 count직접 접근할 수 없다
  • 하지만 반환된 내부 함수는 count에 계속 접근할 수 있다

즉 클로저를 통해 함수 내부 상태가 계속 유지된다.

데이터 은닉 (Encapsulation)

클로저는 외부에서 직접 접근할 수 없는 데이터를 만드는 데도 사용된다.

function createUser(name) {
  let _name = name;

  return {
    getName() {
      return _name;
    },
  };
}

const user = createUser("Alice");
console.log(user.getName()); // Alice

여기서 _name 변수는

  • 외부에서 직접 접근할 수 없고
  • 내부 함수만 접근할 수 있다

이처럼 클로저는 데이터를 보호하는 구조를 만들 때도 활용된다.


6. 핵심 정리

  • 클로저는 함수와 선언 당시의 스코프가 결합된 구조
  • 함수는 자신이 선언된 환경을 기억한다
  • 내부 함수가 외부 변수를 사용하면 해당 스코프는 유지된다
  • 클로저는 상태 유지와 데이터 은닉을 구현할 때 활용된다

2개의 댓글

comment-user-thumbnail
2026년 3월 10일

함수 내부에서 선언된 변수는 함수 내부에서만 사용할 수 있다는 것은 알고 있었지만, 함수 실행이 끝나면 메모리에서도 사라진다는 점은 이번에 처음 알게 되었습니다. 어떻게 보면 당연한 개념일 수도 있지만, 덕분에 새롭게 이해하고 갑니다! 👍

답글 달기
comment-user-thumbnail
2026년 3월 10일

저도 클로저를 잘 모르겠어서 찾아보고 정리했는데 저만 헷갈린 게 아니구나 내심 안심(?)하고 갑니다. 클로저에 대해 다시 복습할 수 있었습니다. 좋은 글 잘 읽고 갑니다!

답글 달기