Go에서 함수 이해하기 - 정의, 매개변수 및 (다중) 반환값
Ethan Miller
Product Engineer · Leapcell

다른 많은 현대 프로그래밍 언어와 마찬가지로 Go는 코드를 구조화하고, 재사용성을 높이며, 복잡성을 관리하기 위해 함수에 크게 의존합니다. 함수는 특정 작업을 수행하도록 설계된 자체 포함 코드 블록입니다. 이 글에서는 Go에서 함수의 핵심 개념, 즉 정의, 매개변수 사용 및 Go의 특징인 다중 반환 값 지원에 대해 살펴보겠습니다.
Go에서 함수 정의하기
Go에서 함수를 정의하는 구문은 간단합니다. func
키워드로 시작하여 함수 이름, 괄호 ()
로 묶인 매개변수 목록, 그리고 마지막으로 선택 사항인 괄호로 묶인 반환 타입 목록이 옵니다. 그런 다음 함수 본문은 중괄호 {}
안에 정의됩니다.
기본 구조는 다음과 같습니다:
func functionName(parameter1 type1, parameter2 type2) (returnType1, returnType2) { // Function body // ... return value1, value2 }
간단한 예제를 살펴봅시다:
package main import "fmt" // This function greets the user by name. func greet(name string) { fmt.Printf("Hello, %s!\n", name) } func main() { greet("Alice") // Calling the function }
이 예제에서:
func
는 함수를 선언하는 키워드입니다.greet
는 함수의 이름입니다.(name string)
은string
타입의name
이라는 단일 매개변수를 정의합니다.- 반환 타입이 지정되지 않았으므로 이 함수는 값을 반환하지 않습니다.
fmt.Printf("Hello, %s!\n", name)
는 함수의 본문으로, 형식화된 문자열을 콘솔에 출력합니다.
매개변수: 함수에 데이터 전달하기
매개변수(인수라고도 함)는 함수 시그니처에 정의된 변수로, 함수가 호출될 때 값을 받습니다. 이를 통해 함수는 각 특정 사례에 대해 다시 작성할 필요 없이 다른 데이터로 작동할 수 있어 재사용성을 높일 수 있습니다.
Go는 모든 매개변수 전달에 *값에 의한 전달(pass-by-value)*을 사용합니다. 이는 변수를 함수에 전달할 때 해당 변수 값의 복사본이 만들어져 함수에 전달된다는 것을 의미합니다. 함수 내부에서 매개변수를 변경하는 것은 함수 외부의 원본 변수에 영향을 미치지 않습니다.
정수 매개변수가 있는 이 예제를 고려해 봅시다:
package main import "fmt" func addOne(num int) { num = num + 1 // This changes the *copy* of num fmt.Printf("Inside addOne: num = %d\n", num) } func main() { value := 10 fmt.Printf("Before addOne: value = %d\n", value) addOne(value) fmt.Printf("After addOne: value = %d\n", value) // value remains 10 }
출력:
Before addOne: value = 10
Inside addOne: num = 11
After addOne: value = 10
addOne
이 매개변수 num
을 수정했음에도 불구하고 main
의 value
가 10
으로 유지되는 것을 알 수 있습니다.
원본 변수를 수정해야 하는 경우 일반적으로 해당 변수의 포인터를 전달합니다. 여전히 값에 의한 전달(포인터의 값이 복사됨)이지만, 포인터 자체가 원본 메모리 위치를 참조합니다.
package main import "fmt" func addOnePtr(numPtr *int) { *numPtr = *numPtr + 1 // Dereference the pointer to modify the value at the address fmt.Printf("Inside addOnePtr: *numPtr = %d\n", *numPtr) } func main() { value := 10 fmt.Printf("Before addOnePtr: value = %d\n", value) addOnePtr(&value) // Pass the address of value fmt.Printf("After addOnePtr: value = %d\n", value) // value is now 11 }
출력:
Before addOnePtr: value = 10
Inside addOnePtr: *numPtr = 11
After addOnePtr: value = 11
가변 매개변수(Variadic Parameters)
Go는 가변 매개변수도 지원하는데, 이는 함수가 특정 타입의 가변적인 수의 인수를 받을 수 있도록 합니다. 이는 매개변수 목록에 타입 앞에 말줄임표 ...
를 사용하여 표시됩니다. 가변 매개변수는 함수 내에서 슬라이스처럼 작동합니다.
package main import "fmt" // calculateSum takes a variable number of integers and returns their sum. func calculateSum(numbers ...int) int { total := 0 for _, num := range numbers { total += num } return total } func main() { fmt.Println("Sum of 1, 2, 3:", calculateSum(1, 2, 3)) fmt.Println("Sum of 5, 10:", calculateSum(5, 10)) fmt.Println("Sum of nothing:", calculateSum()) // You can also pass a slice directly using the ... operator nums := []int{10, 20, 30, 40} fmt.Println("Sum of slice:", calculateSum(nums...)) }
출력:
Sum of 1, 2, 3: 6
Sum of 5, 10: 15
Sum of nothing: 0
Sum of slice: 100
반환 값: 함수가 결과를 돌려주기
함수는 일반적으로 계산을 수행하고 결과를 반환합니다. Go에서 반환 값은 매개변수 목록 뒤, 함수 본문의 여는 중괄호 앞에 지정됩니다.
package main import "fmt" // factorial calculates the factorial of a non-negative integer. func factorial(n int) int { if n < 0 { return 0 // Or handle error appropriately } if n == 0 { return 1 } result := 1 for i := 1; i <= n; i++ { result *= i } return result } func main() { fmt.Printf("Factorial of 5 is: %d\n", factorial(5)) // Output: 120 fmt.Printf("Factorial of 0 is: %d\n", factorial(0)) // Output: 1 }
Go의 슈퍼파워: 여러 반환 값
Go의 가장 독특하고 실용적인 기능 중 하나는 함수에서 여러 값을 반환할 수 있다는 것입니다. 이는 결과와 오류를 함께 반환하거나 관련된 여러 계산 값을 반환하는 일반적인 시나리오에 매우 유용합니다.
반환 타입은 괄호 ()
로 묶고 쉼표로 구분합니다.
package main import ( "errors" "fmt" ) // divide performs division and returns both the quotient and an error if division by zero occurs. func divide(numerator, denominator float64) (float64, error) { if denominator == 0 { return 0, errors.New("cannot divide by zero") // Return 0 for quotient, and an error object } return numerator / denominator, nil // Return the quotient and nil (no error) } func main() { result, err := divide(10, 2) if err != nil { fmt.Printf("Error: %s\n", err) } else { fmt.Printf("Division result: %.2f\n", result) // Output: 5.00 } result, err = divide(10, 0) if err != nil { fmt.Printf("Error: %s\n", err) // Output: Error: cannot divide by zero " // result will be 0 here } else { fmt.Printf("Division result: %.2f\n", result) } }
(result, error)
패턴의 사용은 Go의 관용적인 방식이며 표준 라이브러리 전반에 걸쳐 광범위하게 사용됩니다. 이는 호출자가 명시적으로 오류를 확인하도록 강제하여 보다 강력한 오류 처리를 가능하게 합니다.
명명된 반환 값(Naked Returns)
Go를 사용하면 함수 시그니처에서 반환 값을 명명할 수 있습니다. 이렇게 하면 이러한 명명된 반환 값은 해당 제로 값으로 자동 초기화됩니다. 그런 다음 함수 본문 내에서 해당 값에 값을 할당할 수 있으며, 인수가 없는 return
문은 명명된 변수의 현재 값을 암시적으로 반환합니다. 이를 종종 "naked return"이라고 합니다.
짧은 함수에서는 편리하지만, 반환되는 값이 무엇인지 즉시 명확하지 않으므로 긴 함수를 읽기 어렵게 만들 수 있습니다.
package main import "fmt" // calculateStats calculates sum and average using named return values. func calculateStats(numbers ...int) (sum int, average float64) { // sum and average are initialized to 0 and 0.0 automatically if len(numbers) == 0 { return // Returns sum=0, average=0.0 } for _, num := range numbers { sum += num } average = float64(sum) / float64(len(numbers)) return // Naked return: returns the current values of sum and average } func main() { s, avg := calculateStats(1, 2, 3, 4, 5) fmt.Printf("Sum: %d, Average: %.2f\n", s, avg) // Output: Sum: 15, Average: 3.00 s2, avg2 := calculateStats() fmt.Printf("Sum: %d, Average: %.2f\n", s2, avg2) // Output: Sum: 0, Average: 0.00 }
사용되지 않는 반환 값의 빈 식별자(Blank Identifier)
때로는 여러 반환 값 중 하나에만 관심이 있을 수 있습니다. Go는 선언된 모든 지역 변수가 사용되도록 요구합니다. 반환 값을 무시하려면 빈 식별자 _
를 사용할 수 있습니다.
package main import "fmt" func getData() (string, int, error) { return "example", 123, nil } func main() { // We only care about the string and the error for now dataString, _, err := getData() if err != nil { fmt.Printf("Error getting data: %v\n", err) return } fmt.Printf("Received string: %s\n", dataString) // If we only need the integer _, dataInt, _ := getData() fmt.Printf("Received integer: %d\n", dataInt) }
결론
함수는 Go 프로그램의 구성 요소입니다. 함수를 정의하는 방법, 매개변수를 전달하는 방법(Go의 값에 의한 전달 의미론에 유의), Go의 강력한 다중 반환 값을 효과적으로 사용하는 방법을 이해하는 것은 깔끔하고 효율적이며 관용적인 Go 코드를 작성하는 데 필수적입니다. 오류 처리를 위한 (result, error)
패턴의 일관된 사용은 좋은 Go 프로그래밍의 특징이며 애플리케이션의 복원력에 크게 기여합니다. 이러한 개념을 마스터하면 Go로 복잡하고 안정적인 소프트웨어를 설계하고 구현할 수 있는 좋은 준비를 갖추게 될 것입니다.