Skip to content

Latest commit

 

History

History
126 lines (104 loc) · 9.31 KB

Ch6_advanced2.md

File metadata and controls

126 lines (104 loc) · 9.31 KB

함수를 파라미터로 전달하기

앞에서 함수가 일급 객체이기 때문에 변수에 할당할 수 있다고 했는데, 일급 객체라는 단어를 굳이 외울 필요는 없다. 다만 함수를 변수에 할당할 수 있게 되면서부터 다양한 특징이 생긴 것이므로 '함수를 변수에 할당할 수 있다.'라는 것만은 잘 알아두어야 한다. 이렇게 함수를 변수에 할당할 수 있으면 변수 이름으로 함수를 호출할 수 있다는 것이라고 앞에서 알아보았다. 따라서 변수에 함수를 할당할 수 있기 때문에 함수에 파라미터로 전달하거나 함수에서 반환할 때 사용하는 변수에도 함수가 들어갈 수 있다. 즉, 함수를 파라미터로 전달하거나 함수에서 반환할 수 있게 된다.

그림

이 특징이 코드를 아주 복잡하게 만든다. 그림을 보면 함수로 전달되는 파라미터로 변수가 아닌 함수를 전달하고 있고 함수에서 반환하는 값도 변수가 아닌 함수이다. 즉, 변수 대신 함수가 사용되었다고 불 수 있다. 그러면 함수를 파라미터로 전달하는 방법을 살펴보겠다.

func add(first a:Int, second b:Int, callback:((Int) -> Void)) {
    let result = a + b
    callback(result)
}

새로 입력한 코드가 이해되는가? 함수를 정의할 때 사용하는 소괄호 안에 다시 소괄호가 사용되었다. add 함수로 전달되는 파라미터의 개수를 하나 더 늘린 후 callback이라는 이름의 파라미터를 전달했다. 그런데 이 파라미터는 함수 자료형으로 되어 있다. 즉, Int 자료형으로 된 파라미터 하나를 전달 받고 반환 값은 없는 함수가 전달될 것이라고 선언되었다. 그리고 add 함수 안에서 return 키워드를 사용해 반환하던 결과 값은 반환하지 않도록 선언되었다.

add 함수에서는 결과 값을 반환하는 방식이 변경된 것이다. 즉, return 키워드가 사라졌으니 이 함수를 호출했을 때 변수에 할당할 결과 값은 반환되지 않는다. 그리고 더하기를 한 결과는 callback 이라는 함수를 호출하면서 세 번째 파라미터로 전달된다. callback은 파라미터로 전달 받은 함수이다. 앞에서 함수가 변수에 할당될 수 있고 이렇게 변수에 할당된 함수가 다른 함수의 파라미터로 전달될 수 있다고 했다. 따라서 파라미터로 전달 받은 callback 함수는 add 함수 안에서 호출할 수 있으며, callback 함수 이름 뒤에 소괄호를 붙여 호출할 수 있다.

그러면 이제 이 함수를 호출해 보겠다. add 함수를 호출할 때 파라미터로 전달해야 하므로 그 함수는 다음 코드처럼 show라는 이름으로 정의한다.

func show(result:Int) {
    print("add 함수를 실행한 결과 : \(result)")
}

add 함수를 정의할 때 세 번째 파라미터로 전달될 함수는 ((Int) -> Void)로 정의되어야 한다고 했으므로 show 함수를 정의할 때 파라미터로 정수 값 하나를 전달하도록 하고 반환되는 값은 없도록 만든다. show 함수 안에서는 add 함수를 실행한 결과를 콘솔에 출력하도록 print 함수를 호출한다. 마지막으로 다음 코드처럼 add 함수를 호출한다.

add(first: 10, second: 10, callback: show)

출력값) add 함수를 실행한 결과 : 20

add 함수를 호출할 때 필요한 세 번째 파라미터가 함수이므로 세 번째 파라미터의 이름인 callback 뒤에 콜론(:)을 붙인 후 show라는 함수 이름을 입력한다. 코드를 실행하면 위의 호출값 같은 결과를 불 수 있다.

add 함수가 실행되고 나면 add 함수 안에서 show 함수를 호출해 준다. 이런 함수를 흔히 '콜백 함수(Callback function)'라고 부른다. 콜백 함수는 함수를 실행하면서 파라미터로 전달하는 함수를 가리키며, 함수가 실행된 결과는 콜백 함수로 전달 받아 처리한다. 이번에는 파라미터로 전달되는 함수가 두 개인 경우를 만들어 보겠다. 파라미터로 전달되는 함수가 두 개면 훨씬 복잡한 형태가 된다.

func successPrint() {
    print("성공")
}

func failPrint() {
    print("실패")
}

var names = [1:"소녀시대", 2:"여자친구", 3:"티아라"]
func getData(index:Int, success sCallback : () -> Void, fail fCallback : () -> Void) -> String? {
    if let name = names[index] {
    sCallback()
        return name
    }
    defer {
        fCallback()
    }
    return nil
}

var result = getData(index: 1, success: successPrint, fail: failPrint)
var result2 = getData(index: 5, success: successPrint, fail: failPrint)

출력값) 성공
      실패

getData 함수로 전달되는 파라미터는 총 세 개이며 그중에서 두 개는 함수이다. 두 개의 함수 모두 파라미터로 전달되는 값과 함수에서 반환하는 값이 없으므로 () -> Void 자료형으로 명시한다. 이 함수는 첫 번쨰 파라미터로 숫자를 받은 후 이 숫자를 사용해 names 딕셔너리에 들어 있는 이름을 확인하여 반환하는 기능을 구현한 것이다. 딕셔너리에 원하는 이름이 없을 수 있으니 함수의 반환 자료형은 옵셔널로 선언되었다.

getData 함수 안에는 성공과 실패했을 때 필요한 각각의 기능이 들어 있다. names 딕셔너리 안에 원하는 값이 있으면 sCallback 함수를 실행한 후 찾아낸 이름을 반환하도록 한다. 즉, sCallback 함수는 성공했을 때 실행되는 콜백 함수이다. if 문에서는 name 상수에 값을 바인딩하여 처리한다. if 문에서 찾지 못한 경우에는 if 문 다음에 있는 코드가 실행된다. 따라서 실패했을 떄 실행하는 fCallback 함수가 실행되고 nil이 반환된다.

defer 키워드는 콜백 함수를 실행할 때와 return 키워드로 결과 값을 반환하면서 함수가 종료될 때의 동시성 문제를 해결하기 위해 사용된다. 즉, 콜백함수를 실행하기 전에 return 키워드가 실행되어 함수가 먼저 종료되는 문제를 막기 위해서 약간의 시간차를 만들어 주는 것이다. 이 getData 함수는 두 번 실행하는데 한 번은 1, 또 다른 한 번은 5라는 값을 첫 번째 파라미터로 전달한다.

코드를 실행하면 위의 출력값 같이 '성공'과 '실패'라는 메시지를 표시한다.

여기에서 중요한 것은 콘솔에 메시지를 출력하는 것은 콜백 함수이며, 이 콜백 함수는 getData 함수를 호출하면서 파라미터로 전달된다는 점이다.

그런데 successPrint 함수와 failPrint 함수처럼 함수를 미리 정의한 후 다른 함수를 호출하는 방식은 매번 함수를 미리 만들어 놓아야 한다는 점이 번거롭다. 만약 successPrint 함수가 getData 함수를 호출할 때 한 번만 사용될 함수라면 successPrint 함수를 만들면서 동시에 getData 함수를 호출하면 훨씬 효율적이다. 따라서 스위프트는 함수의 파라미터로 전달되는 함수를 호출할 때 정의할 수 있도록 지원한다. 다음 코드를 추가한다.

var result3 = getData(index:3,
                      success: {
                        () -> Void in
                        print("성공했습니다.")
                    },
                      fail: {
                        () -> Void in
                        print("실패했습니다.")
                    })

출력값) 성공했습니다.

새로 추가한 코드는 앞에서 입력한 코드처럼 getData 함수를 호출하지만 성공했을 때와 실패했을 때 호출될 함수들을 정의하면서 동시에 파라미터로 전달한다. 그런데 잘 보면 코드의 모양이 아주 이상하다. 이런 모양의 코드가 만들어진 것은 다음과 같은 형식으로 정의할 수 있기 때문이다.

{
  함수의자료형 in
  함수코드
}

중괄호 안에 함수의 자료형과 in 그리고 함수를 구현하는 코드가 입력된다. 그런데 함수 코드가 많아지면 이런 형태로 만든 코드는 아주 복잡하게 보일 수 있다. 따라서 이런 방식으로 함수를 전달하는 코드는 파라미터로 전달되는 함수 내의 코드가 간단한 경우에 주로 사용한다. 만약 함수의 코드가 복잡하고 그 양이 많다면 함수를 미리 정의해 두는 것이 좋다.

어쨌든 이런 형식이 가능한 것은 스위프트가 '클로저(Closure)'라는 것을 지운하기 때문이다. 클로저는 이름이 생략된 간단한 함수의 형태를 가지고 있다. 이 클로저에 대해서는 다음 단락에서 자세하게 살펴볼 것이다. 일단 여기서는 이런 형식으로 함수를 전달할 수 있다고 알아두면 된다.

success라는 이름으로 전달되는 함수는 중괄호 안에 () -> Void 자료형으로 선언되었다. 그리고 in 다음에 나오는 코드를 보면 콘솔에 '성공했습니다.'라는 글자를 출력한다. fail이라는 이름의 함수도 마찬가지이다.

Ch6_advanced3.md 파일로 가기

Swift Study Contents로 돌아가기