[iOS] 비동기

Zoe·2023년 12월 4일
0

iOS

목록 보기
27/39

이번 글에서는 Swift에서
Async/Await 기능이 도입되기 전, 비동기 작업을 처리하는 방법과
Async/Await 기능이 도입된 후, 달라진 점에 대해 공부해보자.

네트워킹 작업을 위해서는 비동기 코드가 필수적이다.
지금까지는 completion handler를 주로 사용해왔다. 그러다가 클로저 내부 클로저, 이런 식으로 클로저가 계속 반복되어 사용될 수 밖에 없는 상황을 마주하게 되었다.
예시를 들자면

    func uploadImageToFirebase(image: UIImage, uploadPath: String, completion: @escaping (String?) -> Void) {
        getPreviousImageRef(in: uploadPath + "/") { previousImageRef in
            if let imageData = image.jpegData(compressionQuality: 0.8) {
                let metadata = StorageMetadata()
                metadata.contentType = "image/jpeg"
                storageRef.putData(imageData, metadata: metadata) { (metadata, error) in
                    if let error = error {
                    } else {
                        previousImageRef?.delete { error in
                            if let error = error {
                            } else {
                            }
                        }
                        storageRef.downloadURL { (url, error) in
                            if let error = error {
                            } else if let url = url {   
                                completion(url.absoluteString)
                            }
                        }
                    }
                }
            }
        }
    }

위 코드는 firebase에 이미지를 업로드하는 코드 중 일부를 발췌해온 코드이다.
이전 이미지 참조값을 가져올 때도 클로저를 사용하고, firebase storage에 데이터를 업로드할 때도 클로저를 사용하고, 이전 참조값을 삭제할 때도 클로저를 사용하고, image url을 다운로드할 때도 클로저를 사용한다.

이처럼 기존의 completion handler를 이용한 코드는 가독성을 떨어뜨리고, 콜백 지옥에 빠진다는 단점이 존재한다. 에러 핸들링 자체도 복잡하게 작성된다는 단점도 있다.

이런 단점을 해결하기 위해
Async Await 가 Swift 5.5 에 등장하게 되었다.(🚨주의 : iOS 15이상부터 적용 가능)

먼저 위 코드를 async, await를 이용해서 수정해보자.
getPreviousImageRef, putData, delete, downloadURL 함수가 비동기 버전으로 존재한다고 가정하고 수정한다.

func uploadImageToFirebase(image: UIImage, uploadPath: String) async throws -> String? {
    do {
        let previousImageRef = try await getPreviousImageRef(in: uploadPath + "/")
        guard let imageData = image.jpegData(compressionQuality: 0.8) else {
            return nil
        }
        let metadata = StorageMetadata()
        metadata.contentType = "image/jpeg"
        _ = try await storageRef.putData(imageData, metadata: metadata)
        try await previousImageRef?.delete()
        let url = try await storageRef.downloadURL()
        return url.absoluteString
    } catch {
        throw error
    }
}

async 는 해당 함수가 비동기적으로 작업을 처리하는 함수임을 나타내는 키워드이다. async 함수 타입이라고 한다.
async를 키워드를 사용해서 해당 함수가 비동기적으로 수행됨을 나타냈지만, 내부에 있는 모든 작업이 비동기적으로 수행되는 것은 아닐 수 있다.
그래서 특정 작업에 대해서 await를 사용해 해당 작업은 비동기적으로 진행된다고 명시적으로 알려주어야 한다.

async, await 키워드를 이용해서 기존 코드의 단점을 해결한 것은 분명하다. 하지만 iOS 버전이 15이상에서 사용 가능하고, 기존 메서드를 async 메서드로 래핑해야할 수도 있다는 단점이 존재한다. 결론은 적재적소에 잘 사용해야하는 것이 중요하다는 것이다.

profile
iOS 개발자😺

0개의 댓글