Golang 익명 함수 사용법

GolangBeginner
지금 연습하기

소개

이전 랩에서는 명명된 함수를 작성하고 사용하는 방법, 코드를 모듈로 구성하는 방법, 그리고 로직을 별도의 함수로 분할하여 코드 가독성을 향상시키는 방법을 배웠습니다. 이번 랩에서는 이름이 없는 특수한 유형의 함수인 익명 함수 (anonymous functions) 에 대해 알아보겠습니다. 익명 함수는 별도의 명명된 함수를 선언하지 않고 "제자리에서" 작은 로직 조각을 정의하려는 경우에 유용합니다. 특히 짧고 자체 포함된 연산이나, 함수를 다른 함수에 인수로 전달해야 할 때 (콜백 (callbacks) 에서처럼) 유용합니다. 익명 함수를 사용하면 더 간결하고 표현력이 풍부한 코드를 작성할 수 있습니다.

주요 내용:

  • 익명 함수가 무엇이며, 어떻게 정의하는지
  • 익명 함수를 사용하는 이유와 시기
  • 익명 함수를 이름에 할당하지 않고 호출하는 방법
  • 익명 함수에 매개변수를 전달하고 값을 반환하는 방법
  • 더 유연한 코드를 위해 익명 함수를 콜백 함수로 사용하는 방법

익명 함수 이해하기

Go 에서 익명 함수는 일반 함수와 동일하게 정의되지만, 이름이 없다는 점이 다릅니다. 대신 변수에 할당하거나, 인수로 전달하거나, 정의 직후에 즉시 실행할 수 있습니다. 이러한 특성 때문에 짧고 일회성 연산이나, 함수를 다른 함수에 인수로 전달하는 데 적합합니다. 익명 함수는 함수가 한 번만 필요하고 별도의 명명된 함수가 필요하지 않을 때 특히 유용합니다. 로직을 사용되는 곳 가까이에 유지함으로써 코드의 가독성을 높이는 데 도움이 될 수 있습니다.

익명 함수 구문:

func(input parameters)(return parameters) {
    // code block
}

이는 일반 함수를 정의하는 것과 유사하지만, 함수 이름이 없다는 점이 다릅니다.

일반 함수 선언과 비교:

// Regular function declaration
func functionName(parameters...)(return values...) {
    code block
}

익명 함수를 사용하는 이유?

  • 간결성 (Conciseness): 별도의 명명된 함수를 생성하지 않고도 작은 로직 조각을 정의할 수 있어 코드를 더 간결하게 만듭니다.
  • 지역 범위 (Local Scope): 익명 함수의 범위는 주변 함수 내에 있으므로 네임스페이스 오염을 제한합니다.
  • 유연성 (Flexibility): 다른 함수에 인수로 전달하거나 즉시 정의하고 실행할 수 있습니다.

익명 함수를 언제 사용해야 할까요?

  • 다른 곳에서 재사용되지 않을 짧은 함수가 필요할 때.
  • 콜백 함수 (나중에 살펴보겠습니다).
  • 함수를 즉시 실행하려는 경우 (종종 초기화를 위해).

매개변수 없는 익명 함수 생성하기

익명 함수를 사용하여 "hello world"를 출력하는 간단한 예시부터 시작해 보겠습니다. 먼저, 프로젝트 디렉토리에 anonymous.go라는 파일을 생성합니다.

cd ~/project
touch anonymous.go

anonymous.go를 열고 다음 코드를 추가합니다.

package main

import "fmt"

func main() {
    // 익명 함수를 정의하고 변수 f 에 할당합니다.
    f := func() {
        fmt.Println("hello world")
    }

    // 변수 f 를 통해 익명 함수를 호출합니다.
    f()
}

프로그램을 실행합니다.

go run anonymous.go

예상 출력:

hello world

여기서 func() { ... } 구문을 사용하여 익명 함수를 정의했습니다. 이 함수는 매개변수를 받지 않고, 값도 반환하지 않습니다. 이 익명 함수를 변수 f에 할당했습니다. 그런 다음 f()를 사용하여 함수를 호출합니다. 이렇게 하면 익명 함수가 실행되고 "hello world"가 출력됩니다.

익명 함수에서 매개변수 사용하기

익명 함수는 일반 함수와 마찬가지로 매개변수를 받을 수 있습니다. 문자열을 매개변수로 전달하도록 코드를 수정해 보겠습니다.

anonymous.go의 내용을 다음으로 바꿉니다.

package main

import "fmt"

func main() {
    f := func(s string) {
        fmt.Println(s)
    }
    f("hello world")
}

프로그램을 실행합니다.

go run anonymous.go

예상 출력:

hello world

이번에는 익명 함수가 문자열 매개변수 s를 받습니다. func(s string) 부분은 익명 함수가 문자열 타입의 s라는 매개변수를 받는다고 정의합니다. f("hello world")를 호출하면 문자열 "hello world"가 함수에 전달되어 콘솔에 출력됩니다. 이를 통해 익명 함수에 값을 전달하여 더 다재다능하게 만들 수 있습니다.

익명 함수에서 값 반환하기

익명 함수는 값도 반환할 수 있습니다. 두 개의 정수를 매개변수로 받아 합계를 반환하는 익명 함수를 만들어 보겠습니다.

anonymous.go의 내용을 다음으로 바꿉니다.

package main

import "fmt"

func main() {
    f := func(a, b int) int {
        return a + b
    }
    result := f(3, 5)
    fmt.Println(result)
}

프로그램을 실행합니다.

go run anonymous.go

예상 출력:

8

이제 익명 함수의 시그니처는 func(a, b int) int입니다. 이는 두 개의 정수 (ab) 를 입력으로 받아 정수를 출력으로 반환한다는 의미입니다. 함수의 본문인 return a + b는 합계를 계산하여 반환합니다. f(3, 5)를 호출하면 인수 3 과 5 로 익명 함수가 실행되고 결과 8 이 반환됩니다. 그런 다음 이 결과를 result 변수에 저장하고 콘솔에 출력합니다.

익명 함수 즉시 선언 및 호출

변수에 할당하지 않고 익명 함수를 한 번에 정의하고 호출할 수 있습니다. 이는 빠르고 일회성 작업에 유용할 수 있습니다.

anonymous.go를 업데이트합니다.

package main

import "fmt"

func main() {
    res := func(a, b int) int {
        return a + b
    }(3, 5) // Call the anonymous function directly here
    fmt.Println(res)
}

프로그램을 실행합니다.

go run anonymous.go

예상 출력:

8

여기서는 익명 함수 func(a, b int) int { return a + b }를 정의하고 함수 선언 뒤에 (3, 5)를 추가하여 즉시 호출했습니다. 이 구문 func(...) {...}(...)을 사용하면 단일 표현식으로 함수를 정의하고 호출할 수 있습니다. 괄호 안의 인수는 즉시 함수에 전달됩니다. 이 경우 3 과 5 의 합을 반환하며, 이는 res 변수에 할당됩니다. 이는 간단하고 즉시 실행되는 함수에 대한 일반적인 관행이며, 초기화 또는 짧은 계산에 유용합니다.

콜백 함수로 익명 함수 사용하기

익명 함수는 콜백 (callback) 으로도 사용될 수 있습니다. 즉, 다른 함수의 인수로 전달할 수 있습니다. 이는 명명된 함수를 생성하지 않고 함수의 동작을 사용자 정의하려는 경우에 유용합니다.

콜백 함수란 무엇인가요?

콜백 함수는 다른 함수의 인수로 전달되어 첫 번째 함수가 작업을 완료한 후 실행되는 함수입니다. 이를 통해 호출자는 호출되는 함수의 동작을 사용자 정의하여 더 많은 유연성과 모듈성을 제공할 수 있습니다. 본질적으로 콜백을 받는 함수는 어느 시점에서 콜백 함수를 "다시" 호출합니다.

왜 익명 함수를 콜백으로 사용해야 할까요?

익명 함수는 특정 컨텍스트 내에서만 사용되는 짧고 구체적인 동작을 나타내는 경우가 많기 때문에 콜백 함수로 매우 잘 작동합니다. 익명 함수를 콜백으로 사용하면 코드를 더 간결하게 유지하고 별도의 명명된 함수를 정의할 필요가 없습니다.

anonymous.go를 다음 코드로 바꿉니다.

package main

import (
    "fmt"
    "math"
)

// 'visit'는 슬라이스와 함수를 받습니다. 슬라이스의 각 요소에 함수를 적용합니다.
func visit(lst []float64, f func(float64)) {
    for _, value := range lst {
        f(value)
    }
}

func main() {
    arr := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9}

    // 익명 함수를 사용하여 각 요소를 자체와 더합니다.
    visit(arr, func(v float64) {
        fmt.Printf("Sum:%.0f ", v+v)
    })
    fmt.Println()

    // 익명 함수를 사용하여 각 요소를 자체와 곱합니다.
    visit(arr, func(v float64) {
        fmt.Printf("Product:%.0f ", v*v)
    })
    fmt.Println()

    // math.Pow 를 사용하여 각 요소를 제곱하는 익명 함수를 사용합니다.
    visit(arr, func(v float64) {
        v = math.Pow(v, 2)
        fmt.Printf("Square:%.0f ", v)
    })
    fmt.Println()
}

프로그램을 실행합니다.

go run anonymous.go

예상 출력:

Sum:2 Sum:4 Sum:6 Sum:8 Sum:10 Sum:12 Sum:14 Sum:16 Sum:18
Product:1 Product:4 Product:9 Product:16 Product:25 Product:36 Product:49 Product:64 Product:81
Square:1 Square:4 Square:9 Square:16 Square:25 Square:36 Square:49 Square:64 Square:81

이 프로그램에서는 먼저 float64 슬라이스 (lst) 와 func(float64) 유형의 함수 (f) 를 받는 visit 함수를 만듭니다. visit 함수는 슬라이스를 반복하고 각 요소에 대해 제공된 함수 f를 호출합니다. 이 디자인 패턴을 통해 visit 함수는 제공된 콜백 함수 f에 따라 다른 로직을 실행할 수 있습니다.

main 함수 내에서 콜백이 유연성을 제공하는 방식을 보여주기 위해 서로 다른 익명 함수로 visit를 세 번 호출합니다.

  • 첫 번째 익명 함수는 각 요소를 자체와 더한 값을 계산합니다.
  • 두 번째 익명 함수는 각 요소를 자체와 곱한 값을 계산합니다.
  • 세 번째 익명 함수는 math.Pow를 사용하여 각 요소를 제곱합니다.

이는 익명 함수가 콜백으로 전달될 수 있고 visit 함수가 매개변수로 전달된 콜백 함수를 기반으로 다른 작업을 수행할 수 있음을 보여줍니다. 이를 통해 코드를 더 재사용 가능하고 모듈식으로 만들 수 있습니다.

요약

이 랩에서는 Go 의 **익명 함수 (anonymous functions)**에 대해 배웠습니다. 익명 함수는 이름이 없으며 짧고 일회용 로직 조각에 자주 사용됩니다. 익명 함수는 다음과 같은 기능을 수행할 수 있습니다.

  • 변수에 할당되어 나중에 호출될 수 있습니다.
  • 매개변수를 받고 값을 반환할 수 있습니다.
  • 즉시 정의되고 호출될 수 있습니다.
  • 다른 함수의 인수로 전달될 때 콜백 (callback) 역할을 하여 매우 유연하고 사용자 정의 가능한 동작을 가능하게 합니다.

익명 함수는 특히 너무 많은 명명된 함수로 코드베이스를 복잡하게 하지 않고 "즉석에서" 사용자 정의 로직이 필요한 경우 유연성과 편의성을 제공합니다. 이를 효과적으로 사용하면 더 표현력 있고 간결하며 모듈식인 Go 프로그램을 만들 수 있습니다. 익명 함수는 더 깨끗하고 읽기 쉽고 유연한 코드를 작성하기 위한 강력한 도구입니다.