🟥 클로저란?
▶️ 정의 및 문법
- 정의: "어떤 태스크를 수행하기 위한 코드 블럭"으로, 함수와 유사하다.
- 문법: { 클로저 선언부 in 클로저 실행부 } 의 형태로 작성한다.
- 클로져 선언부: 파라미터와 리턴타입 명시
- 클로져 실행부: 실행 코드 작성
{ (parameters) -> returnType in
statements
}
- Swift에서는 클로져, 함수를 타입으로 사용할 수 있다.
-> 따라서, 변수에 할당할 수 있고, 다른 함수 파라미터로 전달할 수도 있다. (aka. First Citizen)
▶️ 함수 vs 클로저
함수는 이름이 정의되지만, 클로저는 이름이 없다.
"hello"를 출력하는 함수와 클로저를 다음과 같이 작성하고 실행할 수 있다.
함수 | 클로저 |
▶️ hello1(name: String) 라는 이름을 가진 함수 func hello1(name: String) { print("hello, \(name)") } hello1(name: "Julie") // hello, Julie |
▶️ 별도의 이름 없이 'hello2'라는 상수에 할당된 클로저 let hello2 = { (name: String) in print("hello, \(name)") } hello2("Jake") // hello, Jake |
▶️ say1 이라는 상수에 함수 할당, 호출 let say1 = hello1 say1("Yurim") // hello, Yurim |
▶️ say2 라는 변수에 함수 할당, 호출 let say2 = hello2 say2("Jimin") // hello, Jimin |
*주의 : 함수는 호출 시 argument lable을 작성해야 하지만, 클로저는 작성하지 않는다.
🟥 클로저를 상수/변수에 할당할 때
1️⃣ Case 1: input parameter(❌), returning value(❌)
인풋 파라미터와 반환값이 모두 없는 경우이다.
형태 : { statement }
▶️ 예제 : "hello!😀" 를 출력하는 클로저
let hello = {
print("hello!😀")
}
hello() // hello😀
2️⃣ Case 2: input parameter(⭕️), returning value(❌)
인풋 파라미터는 있고, 반환값은 없는 경우이다.
형태 : { (parameter: type) in statement } or { (parameter: type) -> Void in statement }
▶️ 예제 : 이름을 파라미터로 받아, "이름 hello!😀"를 출력하는 클로저
let heyHello = { (name: String) in
print("\(name) hello!😀")
}
heyHello("Julie") // Julie hello!😀
3️⃣ Case 3: input parameter(⭕️), returning value(⭕️)
인풋 파라미터와 반환값 모두 있는 경우이다.
형태 : { (parameter: type) -> returnType in statement }
▶️ 예제 : id가 블랙리스트와 일치하면 "You're banned" 출력하고 false를 반환하는 클로저
let checking = { (id: String) -> Bool in
if id == "bad person" {
print("You're banned")
return false
}
print("\(id) hello!😀")
return true
}
checking("bad person") // 출력: You're banned , return: false
checking("Julie") // 출력: Julie hello!😀, return: true
🟥 클로저를 함수의 파라미터로 받을 때
* 예제 : 블랙리스트 체킹
1️⃣ 방법 1: 클로저를 상수/변수에 따로 만들어 놓은 후 함수에 사용
1. 먼저 클로저를 따로 만든다.
// checking 클로저 만들기
let checking = { (id: String) -> Bool in
if id == "bad person" {
print("You're banned")
return false
}
print("\(id) hello!😀")
return true
}
2. 함수를 생성한다.
// validate 함수 만들기
func validate(id: String, check: (String) -> Bool) -> Bool {
print("Validation 준비중...")
let isValid = check(id)
return isValid
}
// 함수 호출
validate(id: "Yurim", check: Checking)
[더보기] 코드 해석
▶️ 함수 선언부
func validate(id: String, check: (String) -> Bool) -> Bool
-> 함수명 : validate
-> 파라미터 2개 : id (string 타입), check (클로저)
-> 함수 리턴값 : Bool
▶️ 함수 실행부
▷ print("Validation 준비중...")
-> 함수가 실행되면 "Validation 준비중..."을 출력
▷ let isValid = check(id)
-> check에 저장된 클로저 실행됨.
-> id가 "bad person"이면 "You're banned"를 출력하고 isValid에 false 대입
-> 그렇지 않으면 "id hello!😀"를 출력하고 isValid에 true 대입
▷ return isValid
-> isValid에 저장된 값(true / false)를 반환
2️⃣ 방법 2: 클로저를 함수 수행 시 바로 작성하기
// 함수 선언
func validate2(id: String, check: (String) -> Bool) -> Bool {
print("Validation 준비중...")
return check(id)
}
// 함수 실행
let validationResult2 = validate2(id: "User000", check: { (id: String) -> Bool in
id != "User000" {
})
print(validationResult2)
이 코드의 결과로는
// Validation 준비중...
// false
가 출력된다.
🟥 클로저를 반환하는 함수 작성법
클로저를 반환하는 함수를 만들어보겠다.
사람 이름을 인자로 받고, 그 사람을 블랙리스트로로서 사용자 이름과 대조하는 클로저를 반환하는 함수는 다음과 같다.
func returnChecking(name: String) -> (String) -> (Bool) {
return { (id: String) -> (Bool) in
id != name
}
}
만약 Jack이 블랙리스트인 클로저를 얻고 싶다면, 다음과 같이 함수를 실행하면 된다.
let jack = returnChecking(name: "Jack")
클로저가 잘 저장되었는지 확인해보자.
func validate3(id: String, check: (String) -> Bool) -> Bool {
print("Validation 준비중...")
return check(id)
}
let validationJack = validate3(id: "Jack", check: jack)
print(validationJack)
// Validation 준비중...
// false
잘 실행 된다.
🟥 클로저 짧게 쓰기 (경량 문법)
함수 수행시, 클로저를 더 짧게 쓸 수 있는 방법들이다.
let validationResult = validate(id: "User001", check: { (id: String) -> Bool in
if id == "User000" {
return false
}
return true
})
1️⃣ 클로저 실행부(statement) 줄이기 - 코드 리팩터링
클로저 실행부의 코드를 더 간결하게 개선할 수 있다.
before | { (id: String) -> Bool in if id == "User000" { return false } return true } |
after | ➡️ { (id: String) -> Bool in return id != "User000" } |
2️⃣ 클로저 선언부 줄이기 - 매개변수 타입, 리턴타입 생략
클로저 실행부를 보면 어떤 값이 반환될지 유추가 가능하므로,
선언부에서 매개변수 타입과 리턴타입을 다음과 같이 생략해도 된다.
즉, 인자값과 반환값의 타입 어노테이션 대신, 컴파일러의 타입 추론을 사용한다는 것이다.
before | let validationResult = validate(id: "User001", check: { (id: String)-> Bool in return id!= "User000" } ) |
리턴 타입 생략 | ➡️ let validationResult = validate(id:"User001",check: { (id:String) in return id != "User000" } ) |
매개변수 타입, 괄호 생략 | ➡️ let validationResult = validate(id: "User001", check: { id in return id != "User000"}) |
3️⃣ 매개변수, in 생략 -> $0 사용
매개변수가 생략되면 매개변수명 대신 $0, $1,... 와 같은 이름으로 할당된 '내부 상수'를 이용할 수 있다.
이는 입력받은 인자값의 순서대로 매칭된다.
매개변수가 생략되면 남는 것은 실행 구문이므로,
실행부와 선언부를 분리해주던 'in'이 필요 없어진다.
따라서 매개변수명과 더불어 in 키워드 역시 생략할 수 있다.
또한 return 도 생략할 수 있다.
before | let validationResult5 = validate(id: "User001", check: { id in return id != "User000" }) |
매개변수명, in 생략 | ➡️ let validationResult5 = validate(id: "User001", check: { return $0 != "User000" }) |
return 생략 | ➡️ let validationResult5 = validate(id: "User001", check: { $0 != "User000" }) |
4️⃣ 트레일링 클로저 - 인자 레이블 생략, 꼬리로 붙이기
트레일링 클로저(Trailing Closure)는 함수의 마지막 인자값이 클로저일 때, 이를 인자값 형식으로 작성하는 대신 함수의 뒤에 꼬리처럼 붙일 수 있는 문법을 의미한다. 이때 인자 레이블은 생략된다.
before | let validationResult5 = validate(id: "User001", |
after | ➡️ let validationResult5 = validate(id: "User001") { $0 != "User000"} |
⚠️ 트레일링 클로저는 함수의 마지막 인자값에만 적용된다!!
-> 클로저 두 개를 인자로 받는 함수일지라도, 맨 마지막 클로저만 트레일링 클로저 문법을 적용할 수 있다.
🔎 참고: 함수의 인자가 클로저 1개일 경우, 괄호도 생략 가능하다!
1. 원본
value.sort(by: {(s1, s2) in return s1 > s2 })
2. 트레일링 클로저value.sort() { (s1, s2) in return s1 > s2 }
3. 괄호 삭제 (<- 괄호 안에 들어갈 인자가 없고, 트레일러 클로저 구문이라는 게 명확하기 때문)value.sort {(s1, s2) in return s1 > s2 }
=> 협업하는 사람의 능숙도에 따라 줄여서 쓰도록 한다.