본문 바로가기

TIL

[TIL] Swift Concurrency 등장 배경

GCD vs Swift Concurrency

GCD

현재 Swift에서 동시성 프로그래밍에 가장 많이 사용되고 있는 API입니다. 이 GCD에는 크게 한 번에 하나의 테스크를 순차실행하는 Serial DispatchQueue와 많은 작업을 동시에(concurrent) 실행하게 해주는 Concurrent DispatchQueue라는 두 가지가 존재합니다. Queue라는 이름에서 유추할 수 있듯이 실행되는 순서는 선입선출(First-In First-Out)입니다.

Swift Concurrency

Async/Await를 필두로 주로 클로저로 이루어저 있는 GCD의 코드 가독성을 향상시키고, 휴먼 에러로 발생할 수 있는 에러 처리를 컴파일 타임(compile time)에 처리하여 에러 처리에 대한 휴먼 에러를 미연에 방지하기 위해 등장하였습니다.

GCD와 Swift Concurrency의 차이점

가독성

기존 방식으로 네트워크 코드를 구성 시 completion handler와 탈출 클로저를 이용하였는데, 이를 과도하게 사용하게 되면 아래 코드에서 보실 수 있듯이 이른바 콜백 지옥을 경험하게 됩니다. 이는 개발자로 하여금 가독성을 떨어뜨려 유지보수 측면에서 불리하는 단점이 있습니다. 

콜백 지옥 예시

 

이를 해결하기 위해 Swift Concurrency를 이용하면 아래 코드 예시와 같이 들여쓰기가 없어저 가독성을 월등히 향상됨을 알 수 있습니다.

Concurrency 적용 예시

에러 핸들링

에러 핸들링 역시 기존 GCD 방식에서 처리하려고 하다보면 가끔 누락되는 경우가 발생합니다. 더구나 컴파일이 이를 잡아내지 못해 기능 개발에 급급하다 보면 개발 속도 안정성에 지장이 생기기 마련입니다. 다음 코드는 네트워크로 이미지를 다운로드 받아오는 코드 예시 입니다. 아래 코드에서 status code가 200이 아닐 때 처리는 오류가 누락되어 있는 것을 확인해 보실 수 있습니다.

func fetchThumnailURLSession(completion: @escaping (Result<UIImage, JackError>) -> Void) {
        let request = URLRequest(url: Network.url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5)
        
                URLSession.shared.dataTask(with: request) { data, response, error in

                    guard let data else {
                        completion(.failure(.unknown))
                        return
                    }

                    guard error == nil else {
                        completion(.failure(.invalidResponse))
                        return
                    }

                    guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                        // 누락된 부분
                        return
                    }

                    guard let image = UIImage(data: data) else {
                        completion(.failure(.invalidImage))
                        return
                    }

                    completion(.success(image))
                }.resume()
    }

 

하지만 Swift Concurrency를 활용하면 throw 키워드를 활용하여 에러가 발생하면 컴파일이 이를 잡아내 처리할 수 있게 도와주어 에러 처리 누락에 대한 걱정을 덜어줍니다. 다음 코드 예시는 위 GCD로 작성된 코드를 Swift Concurrency로 재작성한 코드 예시입니다.

func fetchThumnnailAsyncAwait() async throws -> UIImage {
    let request = URLRequest(url: Network.url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 5)

    let (data, response) = try await URLSession.shared.data(for: request)

    guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
    	// 에러발생 시, 누락되었던 부분을 잡아냄
        throw JackError.invalidImage
    }

    guard let image = UIImage(data: data) else {
        throw JackError.invalidImage
    }
    print("11111")

    return image

}

 

마무리

이와같이 Swift Concurrecy가 도입된 배경에 대해 간단히 설명해 보았습니다.

비록 비루한 글이지만 끝가지 읽어주셔서 감사합니다.

 

참고 사이트

https://engineering.linecorp.com/ko/blog/about-swift-concurrency

'TIL' 카테고리의 다른 글

[TIL] Alamofire 로깅(Logging)  (0) 2024.05.09
[TIL] Combine VS RxSwift  (0) 2024.05.09
[TIL] Alamofire을 이용한 Token 갱신 적용  (0) 2024.05.04
[TIL] TikTok 피드 구현 과정  (0) 2024.04.12
[TIL] Single에 대한 간단 내용 정리  (0) 2024.04.08