iOS

[iOS] 클로저(Closure) 알아보기

스벅 보안관 2024. 6. 13. 10:15

안녕하세요. 스벅 보안관입니다.

이번 시간에는 클로저에 대해 알아보겠습니다. 사실 오래전에 Swift 문법 공부를 하다가 클로저에 대해 잠깐 공부를 했었는데, 너무 이해가 안되서 때려쳤습니다..ㅋㅋㅋㅋ. 그래도 어느정도 개발을 진행하다보니 클로저에 대해 이해를 많이 하게 된거같아 이렇게 클로저에 대한 블로그를 작성해보려고 합니다. 사실 개발하다보면 코드안에 나도 모르는 클로저를 사용하고 있던 경우가 많아서 어쩔 수 없이(?) 공부를 따로 하긴 해야합니다,, ㅎㅎ

 

 

클로저(Closure)란?

클로저는 코드에서 독립적인 블록으로, 일급 객체로 취급됩니다.

(일급 객체란 변수에 할당하거나, 함수의 인자로 전달하거나, 함수의 반환 값으로 사용할 수 있는 객체입니다. 따로 다룰 예정..)

 

클로저는 변수나 상수에 할당할 수 있고, 함수나 메서드에서 인수로 전달하거나 반환할 수 있습니다.

이렇게만 보면 약간 이해가 안될 수 있는데 간단한 예시를 보여드리겠습니다.

func studyiOS() {
    print("iOS")
    print("개발중")
}

let develop1 = studyiOS

 

위 코드에서 studyiOS는 전역 함수입니다.

develop1에 studyiOS 함수를 할당할 수 있습니다. 전역 함수는 클로저의 한 형태로, () -> Void 타입의 함수입니다.

 

 

그런데 제가 만약 이렇게 코드를 짰다면?

let develop2 = {
    print("iOS")
    print("개발중")
}

 

 

사실 이것도 develop1과 똑같은 기능을 같습니다. studyiOS 함수와 동일한 기능을 수행하지만, 이름이 없습니다.

이걸 바로 "클로저" 라고 하는겁니다. 위에 언급했던 것 처럼 변수에 할당한 객체가 된 것 입니다.

 

 

 

그럼 또 만약 이렇게 코드를 짰다면..?

let develop3 = { () -> Void in
    print("iOS")
    print("개발중")
}

 

사실 이것도 develop1과 똑같은 기능을 같습니다 ㅋㅋ. 

develop2에 경우는 반환하는게 따로 없기 때문에 () -> Void이 생략된 코드라고 보면 됩니다.

in 앞에 있는 "() -> Void" 위치를 "클로저 헤더"라고 하고,

아래에 실행코드 위치를 "클로저 바디"라고 합니다.

 

정리하면,

  • 이 develop1, 2, 3 다 같은 말임
  • Function Type이 () -> Void 이면 생략도 가능
  • in 앞에 있는 () -> Void 위치를 "클로저 헤더"라고함
  • in 뒤에 있는 코드 위치를 "클로저 바디"라고함

 

 

클로저(Closure) 사용 예시

클로저를 자주 사용하는 예시 중 하나인 콜백함수로 예시를 들겠습니다.

import Foundation

// 비동기 네트워크 요청 함수 정의
func fetchData(completion: @escaping (Data?, Error?) -> Void) {
    // 네트워크 요청을 비동기적으로 수행
    DispatchQueue.global().async {
        // 예시를 위해 지연 시간 추가
        sleep(2)
        
        // 네트워크 요청 결과 (예시 데이터와 오류)
        let data = Data("예시 데이터".utf8) // 데이터 생성
        let error: Error? = nil // 오류 없음
        
        // 메인 스레드에서 콜백 함수 호출
        DispatchQueue.main.async {
            completion(data, error)
        }
    }
}

// fetchData 함수 호출 및 콜백 처리
fetchData { data, error in
    if let error = error {
        print("오류 발생: \(error)")
    } else if let data = data {
        let dataString = String(data: data, encoding: .utf8)
        print("데이터 수신: \(dataString ?? "데이터 없음")")
    }
}

 

이러한 콜백 함수가 있다고 했을 때,

fetchData 함수는 비동기로 네트워크 요청을 수행하고, 결과를 completion 클로저를 통해 호출자에게 전달합니다. completion 클로저는 Data?와 Error?를 매개변수로 받으며, 네트워크 요청이 완료된 후 호출됩니다.


그렇다면 저 @escaping은 뭘까?

@escaping 키워드는 클로저가 함수의 실행이 끝난 후에도 실행될 수 있음을 나타냅니다. @escaping은 말 그대로 "탈출"이라는 기능을 갖기에, 함수가 종료 되어도 탈출하여 실행할 수 있도록 합니다.

 


 

오늘의 결론

생각보다 쉽지 않다.