-
Notifications
You must be signed in to change notification settings - Fork 0
Concurrency 방식으로의 전환 #6
Description
Continuation
- 동기/콜백 기반 코드를
async/await로 연결해 주는 어댑터-
CheckedContinuation (런타임에 오류 체크하는 객체)
withCheckedContinuationwithCheckedThrowingContinuation
-
UnsafeContinuation (런타임에 오류 체크하지 않는 객체, 퍼포먼스 좋음)
withUnsafeContinuationwithUnsafeThrowingContinuation
-
continuation.resume
중단된 async 작업을 재개시키는 트리거 (1회 호출)
-
resume()-> Void 반환 -
resume(returning: value)-> 성공 값 전달 -
resume(throwing: error)-> 에러 전달 (Error 타입) -
resume(with: result)->Result<Success, Error>그대로 전달
콜백 기반 API → async/await (단발성 결과)
-
기존 completion을 그대로 감싼 async 함수로 전환
-
성공만 있는 경우 →
CheckedContinuation<Success?, Never>
// 기존: completion 기반
func fetchUserWithCompletion(completion: @escaping (User) -> Void) {
...
completion(user)
}
// 전환: async (성공만, 에러 미포함)
func fetchUser() async -> User {
await withCheckedContinuation {
(continuation: CheckedContinuation<User, Never>) in
fetchUserWithCompletion { user in
continuation.resume(returning: user)
}
}
}에러 있는 콜백 → async/await (throws)
-
Result 에러 콜백을
async throws로 전환 -
성공/실패가 있는 경우
-
withCheckedThrowingContinuation로 전환 -
resume(throwing:)의 파라미터는 Error 타입이어야 함 -
이미 Result로 받는다면
resume(with: result)로 바로 전달
-
// 기존: Result 기반 콜백
func fetchUserWithCompletion(completion: @escaping (Result<User, Error>) -> Void) {
...
if let user {
completion(.success(user))
} else {
completion(.failure(Error.failed))
}
}
// 전환: async throws
func fetchUser() async throws -> User {
try await withCheckedThrowingContinuation {
(continuation: CheckedContinuation<User, Error>) in
fetchUserWithCompletion { result in
// switch로 success/throwing 분기
switch result {
case .success(let user):
continuation.resume(returning: user) // 성공 값 전달
case .failure(let error):
continuation.resume(throwing: error) // 에러 전달
}
// (또는) 성공/실패를 그대로 전달
continuation.resume(with: result)
}
}
}UnsafeContinuation
성능 최적화가 절대적으로 필요할 때만 사용
체크가 없으므로 resume 정확히 한 번 호출을 스스로 보장 필요
func fetchUser() async throws -> User {
try await withUnsafeThrowingContinuation {
(continuation: UnsafeContinuation<User, Error>) in
fetchUserWithCompletion { result in
// 체크 없음: 중복/누락 가능
continuation.resume(with: result)
}
}
}델리게이트 기반 API → async/await (단발성 이벤트)
기존 (델리게이트 방식)
-
델리게이트 메서드가 불리는 시점에 콜백 내부에서 값이 전달됨
-
값을 저장해두고 필요할 때 조회
- 이 경우 언제 값이 세팅될지 확실하지 않으므로, 흐름 제어가 분산
var currentLocation: CLLocation?
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
guard let location = locations.last else { return }
// 즉시 delegate에서 처리
self.currentLocation = location
}
// 다른 곳에서 나중에 접근 (저장된 위치 사용)
if let location = currentLocation {
print("\(location)")
}async/await + Continuation
-
await로 값을 기다릴 수 있는 비동기 함수로 전환-
Continuation 생성 메서드로 결과값 활용
-
비동기적으로 생기는 값을
await기다렸다가 받아서 활용
-
-
델리게이트에서
resume으로 재개 -> 흐름 제어가 동기 코드처럼 작성
var currentContinuation: CheckedContinuation<CLLocation, Error>?
// await로 위치값을 기다릴 수 있는 비동기 함수로 전환
func getCurrentLocation() async throws -> CLLocation {
try await withCheckedThrowingContinuation { continuation in
// (1) continuation 외부 저장
currentContinuation = continuation
locationManager.requestLocation()
}
}
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
guard let location = locations.last else { return }
// (2) 값 반환 시점에 재개
currentContinuation?.resume(returning: location)
// (3) 재개 후 해제
currentContinuation = nil
}