🔥 함수 매개변수와 반환 값

1051자
12분

Swift에서는 함수의 매개변수와 반환 값을 매우 유연하게 정의할 수 있답니다. 이름 없는 매개변수 하나만 받는 단순한 유틸리티 함수부터 표현력 있는 매개변수 이름을 가지고 다양한 옵션을 제공하는 복잡한 함수까지 모두 만들 수 있지요.

매개변수가 없는 함수

먼저 매개변수가 없는 함수부터 살펴볼까요? Swift에서는 꼭 입력 매개변수를 정의하지 않아도 된답니다. 아래는 입력 매개변수 없이 항상 같은 문자열을 반환하는 함수의 예시입니다.

func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())
// "hello, world" 출력
swift

함수에 매개변수가 없더라도 함수 이름 뒤에는 빈 괄호 ()를 붙여줘야 해요. 함수를 호출할 때도 마찬가지로 함수 이름 뒤에 빈 괄호를 붙여줍니다.

여러 개의 매개변수를 가지는 함수

함수는 여러 개의 입력 매개변수를 가질 수도 있답니다. 매개변수는 함수의 괄호 안에 쉼표로 구분하여 작성하면 돼요.

아래 함수는 사람의 이름과 이미 인사를 받았는지 여부를 입력받아, 그 사람에게 적절한 인사말을 반환하는 함수랍니다.

func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: "Tim", alreadyGreeted: true))
// "Hello again, Tim!" 출력
swift

greet(person:alreadyGreeted:) 함수를 호출할 때는 person이라는 레이블을 가진 String 인자와 alreadyGreeted라는 레이블을 가진 Bool 인자를 괄호 안에 쉼표로 구분하여 전달하면 됩니다.

이 함수는 앞서 다뤘던 greet(person:) 함수와는 다른 함수라는 점을 유의하세요. 두 함수는 모두 greet으로 시작하지만, greet(person:alreadyGreeted:) 함수는 두 개의 인자를 받고, greet(person:) 함수는 하나의 인자만 받습니다.

반환 값이 없는 함수

함수가 반환 타입을 정의하지 않아도 된다는 것, 알고 계셨나요? 다음은 반환 값 대신 자체적으로 문자열을 출력하는 greet(person:) 함수의 버전이에요.

func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Dave")
// "Hello, Dave!" 출력
swift

함수가 값을 반환할 필요가 없으므로, 함수 정의에 반환 화살표(->)나 반환 타입이 포함되지 않았습니다.

함수를 호출할 때 반환 값을 무시할 수도 있답니다.

func printAndCount(string: String) -> Int {
    print(string)
    return string.count
}
 
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
 
printAndCount(string: "hello, world")
// "hello, world" 출력 후 12 반환
 
printWithoutCounting(string: "hello, world")
// "hello, world" 출력하지만 반환 값은 사용하지 않음
swift

첫 번째 함수 printAndCount(string:)는 문자열을 출력한 후 Int 타입의 문자 개수를 반환합니다. 두 번째 함수 printWithoutCounting(string:)은 첫 번째 함수를 호출하지만 반환 값은 무시하죠. 두 번째 함수를 호출하면, 여전히 첫 번째 함수에 의해 메시지가 출력되지만 반환된 값은 사용되지 않습니다.

만약 위 코드에서 let _ 코드가 없다면 컴파일러는 반환값을 사용하지 않는다고 경고합니다. 경고를 내지 않고 반환값을 무시할 수 있는 방법이 있습니다. 함수 선언 앞에 @discardableResult 속성을 추가하면 컴파일러 경고를 피할 수 있어요.

@discardableResult
func printAndCount(string: String) -> Int {
    print(string)
    return string.count
}
 
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
 
printAndCount(string: "hello, world")
// "hello, world" 출력 후 12 반환
 
printWithoutCounting(string: "hello, world")
// "hello, world" 출력하지만 반환 값은 사용하지 않음
swift

@discardableResult 속성을 사용하면, 반환 값을 사용하지 않았을 때 컴파일러가 경고를 표시하지 않습니다. 함수 정의에 명확하게 함수 반환값을 무시할 수 있음을 컴파일러에게 알려주는 것이죠.

여러 개의 값을 반환하는 함수

Swift에서는 튜플 타입을 함수의 반환 타입으로 사용하여 여러 값을 하나의 복합 반환 값으로 반환할 수 있습니다.

아래 예제는 Int 값의 배열에서 가장 작은 수와 가장 큰 수를 찾는 minMax(array:) 함수를 정의합니다.

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0] // 현재 최소값을 배열의 첫 번째 정수로 초기화
    var currentMax = array[0] // 현재 최대값을 배열의 첫 번째 정수로 초기화
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value // 현재 값이 currentMin보다 작으면 currentMin 업데이트
        } else if value > currentMax {
            currentMax = value // 현재 값이 currentMax보다 크면 currentMax 업데이트
        }
    }
    return (currentMin, currentMax) // 최종 최소값과 최대값을 튜플로 반환
}
swift

minMax(array:) 함수는 두 개의 Int 값을 포함하는 튜플을 반환합니다. 이 값들은 minmax로 레이블이 지정되어 함수의 반환 값을 조회할 때 이름으로 접근할 수 있어요.

minMax(array:) 함수의 본문은 currentMincurrentMax라는 두 개의 작업 변수를 배열의 첫 번째 정수 값으로 설정하는 것으로 시작합니다. 그런 다음 배열의 나머지 값을 반복하면서 각 값이 currentMincurrentMax의 값보다 작거나 큰지 확인하죠. 마지막으로 전체 최소값과 최대값이 두 개의 Int 값을 가진 튜플로 반환됩니다.

함수의 반환 타입의 일부로 튜플의 멤버 값에 이름이 지정되었으므로, 점 문법을 사용하여 발견된 최소값과 최대값에 접근할 수 있답니다.

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// "min is -6 and max is 109" 출력
swift

함수에서 튜플이 반환되는 시점에는 튜플의 멤버에 이름을 지정할 필요가 없다는 점에 유의하세요. 튜플 멤버의 이름은 이미 함수의 반환 타입의 일부로 지정되어 있거든요.

옵셔널 튜플 반환 타입

함수에서 반환되는 튜플 타입이 전체 튜플에 대해 "값 없음"을 가질 가능성이 있다면, 전체 튜플이 nil일 수 있음을 나타내기 위해 옵셔널 튜플 반환 타입을 사용할 수 있습니다. 튜플 타입의 닫는 괄호 뒤에 물음표를 붙여서 옵셔널 튜플 반환 타입을 작성하면 돼요. 예를 들어 (Int, Int)? 또는 (String, Int, Bool)? 같은 식이죠.

위의 minMax(array:) 함수는 두 개의 Int 값을 포함하는 튜플을 반환합니다. 하지만 이 함수는 전달받은 배열에 대해 안전성 검사를 수행하지 않아요. 만약 array 인자가 빈 배열을 포함한다면, 위에서 정의한 대로라면 minMax(array:) 함수는 array[0]에 접근을 시도할 때 런타임 오류를 트리거할 거예요.

빈 배열을 안전하게 처리하려면, minMax(array:) 함수를 옵셔널 튜플 반환 타입으로 작성하고 배열이 비어 있을 때 nil을 반환하세요.

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil } // 배열이 비어있으면 nil 반환
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1..<array.count] {
        if value < currentMin {
            currentMin = value
        } else if value > currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}
swift

옵셔널 바인딩을 사용하여 이 버전의 minMax(array:) 함수가 실제 튜플 값을 반환하는지 아니면 nil을 반환하는지 확인할 수 있습니다.

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// "min is -6 and max is 109" 출력
swift

암시적 반환이 있는 함수

함수 본문 전체가 단일 표현식이라면, 함수는 암시적으로 해당 표현식을 반환한답니다. 예를 들어, 아래의 두 함수는 동일한 동작을 합니다.

func greeting(for person: String) -> String {
    "Hello, " + person + "!" // 인사말 문자열을 암시적으로 반환
}
print(greeting(for: "Dave"))
// "Hello, Dave!" 출력
 
 
func anotherGreeting(for person: String) -> String {
    return "Hello, " + person + "!" // return 키워드를 사용하여 명시적으로 반환
}
print(anotherGreeting(for: "Dave"))
// "Hello, Dave!" 출력
swift

greeting(for:) 함수의 전체 정의는 함수가 반환하는 인사 메시지이므로, 이 짧은 형식을 사용할 수 있어요. anotherGreeting(for:) 함수는 return 키워드를 사용하여 더 긴 함수처럼 동일한 인사 메시지를 반환하죠.

단일 return 라인으로 작성하는 모든 함수는 return을 생략할 수 있습니다.

Shorthand Getter Declaration에서 볼 수 있듯이, 속성의 getter도 암시적 반환을 사용할 수 있답니다.