[ swift ] Continuation 에 대하여

sonny·2024년 12월 25일
1

TIL

목록 보기
82/133

Continuation은 Swift에서 비동기 작업을 동기 코드처럼 작성할 수 있게 돕는 중요한 개념이라고 한다.

특히 Swift의 async/await 구문과 함께 사용될 때 유용하다고 하는데, Continuation은 기존의 콜백 기반 비동기 API를 async/await 패턴으로 변환할 때 주로 사용되는 것이라고 한다.

아래에 Continuation의 개념과 사용법과 예제까지 작성해봤다.


Continuation로 비동기 작업을 간결하게 다뤄보자

1. Continuation?

Continuation은 Swift에서 비동기 코드를 작성할 때 사용되는 메커니즘인데 실행 흐름을 특정 지점에서 "중단(pause)"하고 이후에 특정 조건이 만족되면 "재개(resume)"할 수 있다고 한다.

그래서 기존의 복잡한 콜백 체인을 간소화해줄 뿐더러 비동기 작업을 동기 코드처럼 읽고 작성할 수 있게 해주는데

Swift에서 Continuation은 withCheckedContinuation 또는 withCheckedThrowingContinuation을 통해 구현이 된다.

저 두 개는 비동기 함수 안에서 호출되고 콜백 기반 API를 async 함수로 변환할 때 유용하게 사용할 수 있다고 하니, 계속 알아보자.


2. Continuation의 종류

  1. withCheckedContinuation
    단순 Continuation으로 에러 처리가 필요 없는 경우 사용된다.

  2. withCheckedThrowingContinuation
    에러 처리를 포함한 Continuation으로 에러를 발생시킬 가능성이 있는 비동기 작업에 적합하다.


3. Continuation의 사용법

(a) 기본 구조

func fetchData() async -> String {
    await withCheckedContinuation { continuation in
        // 비동기 작업이 완료되면 continuation.resume 호출된다.
        someAsyncAPICall { result in
            continuation.resume(returning: result)
        }
    }
}

(b) 에러 처리 포함

func fetchData() async throws -> String {
    try await withCheckedThrowingContinuation { continuation in
        someAsyncAPICall { result, error in
            if let error = error {
                continuation.resume(throwing: error)
            } else if let result = result {
                continuation.resume(returning: result)
            }
        }
    }
}

4. Continuation의 예제

예시로 보는 URLSession과 Continuation

URLSession의 콜백 기반 API를 async/await로 변환하는 예제를 살펴보면,

func fetchData(from url: URL) async throws -> Data {
    try await withCheckedThrowingContinuation { continuation in
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            if let error = error {
                continuation.resume(throwing: error)
                return
            }

            if let data = data {
                continuation.resume(returning: data)
            } else {
                continuation.resume(throwing: URLError(.badServerResponse))
            }
        }
        task.resume()
    }
}

이 코드는 기존의 콜백 기반 URLSessionasync/await로 변환한 예시다. 이렇게 더 간결하고 읽기 쉬운 코드를 작성할 수 있다.


5. Continuation 사용 시 주의사항

  1. Continuation은 반드시 한 번만 호출해야 함
    resume을 두 번 이상 호출하면 런타임 에러가 발생할 수 있다고 한다.

  2. 메모리 관리에 주의
    비동기 작업이 강한 참조 순환을 유발할 수 있기에 [weak self]와 같은 캡처링 규칙을 따르는 것이 좋다고 한다.

  3. 테스트와 디버깅
    withCheckedContinuation은 런타임에서 Continuation이 적절히 호출되지 않았는지 확인해주는데 이게 디버깅 단계에서 유용하다고 한다.


6. Continuation의 장점

  • 코드 가독성 향상: 콜백 지옥을 피하고, 동기 코드처럼 비동기 작업을 작성할 수 음.

  • async/await과 완벽한 조화: 기존 API와 새로운 비동기 패턴을 쉽게 연결할 수 있음.

  • 에러 관리 간소화: try와 함께 사용해 직관적인 에러 처리가 가능함.


7. 결론적으로,

Continuation를 활용하면 복잡한 비동기 작업도 간단하고 유지보수하기 쉬운 코드로 작성할 수 있게 되는걸 예시를 통해 확인했는데,

특히 기존의 콜백 기반 API를 현대적인 async/await 방식으로 변환하는데 탁월한 방법이라고 한다.


음...

기존의 콜백 기반 API를 Continuation으로 변환하는 과정이 생각보다 까다롭다고 느껴졌다. 특히 어디에서 resume을 호출해야 하는지 파악하지 않으면 코드 흐름이 꼬이기 쉽겠다고 생각이 들었고 ..

withCheckedThrowingContinuation을 사용해 에러를 처리하는 방식은 기존의 방식과 조금 다르게 느껴졌지만, 익숙해지니 훨씬 명확하고 간결했다.

"기존 방식"이란 비동기 작업을 처리할 때 일반적으로 사용되던 콜백 기반의 에러 처리 방식이다

.
.

내가 나중에 보려고 하는 차이점 표

기존 방식 (콜백)withCheckedThrowingContinuation 사용
에러와 결과를 콜백 클로저에서 처리해야함trydo-catch를 사용해 에러 처리 가능함
클로저 중첩으로 가독성이 떨어질 수 있음비동기 작업이 동기 함수처럼 읽히므로 가독성 향상됨
상태 관리와 흐름 제어가 복잡할 수 있음async/await로 순차적인 흐름 제어 가능함
Result 타입으로 에러와 데이터를 전달Continuation을 통해 에러와 결과를 나누어 처리함

withCheckedThrowingContinuation을 사용하면 에러 처리가 throws를 통해 통합적으로 이루어지니 기존의 복잡한 콜백 구조에 비해 간단해보이기는 했다. 그런데 이것도 익숙해지기 전에는 Continuation의 resume 호출 방식을 생소하다고 생각하긴 했지만..

그래도 이렇게 기존 방식과 Continuation의 장단점을 비교해보니 Continuation이 가져다주는 코드 개선의 가치가 좀 더 높다는 건 분명히 보였다.

profile
iOS 좋아. swift 좋아.

0개의 댓글

관련 채용 정보