[iOS] @escaping

z-wook·2023년 7월 15일
post-thumbnail

탈출 클로저에 대해 들어보셨나요? 클로저를 다루다 보면 @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이 필수적으로 필요합니다.

profile
🍎 iOS Developer

0개의 댓글