Today I Learn

Today I Learn: @escaping 클로저

Goniii 2025. 3. 14. 21:18

Swift에서 클로저(Closure)는 기본적으로 함수의 실행이 끝날 때 자동으로 해제되는 특징을 가짐

하지만 특정 상황에서는 클로저가 함수가 끝난 후에도 유지되어야 하는 경우가 있음

이때 사용하는 것이 @escaping 키워드

→ 클로저의 생존 기간을 함수 밖으로 연장하는 것

1. @escaping이 필요한 이유

  • 기본적으로 Swift의 클로저는 비탈출(Non-Escaping) 방식이다.
  • → 기본적으로 클로저는 함수가 종료되면 메모리에서 자동으로 해제됨
  • 하지만 비동기 작업(Async Task)의 경우 클로저가 함수의 실행이 끝난 후에도 유지되어야 함

 

예제: 네트워크 요청 (비동기 실행)

func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async { // 비동기 백그라운드 스레드에서 실행 시작
        sleep(2) // 네트워크 요청을 시뮬레이션 (2초간 대기)
        let data = "서버에서 받은 데이터"
        
        DispatchQueue.main.async {
            completion(data) // 함수가 종료된 후에도 실행됨 (탈출 클로저 필요)
        }
    }
}
  • fetchData() 함수가 실행된 후에도, completion 클로저는 여전히 DispatchQueue.global().async 내에서 실행됨
  • 즉, fetchData()가 종료된 이후에도 completion이 유지되어야 하기 때문에 @escaping이 필요함

실행 순서 정리

  1. fetchData() 함수 호출 (실행 위치: 메인 스레드)
    • completion 클로저가 인자로 전달되었지만, 아직 실행되지 않음
  2. DispatchQueue.global().async 실행 (실행 위치: 백그라운드 스레드)
    • 비동기(Async) 작업 시작
    • fetchData()는 DispatchQueue.global().async { ... } 실행 후 즉시 종료됨
    • 비동기 코드이기 때문.
  3. sleep(2) 실행 (실행 위치: 백그라운드 스레드)
    • 2초 동안 대기(네트워크 요청 시뮬레이션)
    • 메인스레드에서 동작 중인 fetchData 함수와 비동기 백그라운드 스레드에서 동작 중인 sleep(2)이 동시에 존재함
    • 이 과정에서 메인 스레드는 영향을 받지 않고 계속 실행됨
    • 즉, fetchData 함수는 비동기 작업(2초간 대기)이 끝나기 전에 이미 종료됨
  4. fetchData 함수 종료 (메인 스레드)
  5. let data = “서버에서 받은 데이터” 실행 (백그라운드 스레드)
    • 2초간 대기 후 상수에 값 저장
  6. DispatchQueue.main.async {} 실행 (백그라운드 → 메인 스레드 예약)
    • 메인 스레드에서 completion(data) 실행을 예약(메인 큐에 추가)
    • 즉시 실행되지 않고 메인 스레드의 실행 큐에 추가
  7. completion(data) (메인 스레드)
    • completion(data)는 메인스레드의 작업 대기열(큐)에 추가됨
    • 메인큐는 기존에 실행해야 할 다른 작업들을 먼저 수행하고 있음 (UI 업데이트 등)
    • 따라서 기존 작업(메인 큐에 먼저 들어온 작업)이 끝난 후 completion(data) 실행

 

 

@escaping이 필요한 이유 정리

fetchData() 함수가 DispatchQueue.global().async 호출 후 즉시 종료되며, 내부 작업은 백그라운드에서 2초 후 실행됨

위에서 Swift에서 클로저(Closure)는 기본적으로 함수의 실행이 끝날 때 자동으로 사라지는 특징을 가진다’고 했다

하지만, fetchData 함수는 비동기 백그라운드 스레드의 작업이 끝나기 전에 종료되기 때문에 파라미터로 받은 completion 클로저가 함께 사라지게 된다

따라서 백그라운드 스레드에서 completion 클로저를 호출하면 오류가 발생한다

왜냐? completion 클로저는 fetchData 함수가 끝나는과 동시에 사라지기 때문에 이미 없다!

그래서 함수가 끝나더라도 힙 영역에 저장해두는 @escaping을 사용하여 fetchData 함수가 끝나더라도 클로저를 사용할 수 있게 해주는거다