포스트

(Deep Dive) Closure

1. 클로저란?

  • 클로저는 본질적으로 이름이 없는 익명 함수이다.

우리가 보통 함수를 정의 할때

1
2
3
4
5
func functionName (parameter : parameterType) -> returnType {
    //code
    
    return output
}

이런식으로 구현하였다.

이렇게 뭔가 정제되지않은 값이 들어가면 함수를 통해 정제된 값으로 나오게된다.

함수는 패키지화 된 기능의 집합체 이다.

그리고 함수에 이름을 부여하여 이후에 해당 기능이 필요하면 언제든지 이름을 호출하여 사용 할 수 있다.

함수는 함수를 입력값으로 받아 출력을 할 수 있다.

코드를 보자

1
2
3
4
5
6
7
8
import UIKit

func calculator (n1 : Int, n2 : Int) -> Int {
    
    return n1 + n2
}

calculator(n1: 2, n2: 3) // 5

두값을 더하는 계산기 함수를 만들었다.

그럼 이번에 저 계산기의 입력값을 함수로 받고 싶다면 어떻게 해야할까?

우선 add라는 함수를 하나 더 만들어 주었다.

1
2
3
4
func add (no1 : Int, no2:Int) -> Int {
    
    return no1 + no2
}

함수를 다른 함수의 파라미터로 넣기 위해선 이함수를 데이터타입으로 간추려야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func calculator (n1 : Int, n2 : Int) -> Int {
    
    return n1 * n2
}

func add (no1 : Int, no2:Int) -> Int {
    
    return no1 + no2
}

// 곱을 구하는 함수도 추가로 생성해주었다.
func multiply (no1 : Int, no2 : Int) -> Int {
    
    return no1 * no2
}

현재는 두 함수 모두 Int type의 파라미터를 받고, Int type으로 리턴을 하고 있다.

즉 이걸 데이터 타입으로 표현을 해보면

(Int, Int) -> Int 인걸 알 수 있다.

이걸 calculator 함수에 넣어보자.

그리고 매개변수명을 operation으로 해주었다.

return또한 그에 맞게 고쳐준다.

1
2
3
4
5
func calculator (n1 : Int, n2 : Int, operation : (Int, Int) -> Int) -> Int {
    //                                            ---  ---
    //                                             n1   n2
    return operation(Int, Int)
}

그다음 위에있는 return도 바꿔주자!

return operation(n1, n2)

이렇게 되면, n1과 n2가 operation을 통과하게 된다.

그럼 함수를 다시 호출 해보자

1
2
calculator(n1: 2, n2: 3, operation: add) // 5
calculator(n1: 2, n2: 3, operation: multiply) // 6

우선 n1, n2값이 calculator 함수로 가게 된다. 그리고 add 함수를 호출하게되고 그 n1, n2값이 add 함수로 전달이 된다.

뭔가 이렇게 쓰니 코드가 장황 해진다. 이때 클로저를 사용하여 간략하게 표시 할 수 있다.

2. 클로저 함수로 전환하는 방법

다음과 같은 함수가 있다.

1
2
3
4
5
6
7
    func sum (firstNumber : Int, secondNumber : Int) -> Int {
//            -------------------------------------     ---
//                           Input                     output


        return firstNumber + secondNumber
    }

우선 func와 함수의 이름을 지운다.

1
2
3
4
(firstNumber : Int, secondNumber : Int) -> Int {

    return firstNumber + secondNumber
}

그리고 Arrow function 뒤에있는 대괄호({) 이걸 앞으로 옮겨준다 그리고 옮겼던 자리에 in을 적어준다

1
2
3
4
{   (firstNumber : Int, secondNumber : Int) -> Int in
//                                                 --
    return firstNumber + secondNumber
}

이걸 바탕으로 multiply 함수를 클로저 형식으로 바꿔보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import UIKit

func calculator (n1 : Int, n2 : Int, operation : (Int, Int) -> Int) -> Int {
    return operation(n1, n2)
}

func add (no1 : Int, no2:Int) -> Int {
    return no1 + no2
}

func multiply(no1: Int, no2: Int) -> Int {
    return no1 * no2
}


calculator(n1: 2, n2: 3, operation: multiply)

지우면 이렇게 된다.

1
2
3
4
5
6
{ (no1: Int, no2: Int) -> Int in
    return no1 * no2
}


calculator(n1: 2, n2: 3, operation: )

이렇게 지운걸 operation 저기에 옮겨주면서 더 줄일 수 있다!

no1, no2의 데이터 타입을 지워준다.

  • 지워줘도 swift는 리턴 타입을 통해 input의 데이터타입을 추론 할 수있다.
1
2
3
calculator(n1: 2, n2: 3, operation:{ (no1, no2) -> Int in
    return no1 * no2
} )

그 다음엔 Arrow function과 return type과 return 키워드도 지워주자!

이것 역시도 스위프트가 추론을 할 수 있기에 가능한 것이다.

1
calculator(n1: 2, n2: 3, operation:{ (no1, no2) in no1 * no2 }) 

이렇게 바뀌었다.

그리고 매개변수명 역시 바꿀 수 있다.

  • 익명 매개 변수명으로 바꾸자.
    • $0은 첫번째 매개변수를 의미한다.
    • $1은 두번째 매개변수를 의미한다.
1
calculator(n1: 2, n2: 3, operation:{$0 * $1}) 

{$0 * $1}을 트레일링 클로저라고 한다.

1
2
let result = calculator(n1: 2, n2: 3, operation:{$0 * $1}) 
let result = calculator(n1: 2, n2: 3){$0 * $1} 

이렇게 간소화가 된다.

클로저사용의 장점은 코드가 간소화가 되는것이지만 그 간소화로인해 가독성이 떨어지는게 단점이다.

3. 클로저의 실제 활용

배열의 값에 모두 1씩 더해보자.

1
let array = [6,2,3,9,4,1]

swift는 map을 제공하는데 컬렉션 유형의 모든 항목을 변환 할 수있다.

1
2
3
4
5
6
7
8
let array = [6,2,3,9,4,1]

func addOne (n1:Int) -> Int {
    
    return n1 + 1
}
print(array.map(addOne)) // [7, 3, 4, 10, 5, 2]

이걸 위의 방법으로 클로저로 만들어 보자

1
print(array.map{$0 + 1}) // [7, 3, 4, 10, 5, 2]

4. 마무리

일반적인 클로저 표현은 다음과 같다.

1
2
3
{ (parameters) -> return type in
    statements
}
  • 괄호로 입력 파라미터를 감싸준다.
  • -> 를 통해 반환 유형을 명시한다.
  • in은 클로저 바디의 시작을 나타낸다.

클로저에 관한 Docs이다 나중에 한번 읽어보자.

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.