클로저(closure)

함수를 일급객체로 취급하는 함수형 프로그래밍 언어(Erlang, Scala, Haskell 등)에서 사용되는 중요한 특성이다.

클로저에 대해 MDN은 아래와 같이 정의하고 있다.

“A closure is the combination of a function and the lexical environment within which that function was declared.”
“클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합이다.”

아래의 예제를 먼저 확인해 보자

package main

func outerFunc() func() {
	x := 10
	innerFunc := func() {
		fmt.Println(x)
	}

	return innerFunc
}

func main() {
    f := outerFunc()
    f() 

    // output:
    // 10
}

scope는 함수를 호출할 때가 아니라 어디에 선언하였는지에 따라 결정된다. 이를 Lexical scoping 이라고 한다. 위 예제에서 함수 innerFunc는 함수 OuterFunc 내부에서 선언되었기 때문에 함수 innerFunc의 상위 스코프는 outerFunc이다. innerFunc가 전역에 선언되었다면 innerFunc의 상위 스코프는 전역 스코프가 된다.

Go언어에서의 클로저

아래 예제에서 nextValue() 함수는 int를 리턴하는 익명함수를 리턴하는 함수이다. go언어에서 함수는 일급함수로서 다른 함수로부터 리턴되는 리턴값으로 사용될 수 있다.

package main
 
func nextValue() func() int {
    i := 0
    return func() int {
        i++
        return i
    }
}
 
func main() {
    next := nextValue()
 
    println(next())  // 1
    println(next())  // 2
    println(next())  // 3
 
    anotherNext := nextValue()
    println(anotherNext()) // 1 다시 시작
    println(anotherNext()) // 2
}

여기서 익명함수가 그 함수 바깥에 있는 변수 i를 참조하고 있다. 익명함수 자체가 로컬 변수로 i를 갖는 것이 아니기때문에 외부 변수 i가 계속 상태를 유지하고 있어 값을 하나씩 증가하는 기능을 하게 된다.(익명함수 자체가 로컬변수로 i를 갖는다면 함수 호출시 i는 항상 0으로 설정된다.)

reference

  1. https://poiemaweb.com/js-closure
  2. http://golang.site/go/article/11-Go-%ED%81%B4%EB%A1%9C%EC%A0%80