클로저 사용하기

이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07

Go 언어는 클로저(Closure)를 지원합니다. 클로저는 함수 안에서 함수를 선언 및 정의할 수 있고, 바깥쪽 함수에 선언된 변수에도 접근할 수 있는 함수를 말합니다.


그림 25-1 클로저의 개념

그림 25-1을 보면 함수 안에 변수 a, 변수 b, 함수가 함께 선언 및 정의되어 있습니다. 이처럼 바깥 함수가 변수와 자기 자신(함수)을 에워싸고(close over) 있다고 해서 클로저(closure)라고 합니다. 또한, 안쪽 함수 자체도 변수에 들어갑니다.

먼저 함수 안에서 함수를 선언하고 정의해보겠습니다.

  • 변수 := func(매개변수명 자료형) 리턴값_자료형 { }
//    ↓ 함수 안에서
func main() {
	//      ↓ 익명 함수를 선언 및 정의
	sum := func(a, b int) int {
		return a + b
	}

	r := sum(1, 2) // 익명함수 사용

	fmt.Println(r) // 3
}

실행 결과

3

익명 함수는 일반적인 함수와는 달리 함수를 정의할 때 이름이 없습니다. 매개변수와 리턴값을 지정하는 방법과 함수를 호출하는 방법은 일반적인 함수와 같습니다. 단, 함수가 들어있는 변수를 함수처럼 호출하게됩니다.

이번에는 이름이 없는 익명 함수의 바깥에 있는 변수를 사용해보겠습니다. 간단하게 y = ax + b를 구하는 함수입니다.

func main() {
	a, b := 3, 5

	f := func(x int) int {
		return a*x + b // 함수 바깥의 변수 a, b 사용
	}

	y := f(5)

	fmt.Println(y) // 20
}

실행 결과

20

func(x int) int 함수의 내용을 보면 바깥의 main 함수에 선언된 변수 ab를 사용하고 있습니다. 이처럼 익명 함수 안에서 바깥의 변수에 접근하는 클로저를 사용할 수 있습니다.

왜 복잡하게 클로저를 사용할까요? 다음 예제를 보면 이유를 알 수 있습니다.

package main

import "fmt"

//              ↓ 리턴 값이 익명 함수
func calc() func(x int) int {
	a, b := 3, 5           // 지역 변수는 함수가 끝나면 소멸되지만
	return func(x int) int {
		return a*x + b // 클로저이므로 함수를 호출 할 때마다 변수 a와 b의 값을 사용할 수 있음
	}
	// ↑ 익명 함수를 리턴
}

func main() {
	f := calc() // calc 함수를 실행하여 리턴값으로 나온 클로저를 변수에 저장

	fmt.Println(f(1)) // 8
	fmt.Println(f(2)) // 11
	fmt.Println(f(3)) // 14
	fmt.Println(f(4)) // 17
	fmt.Println(f(5)) // 20
}

실행 결과

8
11
14
17
20

먼저 알아둘 부분은 함수에서 지역 변수는 함수 실행이 끝나면 소멸됩니다. 여기서 calc 함수는 y = ax + b를 구하는 클로저를 리턴하도록 구성되어 있습니다. 그리고 맨 처음에 f := calc()로 변수 f에 클로저를 저장하고 나면 calc 함수의 지역 변수인 a, b는 소멸됩니다. 하지만 그 아래 fmt.Println 부분을 보면 f 함수는 a, b의 값을 이용하여 정상적으로 값을 구하고 있습니다.

이처럼 클로저를 사용하면 지역 변수가 소멸되지 않고, 나중에 함수를 호출할 때마다 계속 가져다 쓸 수 있습니다. 즉, 클로저는 함수가 선언될 때의 환경을 계속 유지합니다. 다르게 이야기하면 프로그램의 흐름을 변수에 저장할 수 있습니다. 클로저는 함수형 언어의 큰 특징이며 Go 언어는 클로저를 통해 함수형 언어의 기능을 구현하고 있습니다.

예제와 같이 Go 언어의 함수는 일반 자료형뿐만 아니라 함수 자체도 리턴할 수 있습니다. 이때는 함수의 리턴값 자료형 부분에 리턴할 함수의 형태를 지정해줍니다.

  • func 함수명() func(매개변수명 자료형) 리턴값_자료형

따라서 정수형 매개변수, 정수형 리턴값 형태의 함수를 리턴한다면 func calc() func(x int) int가 됩니다.

클로저와 람다 함수
보통 클로저를 구성할 때 함수 안에 이름이 없는 익명 함수를 사용합니다. 이때 사용되는 익명 함수를 람다 함수라고 합니다. 클로저와 람다 함수를 혼동하기 쉬운데 둘은 다른 개념이므로 구분할 필요가 있습니다.

C++, Java 등 다른 언어에서는 람다 함수라는 용어를 사용하지만 Go 언어에서는 람다 함수라는 용어를 잘 사용하지 않습니다.


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

01 June 2015