티스토리 뷰

iOS/Swift

[iOS/Swift] @escaping closure

yanni13 2024. 9. 26. 23:01

 

 

네트워크 요청이나, 비동기 작업할 때 아래와 같은 코드를 본 적이 있을 것이다

 

func deleteUserProfile(completion: @escaping (Result<Void, Error>) -> Void) -> Void

 

실제로 엄청 많이 사용되기도 하고, 자주 사용되는 만큼 개념을 잘 알고 사용해야 한다

 

따라서 @escaping 클로저가 무엇인지에 대해 알아보자!

 

 

📚 들어가기 전

escaping closure를 알기 위해선 클로저에 대해서 기본적으로 알고 있어야 한다!

 

클로저를 다루면서 escaping closure 클로저에 대해 아주 간략하게 포스팅을 해놨었는데 한번 읽어보고 지금 포스팅을 읽으면 더 이해하기 쉬울 것이다.

 

 

[iOS/Swift] Closure

이전 포스팅에서는 life cycle에 대해서 알아보았는데 오늘은 스위프트의 클로저에 대해서 알아보겠다! 스터디에서 가져온 질문은 아래와 같으며 답변하고 정리하는 식으로 블로그 포스팅을 진

yanni13.tistory.com

 

 

💡 escaping closure

 

escape(탈출) 말 그대로 함수가 종료된 뒤 실행하는 클로저이다.

 

보통 비동기 작업을 할 때 클로저를 사용해서 다루곤 한다.

서버에서 받은 값을 성공적으로 가져왔을 때와, 실패했을 때 어떤 동작을 실행할 건지를 다뤄주어야 하기 때문이다.

 

하지만 함수는 작업이 시작한 후에 실행되며 작업이 완료될 때까지 클로저를 호출하지 않기 때문에 escaping 클로저를 통해서 함수가 종료된 후에 실행할 수 있도록 하는 것이다!

 

 

이렇게 말하면 이해가 되지 않을 수도 있으니 코드로 한번 살펴보자!

func orderPizza(completion: @escaping () -> Void) {
    print("피자 주문을 받았습니다.")
    
    // 피자 준비 시간을 시뮬레이션 (5초)
    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        completion()  // 5초 후에 고객에게 연락
    }
    
    print("주문 접수가 완료되었습니다.")
}

orderPizza {
    print("피자가 준비되었습니다! 찾으러 와주세요.")
}

print("주문 후 다른 일을 합니다.")

 

 

위와 같은 코드를 실행시키면 다음과 같은 순서대로 출력이 나온다.

  1. 피자 주문을 받았습니다
  2. 주문 접수가 완료되었습니다.
  3. 주문 후 다른 일을 합니다.
  4. 피자가 준비되었습니다! 찾으러 와주세요.

 

즉 orderpizza 함수가 종료되어도 completion클로저가 살아남아 있기 때문에 5초가 지나 나중에 실행될 수 있는 것이다!!!!

 

이건 아주 간단한 print문의 출력을 확인하기 때문에 입력타입이 존재하지 않고 Void타입을 반환하는 클로저로 completion을 받겠다고 선언했기 때문에 어느 정도 이해를 할 수 있지만 실제 네트워크 통신에서는 조금 더 복잡하게 쓰인다.

 

    func loadProfileImage(from url: String, completion: @escaping (Result<UIImage, Error>) -> Void) {
        updateUserProfileUseCase.loadProfileImage(from: url) { [weak self] result in
            switch result {
            case let .success(image):
                DispatchQueue.main.async {
                    self?.userData.value.imageUpdate(image: image)
                    completion(.success(image)) // 이미지 로드 성공 시 completion 호출
                }
            case let .failure(error):
                Log.debug("Failed to load image: \(error)")
                completion(.failure(error)) // 이미지 로드 실패 시 completion 호출
            }
        }
    }

 

실제 프로젝트에 적용된 escaping closure를 가져와 보았다.

간단하게 이 함수가 하는 역할을 소개하자면 url 타입으로 이미지를 가져온 후 성공여부에 따라 콜백을 호출하는 구조이다!

 

 (Result<UIImage, Error>)가 어떤 걸 의미하는지 알아보자

 

 

 

공식문서에 들어가 보면 성공과 실패를 나타내는 값이라고 설명되어 있다.

 

즉 성공했을 때 UIImage타입을 반환해주고, 실패했을 때 error를 반환한다는 뜻이다. 그래서 다시 코드로 살펴보면 switch문을 통해 case로 성공했을때 image타입을 넣어주고 있고, 실패했을때 error 타입을 넣어주고 있다.

 

 

📍non-escaping 클로저와의 차이점

 

@escaping과 non-escaping의 가장 큰 차이는 클로저가 함수의 실행이 끝난 후에도 실행될 수 있느냐의 차이다!

 

non-escaping클로저는 클로저가 함수 내부에서 실행되어야 하며, 함수 실행이 끝나기 전에 클로저가 호출되어야 한다.

swift에서는 클로저를 사용할 때 기본값으로 non-escaping 클로저로 설정되어 있기 때문에 함수 내부에서 밖에 클로저를 호출하지 못한다.

 

func orderPizza(completion: () -> Void) {
    print("피자 주문을 받았습니다.")
    completion()
    print("주문 접수가 완료되었습니다.")
}

orderPizza {
    print("피자가 준비되었습니다! 찾으러 와주세요.")
}

 

아까 피자 예제랑 같은 동작을 하는 non-escaping으로 된 예제이다.

completion 클로저를 매개변수로 받아서 orderPizza함수 내부에서만 completion 클로저가 실행되는 것을 확인할 수 있다.

 

즉, 함수가 종료한 후에는 실행되지 않기 때문에 비동기 처리할 때 non-escaping 클로저를 사용할 수 없다는 것!

비동기를 처리할 수 없기 때문에 escaping예제에 있었던 '피자가 주문 후 다른 일을 합니다'를 none-escaping 예제에서는 반영할 수 없던 것이다~

 

 

 

 


참고

https://janechoi.tistory.com/6

https://velog.io/@parkgyurim/Swift-escaping-closure

https://iosdevlime.tistory.com/entry/iOSSwift-Non-Escaping-Closure-%EC%99%80-Escaping-Closureescaping

 

 

 

'iOS > Swift' 카테고리의 다른 글

[iOS] @MainActor와 DispatchQueue의 차이  (0) 2025.03.06
[iOS] lazy var에 대해 알아보기  (0) 2024.09.21
[iOS/Swift] Closure  (0) 2024.05.08
[swiftUI] NSPredicate  (0) 2024.03.30
[Swift] Optional  (1) 2024.03.28
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함