ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Closure, 도대체 왜 쓰는 걸까?
    python 2026. 1. 9. 15:55

     

    파이썬을 공부하다 보면 '클로저(Closure)'라는 개념을 마주하게 됩니다. "함수 안에 함수가 있고... 바깥 변수를 기억한다?" 문법적인 정의는 알겠는데, 도대체 "이걸 실무에서 왜, 언제 써야 하는지" 명확하게 와닿지 않을 때가 많습니다.

    그냥 전역 변수를 쓰거나 클래스를 만들면 될 것 같은데 말이죠. 오늘은 클로저를 사용하는 결정적인 이유 3가지를 실전 예제와 함께 정리해 봅니다.

     

     


    데이터 은닉과 안전성 (전역 변수의 대안)

    어떤 값을 계속 기억(유지)해야 하는데, 이 값이 아무나 건드려서는 안 되는 중요한 값일 때 클로저가 빛을 발합니다.

    ❌ 안 좋은 예: 전역 변수(Global) 사용

    전역 변수는 어디서든 접근 가능하기 때문에, 다른 함수나 로직에서 실수로 값을 덮어쓸 위험이 큽니다.

    # 전역 변수: 누구나 접근 가능해서 위험함
    count = 0
    
    def click():
        global count
        count += 1
        print(count)
    
    click() # 1
    count = 100 # 누군가 실수로 변수를 오염시킴! 😱
    click() # 101 (원치 않는 결과)

     

    ✅ 좋은 예: 클로저를 이용한 은닉화

    클로저를 사용하면 count 변수는 오직 counter 함수 내부에서만 접근할 수 있습니다. 외부에서 강제로 값을 바꿀 수 없으므로 안전하게 상태(State)를 유지할 수 있습니다.

    def make_counter():
        count = 0 # 자유 변수: 함수 밖에서 접근 불가 (은닉)
        
        def counter():
            nonlocal count
            count += 1
            return count
        
        return counter
    
    my_counter = make_counter()
    
    print(my_counter()) # 1
    print(my_counter()) # 2
    
    # 외부에서 count에 접근할 방법이 없음 -> 안전함!
    # my_counter.count = 100 (에러 발생 혹은 적용 안 됨)
    

     

     

    간결한 코드 (불필요한 클래스 줄이기)

     

    데이터를 저장해야 한다고 해서 무조건 class를 만드는 것은 때로 과한 엔지니어링(Over-engineering)이 될 수 있습니다. 메서드가 하나뿐인 클래스는 클로저로 대체하는 것이 훨씬 깔끔합니다.

    ❌ 비교: 클래스로 구현했을 때

    단순히 특정 숫자를 곱해주는 기능을 만드는데, __init__에 self에... 코드가 깁니다.

    class Multiplier:
        def __init__(self, factor):
            self.factor = factor
            
        def multiply(self, n):
            return n * self.factor
    
    m3 = Multiplier(3)
    print(m3.multiply(10)) # 30
    

    ✅ 좋은 예: 클로저로 구현했을 때

    함수 호출 한 번으로 깔끔하게 끝납니다. "데이터(factor)를 품은 함수"를 만들어내는 방식입니다.

    def make_multiplier(factor):
        # factor가 클로저 영역에 저장됨
        return lambda n: n * factor
    
    m3 = make_multiplier(3) # 3을 곱하는 함수 생성
    m5 = make_multiplier(5) # 5를 곱하는 함수 생성
    
    print(m3(10)) # 30
    print(m5(10)) # 50
    

     

    함수 팩토리 (Function Factory) 만들기

    비슷한 기능을 하는 함수를 여러 개 찍어내야 할 때, 클로저를 틀(Template)처럼 사용할 수 있습니다. 코드가 매우 간결해집니다.

    상황: 2배 곱하는 함수, 3배 곱하는 함수, 10배 곱하는 함수가 각각 필요하다면?

     
    def mul_factory(m):
        # m의 값을 기억하는 함수를 만들어 리턴
        def multiplier(x):
            return x * m
        return multiplier
    
    # 함수를 '찍어냄'
    mul2 = mul_factory(2)  # 2를 기억하는 함수 생성
    mul3 = mul_factory(3)  # 3을 기억하는 함수 생성
    
    print(mul2(10))  # 20
    print(mul3(10))  # 30

     

     

    데코레이터(Decorator)의 기반

    파이썬의 꽃이라 불리는 데코레이터(@)가 작동하는 원리가 바로 클로저입니다. 기존 함수의 코드를 수정하지 않고, 앞뒤에 부가적인 기능(로깅, 타이머, 권한 체크 등)을 붙이고 싶을 때 사용합니다.

    ✅ 실전 예제: 함수 실행 시간 측정하기

    timer_decorator 함수는 func라는 원래 함수를 기억(Closure)하고 있다가, 실행될 때 시간을 측정하는 코드를 덧붙여 실행합니다.

    import time
    
    def timer_decorator(func):
        # wrapper 함수가 바깥의 func를 참조 (클로저)
        def wrapper(*args, **kwargs):
            start = time.time()
            result = func(*args, **kwargs) # 원래 함수 실행
            end = time.time()
            print(f"실행 시간: {end - start:.5f}초")
            return result
        return wrapper
    
    @timer_decorator
    def heavy_work():
        # 0부터 100만까지 더하기
        sum(range(1000000))
    
    heavy_work()
    # 출력: 실행 시간: 0.03421초 (컴퓨터 성능에 따라 다름)
    

     


    요약: 언제 클로저를 써야 할까?

    1. 데이터 은닉(Hiding): 전역 변수를 쓰기 싫고, 변수를 함수 내부에 안전하게 숨기고 싶을 때.
    2. 상태 보존(State): 함수가 끝난 후에도 특정 값을 기억해야 하는데, 클래스를 만들기엔 너무 거창할 때.
    3. 기능 확장(Decorator): 기존 코드를 건드리지 않고 함수 앞뒤에 기능을 추가하고 싶을 때.

    클로저는 자바스크립트 등 함수형 프로그래밍을 지원하는 언어들의 핵심 개념이기도 합니다. 이 원리를 이해하면 파이썬을 훨씬 더 "파이썬답게(Pythonic)" 사용할 수 있습니다.

Designed by Tistory.