iOS

[Swift] 클로저란?

2022. 1. 4. 21:43

스위프트에는 '클로저(Closure)' 라는 개념이 존재한다.

 

클로저는 코드의 블럭이라고 볼 수 있는데, 우리가 흔히 사용하는 함수도 클로저이다.

 

클로저는 크게 Named Closure  와 UnNamed Closure 이렇게 2가지로 나눠진다.

우리가 주로 클로저를 말할땐 UnNamed Closure 를 지칭하고 함수는 Named Closure 에 포함된다.

 

클로저의 기본 구조는 아래와 같다.

{ (매개변수 리스트) -> 반환타입 in
	실행할 코드
}

 

클로저는 1급 객체로, 변수 형태로 저장할 수 있고 함수의 파라미터로도 넘길 수 있다.

 

여기서 말하는 1급 객체란 함수형 프로그래밍에서 쓰이는 개념으로, 아래의 조건을 만족하는 객체를 말한다.

 

1. 변수나 상수에 저장 및 할당이 가능하다.

2. 함수의 파라미터로 전달이 가능하다.

3. 함수에서 리턴이 가능하다.

 

위 조건들에 대한 예시를 각각 보자.

 

1. 변수나 상수에 저장 및 할당이 가능하다.

// closureSample 상수에 할당
let closureSample = { (city: String) -> String in
	"I'm in \(city)"
}

// 할당된 클로저 실행
closureSample("서울")

 

2. 함수의 파라미터로 전달이 가능하다.

// 상수에 클로저 할당
let sayHello = { (name: String) -> String in
	"Hello \(name)"
}

// 상수에 클로저 할당
let sayGoodbye = { (name: String) -> String in
	"Goodbye \(name)"
}

// 함수의 파라미터로 클로저를 전달받도록 정의
func greet(name: String, sayWhat: (String) -> String) {
	sayWhat(name)
}

// 함수 파라미터로 클로저 전달
// 리턴값 : Hello 이름
greet(name: "Jaebin", sayWhat: sayHello)

// 함수 파라미터로 클로저 전달
// 리턴값 : Goodbye 이름
greet(name: "Jaebin", sayWhat: sayGoodbye)

// 상수 클로저를 전달하지 않고 직접 클로저를 구현해 전달 가능
greet(name: "Jaebin", sayWhat: {( displayName: String) -> String in
	"How are you \(displayName)?"
})

 

3. 함수에서 리턴이 가능하다.

// 함수의 리턴값으로 클로저 반환
func saySomething(word: String) -> (String) -> String {
	return { (word) -> String in
    	"I said \(word)"
    }
}

// 리턴된 클로저를 상수에 할당
let say = saySomething("Hello")

// 클로저 호출
// 리턴값 : I said Hello
say()

 

지금까지만 봤을때는 클로저가 너무 복잡해보이고 왜 사용하는지도 모를 것이라 생각한다.

하지만 이 길고 복잡한 클로저는 축약이 가능하다.

 

 

반환 타입 생략

 

3번 예시를 보자.

위 saySomething  함수에서 리턴되는 클로저에서 우리는 리턴값이 당연히 String 형식이라는 것을 알 수 있다.

이는 컴파일러도 판단할 수 있기 때문에 반환 타입을 명시하지 않아도 된다. 따라서 아래와 같이 반환 타입을 생략하고 표현이 가능하다.

// 축약 전
func saySomething(word: String) -> (String) -> String {
	return { (word) -> String in
    	"I said \(word)"
    }
}

// 축약 후
func saySomething(word: String) -> (String) -> String {
	return { (word) in
    	"I said \(word)"
    }
}

 

트레일링 클로저

 

2번 예시를 보자.

마지막 호출 방법인 직접 클로저를 작성하는 방식에서 축약이 가능하다.

클로저가 함수의 마지막 매개변수일 시 이름을 생략하고 클로저를 구현할 수 있다.

// 축약 전
greet(name: "Jaebin", sayWhat: {( displayName: String) -> String in
	"How are you \(displayName)?"
})

// 축약 후
greet(name: "Jaebin") {( displayName: String) -> String in
	"How are you \(displayName)?"
}

 

인자명 단축

 

3번 예시를 보자.

예시에서는 클로저로 넘기는 인자명이 word 로 짧지만 긴 인자명일 수 있다.

이 때, 인자명을 모두 적지 않고 아래와 같이 축약이 가능하다.

// 축약 전
func saySomething(word: String) -> (String) -> String {
	return { (word) -> String in
    	"I said \(word)"
    }
}

// 축약 후
func saySomething(word: String) -> (String) -> String {
	return { (word) -> String in
    	"I said \($0)"
    }
}

인자 순서대로 $0, $1, $2.. 와 같이 번호가 자동으로 매겨져 이를 이용해 인자명을 단축할 수 있다.