본문 바로가기

Swift/Swift Language

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

클로져 트레일링

만약 여러분이 함수에 클로져 표현식을 함수의 마지막 인자로 전달하고 싶다면, 트레일링 클로져(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