본문 바로가기

iOS 개발/Swift

Swift Closure(클로져)에 대한 이해

- 이 글은 Swift 개발을 시작한지 얼마 안된 초보 개발자 입장에서, Swift개발을 시작한지 얼마 안된 초보 개발자분들이 이해하시기에 적합하도록 최대한 쉽게 풀어 이야기하였습니다.

- 저 또한 해당 개념을 완벽하게 이해하지는 않았기에, 언제든 해당 글은 수정 될 수 있으며, 잘못된 부분에 대한 지적은 대환영입니다!

 

 

안녕하세요? 이번에는 Closure에 대하여 정리해보도록 하겠습니다. Closure는 저와 같은 iOS를 시작한지 얼마 안된 개발자에게 멘붕을 주는 그런 개념인데요.. 쉽게 풀어 정리해보도록 하겠습니다.

 

1. Closure의 개념

먼저, 클로져는 크게 2가지로 나눌 수 있습니다. 바로, 여러분들이 Swift에서 사용하시는 함수(Named Closure)와, 지금부터 설명드릴 클로져(Unnamed Closure)입니다. 이미 개발하면서 사용하고있는 함수들이 이미 지금 배울 Closure라는 개념안에 포함된다는 말이지요.

하지만 , 지금까지는 함수의 func 키워드 뒤에 함수명이 있었지요? 이런 함수 Name이 없는 클로져가 앞으로 설명드릴 Unnamed Closure 입니다. 실제 개발을 할 때 우리는 함수를 클로져라고 부르지 않아요. 이름이 없는 Closure만을 클로져라고 통칭하지요.

쉽게말해서, 기존에 사용하던 함수에 함수명을 제거한 것을 클로져라고 생각하시면 쉽습니다. 그리고 앞으로 설명드릴 클로져 역시, Swift의 함수처럼 일급함수의 개념을 갖습니다.(cf1) 물론, 함수에는 없는 클로져만의 특징 또한 몇가지 있습니다. 

 

(cf1) 일급함수

1. 함수를 변수나 상수에 저장할 수 있다.

2. 파라미터로 전달할 수 있다.

3. 함수에서 리턴할 수 있다.

 

 

이제 클로져의 표현식을 한번 보겠습니다.

{(Parameters) -> Return Type in

           statements

}

-> 위 처럼, 클로져는 기본적으로 { }로 감싸져 있습니다. 그리고 in키워드 좌측 부분을 클로져 헤드라고 부릅니다. 이 부분에는 클로져의 자료형을 기입합니다. 이는 함수에서의 Function Type과 정확히 일치하는 방식입니다.(ex1)

 

(ex1)

-> 보시다시피, { (str: String) -> String in return "Hello, \(str)" } 라는 클로져를 만들었고, 이를 testClosure라는 상수에 저장하였습니다.(상수에 저장할 수 있다는, 일급 함수의 특성).

이 클로져의 in 좌측에는 (str: String) -> String 이 있는데요.이는 function type과 동일한 형식의 값이 들어가게 됩니다. 입력 parameter가 (str: Strnig)이고, 리턴형이 String인 클로져 라는 것이지요. 

 

여기에 "Test Clousre"라는 스트링을 클로져의 파라미터로 담은 결과값인 "Hello, Test Clousre"라는 String이 결과적으로 result값에 저장되고, 출력되는것을 확인 하실 수 있습니다.

(참고로 closure는 함수 자체를 호출할 때와 달리, argument label을 통하여 호출하지 않고, 그 값 자체만 입력하여 해당 클로져를 호출합니다. testClosure(str: "Test Closure")가 아님. 함수를 상수에 담아서 호출할 때와 동일.)

또한, 클로져는 함수의 파라미터로 직접 입력하기에도 용이합니다. (ex2)

 

(ex2)

-> 함수 startClosure는, "(String) -> String" 을 Function Type으로 갖습니다. 이와 타입이 일치하는 이전에 만들었던 testClosure를, 이 함수의 파라미터로 입력하여 정상적인 텍스트가 출력되는 것을 확인 하실 수 있습니다.

 

이처럼 클로져는, 일급함수의 특성을 이용하여 간단하게 상수나 변수에 저장하고, 파라미터로 전달할 수 있으며, 동일한 방식으로 함수에서 리턴할 수도 있습니다. iOS 개발을 하시다보면, 자연스럽게 closure를 이용하신 적이 있을 것입니다. 그리고 Alert 같은 함수를 사용할 때, 자동완성 이후에 return키를 눌러서 자동완성의 모양이 더 짧아지는(축약되는) 현상을 보신 적이 있으실거에요. 여기서 사용된 것이 Closure의 Syntax Optimization(문법 최적화) 입니다.

 

 

2. Syntax Optimization(문법 최적화)

Swift는 단순한 코드를 지향합니다. 따라서, 컴파일러가 추론할 수 있는 부분은, 대부분 생략을 하게 됩니다. 클로져도 마찬가지입니다. 위에 적은 클로져들은, 문법최적화 방식을 통하여 훨씬 짧게 설계 할 수가 있습니다. Syntax Optimization은 상황에 따라 다르지만, 보통 아래의 다섯가지 스탭으로 모두 축약할 수 있습니다.

 

1. 파라미터 형식과 리턴형식을 생략 할 수 있다.

2. parameter 이름은, Shortand Arguments Name 으로 대체한다.

3. 단일 리턴문만 남은경우, return 키워드를 생략한다.

4. 클로져 파라미터가 마지막 파라미터라면, trailing closure로 작성할 수 있다.

5. 괄호사이에 다른 파라미터가 없다면, 괄호를 생략할 수 있다.

(ex3)

 

(ex3) Syntax Optimization 이전의 클로져 예시. + 축약

-> 위 filter 메서드는, "(String) -> Bool" 타입을 파라미터로 받습니다. filter메서드는 배열에 멤버들에 접근하면서 멤버마다의 String에 접근하여, 이 String을 파라미터로 받은 특정 클로져에 대입하여, Return 값이 True인 항목만으로 다시 배열을 만드는 메서드입니다.

위 메서드의 파라미터로 받은 클로져부분은 결국,

{ (cigaName: String) -> Bool in

return cigaName.contains("Dunhill")

}

입니다.

위 클로져 함수를 다섯가지 스탭으로 축약해보겠습니다.

 

-----------------------------------------------------------

let dunhills = ciga.filter({ (cigaName: String) -> Bool in

return cigaName.contains("Dunhill")

})

 

1. 파라미터 형식과 리턴형식을 생략 할 수 있다.

let dunhills = ciga.filter({ (cigaName) in

return cigaName.contains("Dunhill")

})

 

2. parameter 이름은, Shortand Arguments Name 으로 대체한다.

-> 이 과정에서 in을 생략할 수 있습니다. in 좌측 파라미터에 첫번째 항목을 $0으로 대체 할 수 있으며, 순서대로 $1, $2.... 의 Shontand Arguments Name을 갖습니다.

let dunhills = ciga.filter({

return $0.contains("Dunhill")

})

 

3. 단일 리턴문만 남은경우, return 키워드를 생략한다.

let dunhills = ciga.filter({

           $0.contains("Dunhill")

})

 

4. 클로져 파라미터가 마지막 파라미터라면, trailing closure로 작성할 수 있다.

-> 클로져가 함수의 마지막 파라미터라면, 이를 우측으로 뺄 수 있습니다.

let dunhills = ciga.filter(){

          $0.contains("Dunhill")

}

 

5. 괄호사이에 다른 파라미터가 없다면, 괄호를 생략할 수 있다.

let dunhills = ciga.filter{

          $0.contains("Dunhill")

}

 

축약된 형태를 거꾸로 돌리는 순서는, 5가지 스탭을 역순으로 진행하면 됩니다.

 

다만, 지나친 축약은 코드의 줄 수는 줄어들기는 하지만, 유지보수하는데에 어려움을 겪으실 수 있습니다.

적절한 축약과 주석을 잘 활용하셔서 바람직한 Syntax Optimization와 Closure를 이용하실 수 있으면 좋겠습니다.

 

감사합니다.