나의 기록, 현진록

[Swift] 13. 클로저 / 기본 클로저 / 후행 클로저 본문

Programming/Swift

[Swift] 13. 클로저 / 기본 클로저 / 후행 클로저

guswlsdk 2021. 7. 27. 14:56
반응형

스위프트 프로그래밍 3판 - 야곰 지음

 

스위프트에서 함수형 프로그래밍 패러다임을 접할 때 첫걸음으로 꼭 알아야 할 녀석이다. 클로저를 이해해야 스위프트 함수형 프로그래밍 패러다임 스타일을 좀 더 명확하게 이해할 수 있습니다.

 

클로저는 일정 기능을 하는 코드를 하나의 블록으로 모아놓은 것을 말합니다. 뭔가 함수랑 비슷하다고 느껴지면 맞다. 사실 함수는 클로저의 한 형태입니다.

 

클로저는 변수나 상수가 선언된 위치에서 참조를 획득하고 저장할 수 있습니다. 이를 변수나 상수의 클로징이라고 하며 클로저는 여기서 착안된 이름입니다.

 

클로저의 세 가지 형태가 있습니다.

  • 이름이 있으면서 어떤 값도 획득하지 않는 전역함수의 형태
  • 이름이 있으면서 다른 함수 내부의 값을 획득할 수 있는 중첩된 함수의  형태
  • 이름이 없고 주변 문맥에 따라 값을 회득할 수 없는 축양 문법으로 작성된 형태

클로저의 모양 중 하나가 함수입니다.

 

먼저 클로저의 문법을 살펴보기 전 클로저를 얼마나 다양하게 표현할 수 있는지 잠깐 살펴보겠습니다. 

 

  • 클로저는 매개변수와 반환 값의 타입을 문맥을 통해 유추할 수 있기 때문에 매개변수와 반환 값의 타입 생략할 수 있습니다.
  • 클로저에 단 한 줄의 표현만 들어있다면 암시적으로 이를 반환 값으로 취급합니다.
  • 축약된 전달인자 이름을 사용할 수 있습니다.
  • 후행 클로저 문법을 사용할 수 있습니다.

 

클로저 표현 방법은 클로저가 함수의 모습이 아닌 하나의 블록의 모습으로 표현될 수 있는 방법을 의미합니다. 클로저 표현 방법은 클로저의 위치를 기존으로 크게 기본 클로저 표현과 후행 클로저 표현이 있습니다. 또 각 표현 내에서 가독성을 해치지 않는 선에서 표현을 생략하거나 축약할 수 있는 방법이 있습니다.

 

 

13.1 기본 클로저

기본 클로저 내용을 포함하여 앞으로 sorted(by:) 메서드를 이용해 동일한 기능을 하는 코드를 어떻게 간결하게 표현하는지 알아보겠습니다. 스위프트 표준 라이브러리에는 배열의 값을 정렬하기 위해 구현한 sorted(by:) 메서드가 있습니다. 이 메서드는 클로저를 통해 어떻게 정렬할 것인가에 대한 정보를 받아 처리하고 결괏값을 배열로 돌려줍니다. 단순히 정렬만 하기 때문에 입력받은 배열의 타입과 크기가 동일합니다. 기존의 배열은 변경하지 않고 정려된 배열을 새로 생성하여 반환해줍니다. 

 

func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]

 

String 타입의 배열에 이름을 넣어 영문 알파벳을 내림차순으로 정렬하려고 합니다.

let names: [String] = ["wizplan", "eric", "hyunjin", "jenny"]

 

sorted(by:) 메서드는 (배열의 타입과 같은 두 개의 매개변수를 가지며 Bool 타입을 반환하는) 클로저를 전달인자로 받을 수 있습니다. 반환하는 Bool 값은 첫 번째 전달인자 값이 새로 생성되는 배열에서 두 번째 전달인자 값보다 먼저 배치되어야 하는지에 대한 결괏값입니다. true를 반환하면 첫 번째 전달인자가 두 번째 전달인자보다 앞에 옵니다. 

 

전달받는 두 전달인자는 정렬에 참고할 값이고, 반환될 값은 첫 번째 전달인자가 앞으로 배치될지 뒤로 배치될지에 대한 Bool 타입 값입니다.

func backwards(first: String, second: String) -> Bool{
    print("\(first) \(second) 비교중")
    return first > second
}

let names: [String] = ["wizplan", "eric", "hyunjin", "jenny"]

let reserved: [String] = names.sorted(by: backwards)
print(reserved)

 

 

만약 first 문자열이 second 문자열보다 크다면 backwards(first: second:) 함수의 반환 값은 true 값이 될 것이다. 즉, 값이 더 큰 first 문자열이 second 문자열이 second 문자열보다 앞쪽에 정렬되어야 한다는 뜻입니다.

그러나 first > second라는 반환 값을 받기 위해 너무 많은 표현을 사용했습니다. 함수 이름부터 매개변수 표현까지 부가적인 표현도 많습니다. 이를 클로저 표현을 사용해서 조금 더 간결하게 표현하겠습니다.

 

클로저 표현은 통상 아래 형식을 따릅니다.

{(매개변수들) -> 반환타입 in
    실행코드
}

 

클로저도 함수와 마찬가지로 입출력 매개변수를 사용할 수 있습니다. 매개변수 이름을 지정한다면 가변 매개변수 또한 사용 가능합니다. 

 

이제 backwards(first: second:) 함수를 클로저로 표현해보겠습니다.

 

let names: [String] = ["wizplan", "eric", "hyunjin", "jenny"]

let reversed: [String] = names.sorted(by: {(frist: String, second: String) -> Bool in 
return frist > second
})

print(reversed)

코드가 훨씬 간결해지고 직관적으로 바뀌었다. 

 

이렇게 프로그래밍하면 sorted(by:) 메서드로 전달되는 backward(first: second) 함수가 어디에 있는지, 어떻게 구현되어 있는지 찾아다니지 않아도 됩니다. 물론 반복해서 같은 기능을 사용하려면 함수로 구현해두는 것도 나쁘지 않습니다. 선택입니다. 

 

 

13.2 후행 클로저

보다 조금 더 클로저를 읽기 쉽게 바꿔볼 수 있습니다. 함수나 메서드의 마지막 전달인자로 위치하는 클로저는 함수나 메서드의 소괄호를 닫은 후 작성해도 됩니다. 클로저가 조금 길어지거나 가독성이 조금 떨어진다 싶으면 후행 클로저 기능을 사용하면 좋습니다. Xcode에서 자동완성 기능을 사용하면 자동으로 후행 클로저로 유도합니다. 

 

단,  후행 클로저는 맨 마지막 전달인자로 전달되는 클로저에만 해당되므로 전달인자로 클로저 여러 개를 전달할 때는 맨 마지막 클로저만 후행 클로저로 사용할 수 있습니다. 또한 sorted(by:) 메서드처럼 단 하나의 클로저만 전달인자로 전달하는 경우에는 소괄호를 생략해줄 수 있습니다. 

 

또 매개변수에 클로저가 여러 개 있는 경우, 다중 후행 클로저 문법을 사용할 수 있습니다. 다중 후행 클로저를 사용하는 경우, 중괄호를 열고 닫음으로써 클로저를 표현하며, 첫 번째 클로저의 전달인자 레이블은 생

let names: [String] = ["wizplan", "eric", "hyunjin", "jenny"]

//후행 클로저의 사용
let reserved: [String] = names.sorted() { (frist: String, second: String) -> Bool in 
    return frist > second
}

//sorted(by:) 메서드의 소괄호까지 생략 가능합니다.
let reserved: [String] = names.sorted { (first: String, second: String) -> Bool in
    return frist > second
}

func doSomething(do: (String) -> Void, onSuccess: (Any) -> Void, onFaliure: (Error) -> Void){
    //do something
}

doSomthing{ (someString: String) in
    //do closure
} onSuccess: { (result: Any) in
    //success closure
} onFailure: { (error: Error) in
    //failure closure

}

print(reversed)

략합니다. 

반응형