탈출 클로저에 대해 들어보셨나요? 클로저를 다루다 보면 @escaping이 붙은 클로저를 볼 수 있는데 해당 클로저가 탈출 클로저입니다. 클로저와는 어떤 차이점이 있을까요? 아래의 예시를 통해 설명해 드리겠습니다.
(간단한 예시를 위해 강제 언래핑을 사용한 점 양해 부탁드립니다.)
class testClass {
var completionHandler: (() -> Void)!
func withoutEscaping(completion: () -> Void) {
completion()
}
func withEscaping(completion: @escaping () -> Void) {
completionHandler = completion
}
}
let test = testClass()
test.withoutEscaping {
print("----> 실행 1")
}
test.withEscaping {
print("----> 실행 2")
}
test.completionHandler()
withoutEscaping, withEscaping 메서드는 모두 클로저를 매개변수로 받지만
차이점으로 withEscaping은 @escaping 키워드가 붙어 있습니다.
실행 결과를 보면 아래와 같습니다.
withoutEscaping 메서드는 클로저가 즉시 실행됩니다. 따라서 "----> 실행 1"가 출력됩니다.
withEscaping 메서드는 클로저를 completionHandler에 할당합니다. 그리고 test.completionHandler()를 호출하여 할당된 클로저를 실행합니다. 따라서 "----> 실행 2"가 출력됩니다.
@escaping 속성이 지정된 클로저는 함수 밖에서(탈출)도 사용이 가능하기 때문에 탈출 클로저라고 부릅니다.
만약에 @escaping 속성이 지정되지 않았음에도 함수 밖에서 사용하려고 한다면 아래와 같은 에러가 뜹니다. 이 경우는 함수가 종료되고 나서 클로저가 실행될 수 없다는 뜻입니다. (함수 내에서만 사용 가능)
따라서, 탈출 클로저를 사용하면
1. 해당 클로저를 외부 변수에 저장 가능하게 만듭니다.
2. 함수가 종료된 후 실행이 가능하게 만듭니다.
그래서 @escaping이 붙으면 함수 밖에서 사용 가능한 탈출 클로저라는 것을 알았는데 언제 사용할까요??
아마 개발을 하면서 네트워크 통신을 해봤다면 아래와 같이 completion을 본 적 있을 것입니다.
translateManager.translate(from: sourceText) { [weak self] translatedText in
guard let self = self else { return }
self.resultLabel.text = translatedText
}
struct TranslatorManager {
var sourceLanguage: Language = .ko
var targetLanguage: Language = .en
func translate(
from text: String,
completion: @escaping (String) -> Void
) {
guard let url = URL(string: "https://openapi.naver.com/v1/papago/n2mt") else { return }
let requestModel = TranslateRequest(
source: sourceLanguage.languageCode,
target: targetLanguage.languageCode,
text: text
)
let headers: HTTPHeaders = [
"X-Naver-Client-Id": Info.id,
"X-Naver-Client-Secret": Info.pw
]
AF.request(url, method: .post, parameters: requestModel, headers: headers)
.responseDecodable(of: TranslateResponse.self) { response in
switch response.result {
case .success(let result):
completion(result.translatedText)
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
위의 코드에서 completion이 @escaping으로 선언되어 있는 부분입니다.

해당 코드는 네트워크 통신을 통해 번역된 결과를 completion 클로저를 호출하여 전달한 후 UI에 표시하는 클로저가 포함된 예시입니다.
해당 코드의 활용 예시와 같이 클로저를 사용하여 비동기적인 처리를 진행하고, 번역 결과를 completion 클로저를 통해 외부로 전달하기 위해서 @escaping이 필수적으로 필요합니다.