클로저가 클로저인 이뉴는 “close over” 입니다. 클로버 범위 내에서 변수와 상수를 “닫을” 수 있다는 뜻이죠. 닫는다? 뭘 닫는다는 걸까요?
저는 “닫을” 을 “연산할 수” 혹은 “해결할 수” 라고 이해했습니다.
그러니까 클로저 범위 내에서 변수와 상수를 연산을 할 수 있다. 해결을 할 수 있다. 로 인지하고 있습니다.
용어 하나를 설명하자면,
“클로저 범위에 상수와 변수가 들어왔다.” == “캡쳐되었다.” 라고 합니다.
클로저에 대한 또 다른 이해는 “이름 없는 함수” 입니다. 함수의 역할을 하지만 함수의 이름이 없습니다. 이걸 왜 이용하는 걸까요?
제 생각은 코드량을 줄여주고, 코드의 로직을 바로 볼 수 있다는 장점에 사용하는 것이라고 봅니다.
( 다른 이유로는 다른 사람의 코드를 읽기위해서 )
간단하게 클로저를 정의하겠습니다.
var multiplyClosure: (Int, Int) -> Int
multiplyClosure
이 친구는 두 개의 Int 값을 받고 Int를 리턴하고 있습니다.
플레이그라운드에서 실행하기 위해서는 다음과 같이 정의해주어야 합니다.
var multiplayClosure = { (a: Int, b: Int) -> Int in
return a * b
}
a와 b 라는 값을 받습니다. 타입은 Int 입니다. 리턴값으로 Int를 리턴합니다. 이전과 다르게 “in” 이라는 친구가 생겼죠. 위 코드를 구어적 표현으로 하자면 다음과 같습니다.
"multiplayClosure" 를 정의할께. 근데 값은 a와 b를 받고 리턴은 Int로 해. 아 맞다 a와 b도 Int야. 근데 리턴을 어떻게 처리할 거냐고? 그건 'in' 내부 를 보면 된단다.
이제 클로저를 사용해보겠습니다.
let result = multiplayClosure(4, 2)
// result: 8
xcode를 통해서 Definition을 보신 경험이 있다면, (기본 라이브러리든 다른사람의 코드든) $ 을 본 경우가 있을 겁니다. 저도 처음봤을 때, 뭔가 했던 기억이 있네요. 그런 것들은 swift에서 약속된 “대명사” 같은 것들 입니다. 이것에 대해서 설명드릴께요.
먼저 기본형에서 “return”을 생략한 경우입니다.
multiplayClosure = { (a: Int, b: Int) -> Int in
a * b
}
return 만 사라졌죠. 그래도 한 줄일 경우 return이라고 추론을 통해 자동으로 처리해줍니다.(누가? swift)
좀 더 축약을 해볼게요.
multiplayClosure = { (a, b) in
a * b
}
a와 b를 받아서 a*b로 리턴한다. 여전히 코드의 로직은 이전과 동일합니다. 다만 Type 들이 사라졌죠. swift에서 타입 추론을 적극적으로 활용했다고 볼 수 있겠습니다.
이제 극한으로 가봅니다.
multiplayClosure = {
$0 * $1
}
초반부에 말씀드린 “$”가 나왔습니다. 앞으로 $를 보면 인자를 뜻하는 구나.. 라고 생각하시면 됩니다.
0번째 인자와 1번째 인자를 받겠구나, 그리고 그 둘을 곱해서 리턴하는구나. 라고 이해하시면 됩니다. 처음 보면 익숙치 않아서 그런 것 뿐이지 귀납적으로 경험이 쌓이다보면 역으로 이해되는 부분이니 눈 도장 찍고 넘어가시면 됩니다.
지금까지 배운 것들에 복잡성을 한 술 추가해보겠습니다.
func operateOnNumbers(_ a: Int, _ b: Int,
operation: (Int, Int) -> Int) -> Int {
let result = opertaion(a, b)
print(result)
return result
}
“oprateOnNumbers” 메소드는 인자로 3 가지를 받네요.
a, b, operation
a와 b는 직관적으로 Int입을 알 수 있는데, operation은 뭐지? 라고 하실 수 있습니다. 어려울수록 쪼개서 보면 쉽습니다.
(Int, Int) -> Int
Int 값 두 개를 받아서 Int를 리턴하는 것을 ‘인자’ 로 받는구나…
함수 전체는 Int로 리턴합니다.
오케이. 그러면 한번 사용해보죠.
let addClosure = { (a: Int, b: Int) in
a + b
}
opertaeOnNumbers(4, 2, operation: addClosure)
4 == a
2 == b
operation == addClosure
모두 type이 일치하므로 정상적으로 동작합니다.
아까 클로저는 이름이 없는 함수라고 했죠. 그래서 이렇게 작성해도 동일합니다.
func addFunction(_ a: Int, _ b: Int) -> Int {
a + b
}
opertaeOnNumbers(4, 2, addFunction)
위 예시는 클로저 아래 예시는 메소드 입니다.
이제 코드에 축약을 계속 해보겠습니다,
opertaeOnNumbers(4, 2, opertaion: { (a: Int, b: Int) -> Int in
return a + b
})
operateOnNUmbers(4, 2, opertaion: +)
operateOnNumbers(4, 2, opertaion: { $0 + $1 }
operateOnNumbers(4, 2) {
$0 + $1
}
이렇게 축약하는 것의 명칭은 “trailing closure syntax.” 라고 합니다. ( 다른 개발자와 소통하기 위해 명칭을 꼭 알아두는 것이 좋더라구요.)
클로저는 어찌보면 도움을 주는 기술입니다. 본질적으로는 "함수"라고 퉁치고 이해하셔도 될 겁니다. 다만, 정량적으로 코드를 많이 작성하게 될 때, 좀 더 코드를 간결하고 줄일 수 있는 방법을 찾을 때, 그런 상황에서 "클로저"를 떠올리시면 실전에서 도움이 되지 않을까 조심스레 조언해봅니다.