Python 클로저: JavaScript에서 온 경우

발행: (2026년 1월 20일 오후 10:00 GMT+9)
10 min read
원문: Dev.to

Source: Dev.to

위에 제공된 소스 링크 외에 번역할 텍스트를 알려주시면 한국어로 번역해 드리겠습니다.

Source:

책으로 배우기 vs. 영상

“책 – 주제에 대한 깊이 있는 지식 습득
영상 – 특정 기술을 빠르게 익히기”

새로운 것을 배우고 싶을 때면 여전히 책을 집어 듭니다. 오늘날의 문서, Udemy 강좌, 그리고 기타 영상 자료들이 매우 귀중하지만, 텍스트(디지털이든 종이든) 한 장에 담긴 느낌이 더 영구적인 것 같습니다.

올해의 목표 중 하나는 Python 실력을 끌어올려 AI/ML 솔루션을 보다 편하게 만들 수 있게 하는 것입니다. 그 여정의 핵심 부분은 Python의 클로저(closures) 를 이해하는 것이었습니다.

*아래 대부분의 자료는 **Fluent Python (2nd Ed.)**와 **Python Cookbook (3rd Ed.)*에서 발췌한 것으로, 실용적이고 흥미로운 Python 안내서로 강력히 추천합니다.

클로저가 중요한 이유

  • 데코레이터 – 파이썬 데코레이터는 클로저를 기반으로 합니다(자세한 내용은 나중에).
  • 콜백 – 자바스크립트 콜백과 마찬가지로, 클로저는 상태를 “기억”하도록 합니다.
  • 팩토리 함수 – 즉석에서 특수화된 함수를 생성합니다(예: 곱셈기).
  • 데이터 캡슐화 – 클래스를 사용하지 않고 내부 상태를 숨깁니다.

Flask에서 @app.route()를 작성한 적이 있다면, 이미 클로저를 사용한 것입니다—비록 인식하지 못했더라도.

클로저 정의

외부(상위) 함수의 스코프에 있는 변수를 기억하고 접근할 수 있는 중첩 함수로, 외부 함수가 실행을 마친 후에도 내부 함수가 상태를 유지할 수 있게 합니다.

기본 예제: 승수기

Python 구현 (mult_closure.py)

def make_multiplier_of(n):
    """Outer function that takes a factor `n` and returns a closure."""
    
    def multiplier(x):
        """Inner function (closure) that uses the remembered factor `n`."""
        return x * n
    
    return multiplier

# Create two closures, each remembering a different `n` value
doubler = make_multiplier_of(2)   # remembers n = 2
tripler = make_multiplier_of(3)   # remembers n = 3

# Results – the scalar is remembered, so we only pass the value to be multiplied
print(f"8 times 2 is {doubler(8)}")
print(f"4 times 3 is {tripler(4)}")

Output

8 times 2 is 16
4 times 3 is 12

설명: 외부 함수가 중첩된 multiplier 함수를 반환하며, 이 함수는 n 값을 기억합니다.

호출 방식 시각화

# Saved to a variable
doubler = make_multiplier_of(2)
doubler(4)

# Executed in‑place
make_multiplier_of(2)(4)

Source:

JavaScript에서 동일한 아이디어

JavaScript 구현 (mult_closure.js)

function makeMultiplierOf(n) {
    // Outer function that takes a factor `n` and returns a closure
    function multiplier(x) {
        // Inner function (closure) that uses the remembered factor `n`
        return x * n;
    }
    return multiplier;
}

// Instances of our new closure
const doubler = makeMultiplierOf(2);
const tripler = makeMultiplierOf(3);

// Results – the closure remembers the initial value it was passed
console.log(`8 times 2 is ${doubler(8)}`);
console.log(`4 times 3 is ${tripler(4)}`);

호출 간 상태 유지

클로저는 호출 사이에 지속되는 가변 데이터를 보유할 수 있습니다. 아래 예시는 외부 함수의 스코프에 보관된 리스트에 메시지를 저장하는 로거입니다.

Python 로거 클로저 (logger_closure.py)

def create_logger(source):
    """Outer function that holds a list of logs for a given `source`."""
    logs = []

    def log_message(message=None):
        """
        Inner function that appends a message (if provided) and always returns
        the current list of logs.
        """
        if message:
            logs.append({"source": source, "message": message})
        return logs

    return log_message

# Two independent loggers
error_log = create_logger("error")
info_log  = create_logger("info")

# Log some messages
info_log("Hello world")
error_log("File Not Found")

# Retrieve logs
print(error_log("Zero Division"))
print(info_log())

출력

[{'source': 'error', 'message': 'File Not Found'},
 {'source': 'error', 'message': 'Zero Division'}]
[{'source': 'info', 'message': 'Hello world'}]

핵심 포인트: create_logger를 호출할 때마다 고유한 logs 리스트가 생성됩니다. error_loginfo_log는 동일한 클로저 구조를 공유하지만, 각각 별개의 상태를 유지합니다.

파이썬의 변수 스코핑 vs. 자바스크립트

  • 자바스크립트 (ES6 이전)var에 의존했으며, 함수 스코프를 신중히 다뤄야 했습니다. 현대 JS는 letconst를 사용해 블록 스코프 변수를 생성합니다.
  • 파이썬let/const와 직접 대응되는 것이 없습니다. 대신 LEGB 규칙(Local → Enclosing → Global → Built‑in)을 따라 이름을 해석합니다. 이 규칙을 이해하는 것이 클로저를 사용할 때 필수적입니다.

TL;DR

  • 클로저는 중첩 함수가 자신을 둘러싼 스코프의 값을 기억하도록 합니다.
  • 클로저는 데코레이터, 콜백, 팩토리 함수, 그리고 간단한 상태를 가진 객체의 핵심입니다.
  • 파이썬의 렉시컬 스코핑(LEGB)은 규칙을 파악하면 클로저를 직관적으로 사용할 수 있게 해줍니다.

행복한 코딩 되세요! 🚀

Example 4 – legb_overview.py: LEGB 규칙의 시각적 표현

"""Global scope"""
x = "global"

def outer():
    """Enclosing scope"""
    y = "enclosing"

    def inner():
        """Local scope"""
        z = "local"   # Local scope

        # Python searches: Local → Enclosing → Global → Built‑in
        print(z)      # Found in Local
        print(y)      # Not in Local, found in Enclosing
        print(x)      # Not in Local/Enclosing, found in Global
        print(len)    # Not in Local/Enclosing/Global, found in Built‑in

    inner()

outer()

Output

local
enclosing
global

How the LEGB search works

ScopeDescription
Local인터프리터는 먼저 현재 함수의 로컬 네임스페이스를 확인합니다.
Enclosing함수가 중첩된 경우, 파이썬은 한 단계 위의 감싸는 함수 네임스페이스를 살펴봅니다(클로저에서 많이 사용됨).
Global다음으로 모듈 수준 네임스페이스(파일 상단에 정의되었거나 임포트된 변수)를 확인합니다.
Built‑in마지막으로 파이썬 내장 네임스페이스(예: len, print)를 확인합니다.

Important: 파이썬은 일치하는 이름을 찾는 즉시 검색을 중단합니다. 일치 항목을 찾은 후에는 외부 스코프로 계속 탐색하지 않습니다.

nonlocal 키워드

예제 5 – make_avg_err.py: nonlocal 없이 클로저 만들기

"""Before"""
def make_avg():
    count = 0
    total = 0

    def inner(new_val):
        count += 1          # `nonlocal`은 (전역이 아닌) 상위 스코프에 있는 변수를 **재바인딩**해야 할 때 필요합니다. `int`, `float`, `str` 등과 같은 불변 객체에 적용됩니다.

가변 객체(예: 리스트)의 경우, 이름 자체를 재바인딩하지 않기 때문에 nonlocal 없이도 객체의 내용을 수정할 수 있습니다(my_list.append(item)).

JavaScript 개발자를 위한 간단한 메모

JavaScript에서 온 경우, 같은 패턴을 파이썬에 그대로 적용하고 싶을 수 있습니다. 하지만 파이썬의 스코프 규칙—특히 클로저와 nonlocal 키워드와 관련된 부분—은 다르게 동작합니다. 이러한 미묘한 차이를 이해하면 은밀한 버그를 피하고 코드를 보다 “파이썬답게” 만들 수 있습니다.

다음은?

저는 현재 Python의 효율성에 기여하는 많은 “작은 것들”을 탐구하기 위해 AST File Parser를 만들고 있습니다. 목표는 이번 달 말까지 프로젝트를 마무리하고, 그 후에는 다음과 같은 다른 Python 주제로 파고들 예정입니다:

  • 제너레이터
  • 데코레이터
  • 특수(매직) 메서드

이 개념들에 대해 논의하고 싶으시면 언제든지 연락 주세요!

Back to Blog

관련 글

더 보기 »

핸즈온 실습: Amazon Personalize

!Amazon Personalize https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16ui0v8xk3zc0lybrveg.png 간략한 개요 Amazon Personalize는 기업이 …

Rapg: TUI 기반 시크릿 매니저

우리 모두 그런 경험을 해봤습니다. 새로운 프로젝트에 참여하면 가장 먼저 듣게 되는 말이 있습니다: > “Slack에서 고정된 메시지를 확인해서 .env 파일을 찾아보세요.” 또는 .env 파일이 여러 개 있는 경우…