클로저
클로저
클로저은(는) CoddyKit의 무료 Swift Academy 강의입니다. 이것은 1개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Swift Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Swift Academy 강의에는 총 1개의 강의가 포함되어 있습니다.
소개
안녕하세요.
클로저는 독립적으로 사용할 수 있는 코드 블록입니다. 클로저를 코드에 매개변수로 전달하거나 코드 내부에서 사용할 수 있습니다.
클로저는 함수의 한 종류입니다. 코드에 전달하여 사용할 수 있습니다. 다른 프로그래밍 언어의 람다와 비슷합니다.
메모리 관리
가장 중요한 점 중 하나는 클로저가 자신이 정의된 문맥에 있는 상수와 변수의 값을 저장할 수 있다는 것입니다.
이러한 저장 방식을 클로저 캡처라고 합니다.
Swift는 이 저장 과정에서 메모리를 자동으로 관리합니다.
클로저 타입
클로저에는 여러 타입이 있습니다.
- 전역 함수는 이름이 있는 클로저입니다. 이러한 함수는 어떤 값도 저장하지 않습니다.
- 중첩 함수는 이름과 자신을 감싸고 있는 함수의 값을 전달할 수 있습니다.
- 반면 클로저 표현식은 이름이 없는 클로저이며, 간단한 구문으로 자신을 둘러싼 문맥의 데이터를 저장할 수 있습니다.
클로저 구문
클로저의 구문은 가능한 한 간단하게 만들 수 있습니다. 주요 특징은 다음과 같습니다.
- 클로저의 매개변수 타입을 문맥에서 추론할 수 있습니다.
- 반환값의 타입을 추론할 수 있습니다.
- 클로저를 한 줄로 작성할 수 있습니다.
- 인수 이름을 $ 0 또는 $ 1과 같은 축약형으로 표현할 수 있습니다.
- 후행 클로저 구문을 사용할 수 있습니다.
정보
클로저 표현식은 구문과 간결성에 중점을 두고 클로저를 인라인으로 작성하는 것을 의미합니다.
클로저 구문
아래 예제 코드에 클로저 구문이 나와 있습니다.
{ (parameters) -> return type in
statements
}
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
//If we didn't use closures, our code structure would be as follows.
var reversedNames = names.sorted(by: backward)
클로저 구문
하지만 클로저를 사용하면 이런 유형의 클로저에 불필요하게 이름을 붙인 전역 클로저를 정의할 필요가 없습니다.
매개변수에 간단한 클로저 표현식을 정의하면 충분합니다. 예제를 살펴보겠습니다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
print(reversedNames)
///sorted method takes closure as parameter.in 키워드
in 키워드를 보면 클로저의 본문이 시작된다는 것을 알 수 있습니다.
in 키워드를 시작하기 전에 클로저가 받는 매개변수와 반환 타입을 정의해야 합니다.
문맥에서 타입 추론하기
문맥에서 타입 추론하기
위 예제에서는 sorted 메서드에 클로저가 전달되므로 매개변수와 반환 타입을 추론하여 클로저 표현식을 이해할 수 있습니다.
따라서 매개변수와 반환 타입을 별도로 정의할 필요가 없습니다.
이 예제에서는 names 배열의 값이 문자열이므로 클로저가 받는 매개변수도 문자열 타입으로 추론됩니다.
마찬가지로 해당 클로저는 정렬을 위해 불리언 타입의 결과를 반환해야 하므로 반환 타입은 불리언으로 추론됩니다.
예제
이를 통해 아래와 같이 더 간단하게 사용할 수 있습니다.
반환 타입을 추론하면 코드에서 화살표도 생략할 수 있습니다.
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
let reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
print(reversedNames)짧은 구문
코드 독자가 혼동하지 않도록 짧은 구문보다 긴 구문을 사용하는 것이 좋습니다.
위 구문은 더 짧게 만들 수 있습니다.
반환 키워드를 제거하면 클로저 표현식으로 반환값을 더 짧게 작성할 수 있습니다.
/*
In this example, the return keyword has been extracted.
It already has single expression.
There is no need to express it as an extra with return.
*/
var names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
print(reversedNames)예제
이 예제에서는 반환 키워드를 생략했습니다.
이미 단일 표현식이 있으므로 반환 키워드로 이를 별도로 나타낼 필요가 없습니다.
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )이름 축약형
구문을 더 짧게 만들 수 있습니다. 위 예제에서는 클로저가 두 개의 매개변수를 받는다고 명시했습니다. (s1과 s2 포함) 매개변수의 타입은 추론됩니다.
이제 축약형을 사용하면 이러한 매개변수를 직접 작성하고 정의하는 수고를 덜 수 있습니다.
또한 매개변수 정의와 반환값 전달이 없으므로 in 키워드도 생략할 수 있습니다.
let names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: { $0 > $1 } )
print(reversedNames)
// In this example $0 will shortcut the first parameter and $1 will shortcut the second parameter.예제 간소화
var names = ["Coddy", "Kit"]
let reversedNames = names.sorted(by: >)
print(reversedNames)
// Greater than operator takes two parameters and the rotation type is squat.TRAILING CLOSURE
후행 클로저
클로저 함수에 전달할 매개변수가 함수의 마지막 인수이고 클로저 표현식이 긴 경우, 이를 후행 클로저 문으로 작성하는 것이 더 편리할 수 있습니다.
후행 클로저를 구문 설탕으로 사용할 수 있습니다. 코드의 가독성을 높이는 데 사용됩니다.
대부분의 iOS 메서드에서 마지막 매개변수는 클로저입니다.
마지막 매개변수
예를 들어 이 예제에서는 마지막 매개변수가 클로저입니다.
class Animator {
class func animate(withDuration duration: Double, animations: () -> Void) {
animations()
}
}
class MyViewController {
func start() {
// With the traditional method, we can call this fund as follows.
Animator.animate(withDuration: 1, animations: { [unowned self] in
print("red")
})
}
}
MyViewController().start()위 코드는 가독성이 떨어지므로 다음과 같이 작성할 수 있습니다
위 코드는 가독성이 떨어지므로 다음과 같이 작성할 수 있습니다.
import Foundation
Thread.sleep(forTimeInterval: 1)
print("Background color changed to red")예제
후행 클로저 구문을 사용할 때는 인수 레이블을 작성하지 않아도 됩니다.
이제 클로저가 함수 호출 구문의 일부가 되었기 때문입니다.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}구문
후행 클로저 구문은 구문 설탕이라고 생각할 수 있습니다. 코드를 더 읽기 쉽게 만들어 줍니다.
/*
In the example below, animations
are expressed as closure.
*/
public class func animate(withDuration: TimeInterval, animations: () -> Void)
// In traditional usage, we need to make a call like the following.
UIView.animate(withDuration: 1, animations: { [unowned self] in
self.view.backgroundColor = UIColor.red
})
/*
In this way, both the readability of the code
increases and we avoid the use of extra brackets.
If it is as above and the last parameter of the method
is closure, Swift allows to make a call like the one below.
*/
UIView.animate(withDuration: 1) { [unowned self] in
self.view.backgroundColor = UIColor.red
}코드를 테스트하는 데 다음 코드를 사용할 수 있습니다
코드를 테스트하는 데 다음 코드를 사용할 수 있습니다.
func greetThenRunClosure(name: String, closure: () -> ()) {
print("Hello, \(name)!")
closure()
}
greetThenRunClosure(name: "Paul") {
print("The closure was run")
}클로저가 값 캡처하기
클로저가 값 캡처하기
클로저를 둘러싼 환경에 있는 변수의 값을 보관할 수 있습니다. 클로저 본문에서 이러한 변수의 값을 변경하고 접근할 수도 있습니다.
이 변수들이 원래 정의된 범위에 더 이상 존재하지 않더라도 가능합니다.
이어서
일반적으로 중첩 함수와 클로저 표현식은 이 데이터를 함께 보관할 수 있습니다. 중첩 함수와 클로저 표현식은 외부 함수의 데이터를 저장할 수 있습니다.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen())
// returns a value of 10
print(incrementByTen())
// returns a value of 20
print(incrementByTen())
// returns a value of 30이어서
클로저를 클래스 속성에 할당하면 클로저가 해당 인스턴스에 대한 참조를 보관하기 시작합니다.
따라서 클로저와 인스턴스 사이에 강한 참조 순환이 생깁니다.
메모리 누수가 발생하지 않도록 각별히 주의해야 합니다.
주의
클로저와 함수는 참조 타입입니다.
@escaping
클로저를 비동기적으로 사용하는 여러 상황에서 이 기능이 필요할 수 있습니다.
Swift 초기 버전(1과 2)에서는 Swift 매개변수의 클로저가 이스케이핑 클로저였으며, 이후에는 이스케이핑되지 않는 클로저가 되었습니다. 이스케이핑되지 않는 클로저를 전달하려면 이를 명시적으로 지정해야 했습니다.
이후 버전에서는 메모리 사용 측면에서 효율을 높이기 위해 클로저 매개변수를 이스케이핑되지 않는 클로저로 설정했습니다.
이제 이스케이핑 클로저를 정의하려면 @escaping 키워드를 사용해야 합니다.
@ESCAPING 키워드
@ESCAPING 키워드
함수가 반환된 후에도 함수에 할당된 클로저를 호출하려면 @escaping 키워드를 사용해야 합니다.
기본적으로 함수에 정의된 클로저는 참조 타입이지만 참조 횟수를 증가시키지 않습니다. 호출 시 클로저의 수명이 끝나므로 참조 횟수를 별도로 늘릴 필요가 없기 때문입니다.
하지만 함수 호출 후에도 매개변수로 전달한 클로저가 필요하다면 @escaping 키워드로 표시해야 할 수 있습니다.
nonEscaping
먼저 nonEscaping 클로저가 어떻게 작동하는지 살펴보겠습니다.
이 클로저를 함수에 매개변수로 전달하면 함수 본문에서 클로저가 실행된 후 컴파일러로 제어가 반환됩니다.
함수가 반환되면, 즉 실행이 완료되면 클로저의 범위를 벗어나 메모리에서 해제됩니다.
이스케이핑 클로저 사용
이스케이핑 클로저 사용
클로저 함수를 인수로 전달하면 클로저 함수가 반환되고 실행이 완료된 후에도 클로저가 계속 유지됩니다.
클로저가 실행될 때까지 메모리 공간을 차지합니다. 이스케이핑 클로저에는 여러 유형이 있습니다.
축하합니다
축하합니다 🎉
레슨의 마지막까지 왔습니다.
다음 레슨에서 뵙겠습니다 😎

자주 묻는 질문
“클로저” 강의는 무료인가요?
네 — “클로저” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Swift Academy 강의 전체를 잠금 해제할 수 있습니다. Swift Academy 강의에는 총 1개의 강의가 포함되어 있습니다.
“클로저”에서 뭘 배우나요?
클로저 브라우저에서 직접 실행하는 실습 코드로 Swift Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Swift Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Swift Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 1개 중 1번째 강의입니다.
“클로저” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Swift Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Swift Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.