-
[Python] 파이썬 클로저(Closure)를 이해한다는 것IT 2026. 1. 21. 14:41

클로저(Closure)는 공부하다 보면 한 번쯤은 반드시 마주치게 되는 개념입니다. 하지만 많은 사람들에게 클로저는 "개념은 설명할 수 있는데, 막상 왜 필요한지는 잘 모르겠는" 상태로 남아 있곤 합니다. 데코레이터나 고차함수를 배우다 보면 자연스럽게 등장하지만, 그 자체로는 어딘가 애매하게 느껴지는 개념이기 때문입니다.
이제 클로저가 뭔지 알아보겠습니다.
함수가 끝났는데, 변수는 왜 살아 있을까?
클로저를 처음 접할 때 가장 많이 드는 의문은 이것입니다.
보통 우리는 함수가 끝나면 그 안에서 사용하던 지역 변수들도 함께 사라진다고 배워왔습니다. 실제로 대부분의 경우 그렇습니다.
def outer(): x = 10 print(x) outer()위 함수에서 x 는 outer 함수가 끝나는 순간 더 이상 접근할 수 없습니다. 이 흐름은 매우 자연스럽습니다.
그런데 파이썬에서는 아래와 같은 코드가 가능합니다.
def outer(): x = 10 def inner(): print(x) return inner func = outer() func()outer 함수는 이미 실행이 끝났는데도, inner 함수는 여전히 x 의 값을 기억하고 있습니다.
이때 바로 클로저가 만들어집니다.
즉, 중첩 함수인 inner가 외부 함수인 outer의 변수 x를 참조하기에 x변수와 inner의 환경을 저장하는 클로저가 동적으로 생성되었고, func() 가 실행될때는 해당 클로저는 참조하여 x의 값을 출력할 수 있는 것 입니다.
이 클로저는 func 변수에 outer 함수가 할당될 때 생성됩니다.
그렇다면 이 클로저는 대체 어디에 존재하는 걸까요?
이는 func 변수를 dir 함수로 출력해보면 알 수 있습니다.
print(dir(func)) ['annotations__', '__call__', '__class__', '__closure__', ...]클로저란 무엇일까?
좀 더 정확하게 말하자면, 클로저란 자신이 정의될 당시의 외부 환경을 기억하고 있는 함수를 의미합니다.
여기서 말하는 외부 환경이란, 함수가 선언될 때 접근할 수 있었던 변수들의 집합입니다.
중요한 점은, 이 변수들이 단순히 값으로 복사된 것이 아니라, 연결된 상태로 유지된다는 점입니다. 그래서 함수가 끝난 뒤에도 그 값을 계속 사용할 수 있습니다.
이 특성 덕분에 클로저는 마치 "자기만의 상태를 가진 함수" 처럼 보이게 됩니다.
클로저는 왜 필요했을까?
클로저가 필요한 이유를 이해하려면, 한 가지 상황을 떠올려 보는 것이 좋습니다.
"함수 내부의 상태를 외부에 노출하지 않으면서, 계속 유지하고 싶다"
클래스를 사용하면 이런 문제를 해결할 수 있습니다. 하지만 항상 클래스를 쓰는 것이 최선이 아닙니다. 아주 단순한 상태 하나를 위해 클래스를 만드는 것은 오히려 과할 수 있습니다.
이럴때 클로저는 굉장히 좋은 선택지가 됩니다.
def counter(): count = 0 def increase(): nonlocal count count += 1 return count return increase c = counter() print(c()) print(c()) print(c())이 코드에서 count는 외부에서 직접 접근할 수 없지만, increase 함수가 호출될 때마다 값이 유지됩니다. 클래스 없이도 상태를 가진 동작을 표현할 수 있는 것입니다.
nonlocal 키워드는 왜 필요할까?
위 예제에서 눈에 띄는 키워드가 하나 있습니다. 바로 nonlocal 입니다. 이 키워드는 많은 사람들이 클로저를 어려워하는 지점 중 하나이기도 합니다.
파이썬에서 함수 안에서 변수에 값을 할당하는 순간, 그 변수는 기본적으로 지역 변수로 취급됩니다. 따라서 외부 함수의 변수를 수정하려고 하면 에러가 발생합니다.
nonlocal 은 파이썬에게 이렇게 말하는 역할을 합니다.
" 이 변수는 새로운 지역 변수가 아니라, 바깥 함수에 있는 변수를 사용할겠다."
즉, 클로저가 단순히 값을 읽는 것에서 그치지 않고, 상태를 변경하려 할 때 필요한 선언이라고 이해하면 됩니다.
클로저는 언제 자연스럽게 등장할까?
클로저는 의도적으로 만들지 않아도, 파이썬 코드를 작성하다 보면 자연스럽게 등장하는 경우가 많습니다. 특히 고차원 함수와 사용할 때 그렇습니다.
def multiply_by(n): def multiplier(x): return x * n return multiplier double = multiply_by(2) triple = multiply_by(3) print(double(5)) print(triple(5))이 코드에서 double과 triple은 각각 다른 n을 기억하는 클로저입니다. 같은 함수 구조를 가지지만 서로 다른 환경을 품고 있는 셈입니다.
이 패턴은 설정 값에 따라 동작이 달라지는 함수를 만들 때 매우 자주 사용됩니다.
데코레이터와 클로저의 관계
데코레이터를 이해했다면, 사실 이미 클로저를 사용하고 있었다고 볼 수 있습니다. 데코레이터의 내부 구조를 다시 떠올렵면, 대부분 이런 형태를 가지고 있습니다.
def decorator(func): def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper여기서 wrapper 함수는 func를 기억하고 있고, 이 관계 자체가 바로 클로저입니다. 즉, 데코레이터는 클로저를 활용한 대표적인 활용 사례라고 볼 수 있습니다.
클로저를 이해하는 핵심 관점
클로저를 이해할 때 가장 중요한 것은 문법이 아니라 관점입니다. 다음과 같은 질문에 대한 하나의 해답입니다.
"상태를 가지는 함수를, 클래스 없이 표현할 수 없을까?"
이 질문에 고개가 끄덕여진다면, 클로저는 더 이상 추상적인 개념이 아니라 매우 실용적인 도구로 보이기 시작할 것입니다.
마무리하며
클로저는 파이썬의 문법 중에서도 특히 오해를 많이 받는 개념 중 하나입니다. 하지만 한 겹씩 벗겨서 살펴보면, 결국 우리가 이미 알고 있던 함수의 성질을 자연스럽게 확장한 것에 불과하다는 사실을 알 수 있습니다.
클로저를 제대로 이해하면 고차함수, 데코레이터, 함수형 프로그래밍 전반이 훨씬 명확해집니다. 그리고 파이썬 코드가 왜 이런 형태로 설계되었는지도 조금은 보이기 시작할 것 입니다.
개념을 외우기 보다는, 이런 질문에서 출발해보세요.
"이 함수는 무엇을 기억하고 있는가?"
그 질문에 답할 수 있다면, 이미 클로저를 이해하고 있다고 볼 수 있습니다.
읽어주셔서 감사합니다.
다음 글에서 뵙겠습니다.
'IT' 카테고리의 다른 글
[Python] 이터레이터와 제너레이터, 그리고 파이썬의 지연 평가 (0) 2026.01.21 [Python] 파이썬 제너레이터(Generator)를 이해한다는 것 (0) 2026.01.21 [Python] 고차함수(Higher-Order Function) 란? (0) 2026.01.21 [Python] 파이썬 데코레이터(decorator)를 제대로 이해해보자 (0) 2026.01.21 [Python] Higher-Order Functions와 Generator로 Django FCM 코드 중복 제거 (0) 2026.01.21