[iOS | Swift] 동시성 프로그래밍, Swift Concurrency vs. GCD

someng·2024년 2월 6일
0

iOS

목록 보기
33/33

Objective-C에서 비동기 작업을 위해 자주 사용하던 GCD(Grand Central Dispatch)단점을 파악해보고,
이를 보완하기 위한 뉴페이스(?) Swift Concurrency와 비교해보자.

Swift Concurrency

WWDC 2021 에서 새로 소개된 동시성 프로그래밍 API.
Swift Concurrency는 동시성 프로그래밍을 가독성이 좋은 깔끔한 코드로 작성하고자 도입된 개념이다. asyncawait 키워드를 이용해 비동기 테스크 종료 후 코드를 작성할 수 있다.
await 키워드로 인해 중지되면 이후에 사용해야 하는 데이터를 힙(heap) 영역에 저장해 두고, 이후에 다시 힙 영역에서 해당 데이터를 가져와 사용한다.

1. 불안정한 에러 핸들링

에러 처리를 위해 모든 case에서 completion handler 호출이 필요하다. 개발자의 실수로 completion handler 호출을 빠뜨린다면 에러 처리에 누락이 발생한다. 😱

또한, 코드 내부에서 self 키워드에 접근해야 한다면 retain cycle(참조 사이클) 발생 가능성을 고려해야 한다. 그렇다고 무분별한 [weak self]의 사용은 런타임 오버헤드를 발생시킬 수도 있고, nil을 체크해야 하는 수고를 거쳐야 한다.

✅ Swift Concurrency로 작성하면 에러 핸들링은 throw로, 데이터 전달은 return으로 분리할 수 있다. 이런 식으로 작성하면 guard let - else 구문에서 completion handler를 누락하는 실수를 방지할 수 있다.

2. 코드 가독성 저하

비동기 작업이 연쇄적으로 일어나는 경우, 코드의 가독성이 떨어진다.

간단한 예제로 비교해보자.

💥 GCD를 이용한 코드

func makeCrunchyCookie(completion: @escaping ((Cookie) -> Void)) {
    makeDough { dough in // ✅ (1)
        self.chillDough(dough: dough) { ripedDough in // ✅ (2)
            self.bakeCookie(dough: ripedDough, time: bakeTime) { cookie in // ✅ (3)
                self.drawFace(cookie: cookie) { crunchyCookie in // ✅ (4)
                    completion(crunchyCookie)
                }
            }
        }
    }
}

➡️ 예제에서는 4개의 간단한 로직이라 읽기 어렵지 않지만, 로직이 조금만 복잡해져도 클로저의 늪에 빠지게 된다😵!

💥 Swift Concurrency를 이용한 코드

func makeCrunchyCookie() async throws -> Cookie {
    let dough = try await makeDough() // ✅ (1)
    let ripedDough = try await chillDough(dough: dough) // ✅ (2)
    let cookie = try await bakeCookie(dough: ripedDough, time: bakeTime) // ✅ (3)
    let crunchyCookie = try await drawFace(cookie: cookie) // ✅ (4)

    return crunchyCookie
}

3. 성능 저하

일반적으로 함수가 호출되면 stack에 push되고, 실행이 끝나 함수가 return 되면 pop을 수행한다. 기존에 동시성 프로그래밍을 구현하기 위해서는 여러 개의 스레드를 사용해 처리해야 했다. 이처럼 여러 개의 스레드 작업을 번갈아가며 처리할 때 컨텍스트 스위칭(context switching)이 발생한다.
컨텍스트 스위칭이 많아지면 성능이 저하될 수 있으며, 블록된 스레드가 어떤 자원을 잠그고(lock) 있을 때 데드락을 발생시킬 수도 있다.

반면, Swift Concurrency에 도입된 코루틴에서는 비동기 함수의 실행을 stack과 heap에서 관리한다. stack에는 비동기 함수를 실행할 때 사용되지 않는 지역변수들을 저장한다. 추가로 heap에는 suspension point에서 실행하는데 필요한 함수 컨텍스트들을 저장한다. 이것을 continuation이라고 부르며, 이를 통해 일시정지된 함수의 상태를 추적해 어디서부터 재개할지 알 수 있다.

참조

profile
👩🏻‍💻 iOS Developer

0개의 댓글