클로저 (Closure)
클로저란?
클로저(Closure)는 함수가 외부 스코프의 변수를 참조하여, 해당 변수의 상태를 "기억"한 채로 실행되는 함수를 말합니다. 즉, 클로저는 자신이 생성된 스코프(환경)의 상태를 캡처하고 이를 유지할 수 있는 특별한 함수입니다.
Dart에서 클로저는 함수를 반환하거나, 함수 내부에서 정의된 함수가 외부 변수에 접근할 때 활용됩니다.
클로저의 특징
- 외부 변수의 참조
- 클로저는 자신이 선언된 외부 스코프의 변수를 "캡처"하여 유지합니다.
위 코드에서 addFive는 클로저로, 외부 변수 base의 상태를 기억합니다.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 }
- 상태를 유지
- 클로저는 생성 당시의 외부 변수를 "복사"하는 것이 아니라, 변수의 참조를 유지합니다. 따라서 외부 변수의 값이 변경되면 클로저 내부에서도 해당 변경 사항이 반영됩니다.
Function counter() { int count = 0; // 외부 변수 return () => ++count; } void main() { final increment = counter(); print(increment()); // 출력: 1 print(increment()); // 출력: 2 print(increment()); // 출력: 3 }
- 함수형 프로그래밍의 핵심 요소
- 클로저는 상태를 안전하게 캡처하고 관리할 수 있으므로, 함수형 프로그래밍에서 자주 사용됩니다.
클로저의 활용 예제
- 캡슐화된 상태 관리
- 클로저는 외부 스코프에 있는 변수의 상태를 안전하게 보호하면서 접근할 수 있는 방법을 제공합니다.
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 }
- 이벤트 핸들러 생성
- 클로저를 활용하여 이벤트에 따른 동작을 동적으로 정의할 수 있습니다.
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 }
- 지연 실행 (Lazy Evaluation)
- 클로저를 사용하여 필요한 시점에만 계산을 실행하도록 할 수 있습니다.
Function delayedAdder(int base) { return (int value) => base + value; } void main() { final addTenLater = delayedAdder(10); print(addTenLater(5)); // 출력: 15 }
클로저와 커링의 관계
공통점
- 외부 변수의 상태를 캡처:
- 클로저와 커링 모두 외부 변수나 상태를 캡처하여 유지합니다.
- 함수 반환:
- 클로저와 커링 모두 함수를 반환하며, 이 반환된 함수가 외부 상태에 의존해 동작합니다.
차이점
- 목적:
- 클로저: 외부 상태를 유지하여 해당 상태를 기반으로 동작하는 함수를 생성.
- 커링: 다중 매개변수를 가지는 함수를 분리하여 단계별로 호출할 수 있도록 변환.
- 구조:
- 클로저는 상태를 기억하는 함수 자체를 생성.
- 커링은 매개변수를 나누어 부분적으로 고정된 함수를 생성.
왜 밀접하게 느껴질까?
- 클로저는 커링을 구현하는 데 필수적입니다. 커링은 함수를 여러 단계로 분리하면서 각 단계에서 매개변수의 상태를 "기억"해야 하므로, 클로저를 활용합니다.
예시 (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와 같은 언어에서 상태 관리, 지연 실행, 함수형 프로그래밍 등 다양한 활용이 가능합니다. 클로저는 커링을 구현하는 데 필수적인 역할을 하며, 두 개념은 함수와 상태를 다룬다는 점에서 함수형 프로그래밍에서 매우 밀접한 관계를 가집니다.
'Flutter & Dart' 카테고리의 다른 글
Flutter & Dart - SDK 업데이트이 후 발생한 이슈.. (1) | 2025.03.05 |
---|---|
Flutter & Dart - Isolates<in Dart> (1) | 2025.01.13 |
Flutter & Dart - Functional Programming (함수형 프로그래밍) (0) | 2025.01.09 |
Flutter & Dart - Dart 에서의 커링(Currying) (0) | 2025.01.09 |
Flutter & Dart - Lambda (2) | 2025.01.08 |