Swift/Swift Language

Swift 언어 가이드 - 트레일링 클로져

batterflyyin 2020. 3. 5. 20:42

클로져 트레일링

만약 여러분이 함수에 클로져 표현식을 함수의 마지막 인자로 전달하고 싶다면, 트레일링 클로져(trailing closure)를 사용하는 것이 좋습니다. 트레일링 클로져는 함수 호출의 소괄호 이후 부터 작성합니다. 소괄호를 벗어나지만 여전히 그 함수의 인자로 인정됩니다. 트레일링 클로져 문법을 사용할 때, 클로져에 인자 레이블을 부여해서는 안됩니다.

func someFunctionThatTakesAClousre(closure: () -> Void) {
    // 실행할 함수 내부 
}
// 트레일링 클로져 없이 함수를 호출 한다면?
someFunctionThatTakesAClosure(closure: {
  // 실행할 클로져 내부
})

// 트레일링 클로져를 사용해 함수를 호출 한다면?
someFunctionThatTakesAClosure() {
  // 실행할 클로져 내부
}

위 섹션에서 살펴본 문자열-정렬 클로져는 sorted(by:) 의 바깥에서 작성되었습니다. 이것 또한 트레일링 클로져입니다.

reversedNames = names.sorted() { $0 > $1 }

만약 클로져 표현식이 함수로 제공되거나 메서드의 유일한 함수로 제공된다면, 트레일링 클로져 앞의 소괄호 쌍 () 을 작성하지 않아도 됩니다.

reversedNames = names.sorted { $0 > $1 }

트레일링 클로져는 클로져가 한 줄에 작성될 수 없을 정도로 길이가 길 때 유용합니다. 예를 들어, 스위프트의 Array 타입은 map(_:) 메서드를 가지고 있습니다. 이 메서드는 클로져 표현식을 유일한 인자로 받습니다. 이 클로져는 배열의 모든 아이템에 한 번씩 호출됩니다. 그리고 다른 매핑된 값을 반환합니다. 매핑의 특성과 반환되는 값의 타입은 클로져가 정하게 됩니다.

모든 배열의 원소에 클로져를 적용하고 나면, map(_:) 메서드는 매핑된 값들을 포함한 새로운 배열을 반환합니다. 이 때, 원본 배열과 똑같은 순서가 유지 됩니다.

어떻게 map(_:) 함수를 트레일링 클로져와 사용하여 Int 배열을 String 배열로 바꾸는지 작성해보겠습니다. 배열 [16, 58, 510]["OneSix", "FiveEight", "FiveOneZero"] 으로 반환됩니다.

let digitNames = [
 0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
 5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

이 코드는 영어 버젼의 숫자와 그 숫자에 대한 딕셔너리를 생성합니다. 또한 정수의 배열을 정의하였습니다. 이제 문자열로 변환할 준비를 마쳤습니다.

여러분은 이제 numbers 배열을 String 값의 배열로 생성할 수 있습니다. 클로져 표현식을 배열의 map(_:) 메서드의 트레일링 클로져로 전달합니다.

let strings = numbers.map { (number) -> String in
                           var number = number
                           var output = ""
                           repeat {
                             output = digitNames[number % 10]! + output 
                             number /= 10
                           } while number > 0
                           return output
                          }
// strings는 [String] 타입으로 추론됩니다.
// 이 상수의 값은 ["OneSix", "FiveEight", "FiveOneZero"] 입니다.

클로져 표현식을 호출한 것은 map(_:) 메서드입니다. 이 클로져 표현식은 배열의 모든 아이템을 한 번씩 호출합니다. 클로져의 입력 파라미터에 타입을 명시할 필요는 없습니다. 왜냐하면 이 타입, number 의 타입은 배열의 값으로부터 추론될 수 있기 때문입니다.

예를 들어, 변수 number 는 클로져의 number 파라미터의 값으로 초기화됩니다. 따라서, 값은 클로져 내부에서 수정됩니다. 클로져 표현식은 또한 반환 타입인 String 을 추론합니다.

클로져 표현식은 배열의 원소를 한 번씩 호출할 때 마다, output 이라는 문자열을 만듭니다. 이것은 number 의 마지막 숫자를 나머지 연산자 (number % 10)으로 계산하며, 이 값은 digitNames 딕셔너리의 적절한 문자열을 찾는데 사용됩니다. 이 클로져는 0보다 큰 어떠한 정수라도 문자열 표현을 만들 수 있습니다.

주의

이 예제에서, digitNames 딕셔너리의 서브스크립트 뒤에 느낌표 (!) 가 사용되었습니다. 왜냐하면 딕셔너리의 서브스크립트는 옵셔널 값을 반환하여 딕셔너리가 키를 찾지 못할 때를 알려주기 위해서 입니다. number % 10 은 항상 digitNames 딕셔너리의 적절한 서브스크립트 키를 가져오는 것이 자명하므로, 강제 언래핑인 느낌표(!)를 사용하였습니다.

이 딕셔너리, digitNames에서 꺼내온 문자열은 output 의 앞에 추가됩니다. 숫자를 거꾸로 추가하여 문자열 버젼으로 만들기 위해서 입니다. ( number % 1016 에는 6 을 반환, 58 에는 8, 510 에는 0 을 반환 )

이후, 변수 number10 으로 나누어 집니다. 왜냐하면 이것은 정수이므로 소수점 이하는 버려지게 됩니다. 따라서 161이 되고, 585 가 되며, 51051 이 됩니다.

이 절차는 number 가 0이 될 때 까지 반복됩니다. 그리고 output 문자열이 클로져에 의해 반환되며 map(_:) 메서드가 반환할 배열에 추가됩니다.

트레일링 클로져 문법을 사용하면 간결하게 클로져의 기능을 캡슐화할 수 있습니다. 따라서, map(_:) 메서드의 소괄호 내에 클로져 전체를 감쌀 필요가 없습니다.


원본 : https://docs.swift.org/swift-book/LanguageGuide/Closures.html