본문 바로가기
Flutter & Dart

Flutter & Dart - Closure

by 방정맞은사람 2025. 1. 14.

클로저 (Closure)

클로저란?

클로저(Closure)는 함수가 외부 스코프의 변수를 참조하여, 해당 변수의 상태를 "기억"한 채로 실행되는 함수를 말합니다. 즉, 클로저는 자신이 생성된 스코프(환경)의 상태를 캡처하고 이를 유지할 수 있는 특별한 함수입니다.

Dart에서 클로저는 함수를 반환하거나, 함수 내부에서 정의된 함수가 외부 변수에 접근할 때 활용됩니다.


클로저의 특징

  1. 외부 변수의 참조
    •   클로저는 자신이 선언된 외부 스코프의 변수를 "캡처"하여 유지합니다.
    Function makeAdder(int base) {
      return (int value) => base + value; // base는 외부 변수
    }
    
    void main() {
      final addFive = makeAdder(5); // base = 5
      print(addFive(10));          // 출력: 15
      print(addFive(20));          // 출력: 25
    }
    
    위 코드에서 addFive는 클로저로, 외부 변수 base의 상태를 기억합니다.
  2. 상태를 유지
    •   클로저는 생성 당시의 외부 변수를 "복사"하는 것이 아니라, 변수의 참조를 유지합니다. 따라서 외부 변수의 값이 변경되면 클로저 내부에서도 해당 변경 사항이 반영됩니다.
    Function counter() {
      int count = 0; // 외부 변수
      return () => ++count;
    }
    
    void main() {
      final increment = counter();
      print(increment()); // 출력: 1
      print(increment()); // 출력: 2
      print(increment()); // 출력: 3
    }
    
    
  3. 함수형 프로그래밍의 핵심 요소
    •   클로저는 상태를 안전하게 캡처하고 관리할 수 있으므로, 함수형 프로그래밍에서 자주 사용됩니다.

클로저의 활용 예제

  1. 캡슐화된 상태 관리
    •   클로저는 외부 스코프에 있는 변수의 상태를 안전하게 보호하면서 접근할 수 있는 방법을 제공합니다.
    Function createCounter() {
      int count = 0;
      return () => ++count;
    }
    
    void main() {
      final counter1 = createCounter();
      final counter2 = createCounter();
    
      print(counter1()); // 출력: 1
      print(counter1()); // 출력: 2
    
      print(counter2()); // 출력: 1
      print(counter2()); // 출력: 2
    }
    
    
  2. 이벤트 핸들러 생성
    •   클로저를 활용하여 이벤트에 따른 동작을 동적으로 정의할 수 있습니다.
    List<Function> createHandlers() {
      final handlers = <Function>[];
      for (int i = 0; i < 3; i++) {
        handlers.add(() => print('Handler $i'));
      }
      return handlers;
    }
    
    void main() {
      final handlers = createHandlers();
      handlers[0](); // 출력: Handler 0
      handlers[1](); // 출력: Handler 1
      handlers[2](); // 출력: Handler 2
    }
    
    
  3. 지연 실행 (Lazy Evaluation)
    •   클로저를 사용하여 필요한 시점에만 계산을 실행하도록 할 수 있습니다.
    Function delayedAdder(int base) {
      return (int value) => base + value;
    }
    
    void main() {
      final addTenLater = delayedAdder(10);
      print(addTenLater(5)); // 출력: 15
    }
    
    

클로저와 커링의 관계

공통점

  1. 외부 변수의 상태를 캡처:
    •   클로저와 커링 모두 외부 변수나 상태를 캡처하여 유지합니다.
  2. 함수 반환:
    •   클로저와 커링 모두 함수를 반환하며, 이 반환된 함수가 외부 상태에 의존해 동작합니다.

차이점

  1. 목적:
    •   클로저: 외부 상태를 유지하여 해당 상태를 기반으로 동작하는 함수를 생성.
    •   커링: 다중 매개변수를 가지는 함수를 분리하여 단계별로 호출할 수 있도록 변환.
  2. 구조:
    •   클로저는 상태를 기억하는 함수 자체를 생성.
    •   커링은 매개변수를 나누어 부분적으로 고정된 함수를 생성.

왜 밀접하게 느껴질까?

  •   클로저는 커링을 구현하는 데 필수적입니다. 커링은 함수를 여러 단계로 분리하면서 각 단계에서 매개변수의 상태를 "기억"해야 하므로, 클로저를 활용합니다.

예시 (Dart):

Function multiply(int a) => (int b) => (int c) => a * b * c;

void main() {
  final multiplyBy2 = multiply(2);        // a = 2 고정
  final multiplyBy2And3 = multiplyBy2(3); // b = 3 고정
  final result = multiplyBy2And3(4);      // c = 4 고정
  print(result); // 출력: 24
}

위 코드에서 multiply는 클로저를 사용하여 각 매개변수를 캡처하며, 커링을 구현합니다. 이처럼 커링과 클로저는 함수형 프로그래밍의 중요한 구성 요소로 서로 밀접하게 연결되어 있습니다.


결론

클로저는 외부 상태를 캡처하여 유지할 수 있는 강력한 도구로, Dart와 같은 언어에서 상태 관리, 지연 실행, 함수형 프로그래밍 등 다양한 활용이 가능합니다. 클로저는 커링을 구현하는 데 필수적인 역할을 하며, 두 개념은 함수와 상태를 다룬다는 점에서 함수형 프로그래밍에서 매우 밀접한 관계를 가집니다.