들어가며
Swift는 계속 성장 중인 오픈 소스 패키지 카탈로그입니다.
본론으로 들어가기 전에 AsyncSequence 프로토콜을 한 번 상기해 봅시다.
AsyncSequence Recap
AsyncSequence 프로토콜은 비동기 생성값을 기술할 수 있게 해줌
반복자 다음 함수(next function form its iterator)는 Swift 동시성을 사용해 값을 전달하기 때문에 비동기 함수입니다.
•
Swift의 throw를 통해 잠재적인 장애를 해결합니다.
•
Sequence에서처럼 for-await-in 구문으로 이걸 반복할 수 있습니다.
for-await-in syntax
AsyncSeqence를 도입하면서 Sequence에서 찾아볼 수 있는 모든 툴을 비동기 버전으로 추가했습니다.
고급 알고리즘을 통합하고 강력한 걸 제공하고자 Clock와 상호 운용되죠. Swift 동시성을 늘리는 AsyncSequence 알고리즘의 오픈 소스 패키지입니다.
작년 Swift Algorithm 패키지를 소개했고 그 사용 사례를 증명하고자 메시징 앱을 만들었습니다.
Multi-input algorithms
먼저, 우리에게는 다중 입력 AsyncSequence로 작동하는 특정 알고리즘들이 있습니다. 이 알고리즘들은 AsyncSequence를 다양한 방식으로 결합하는데요.
이 알고리즘은 각 베이스에서 결과 튜플을 생성하기 위해 다중 입력을 취하고 그것을 반복합니다.
•
입력물 각각은 Zip이 구조를 이루고 있는 베이스입니다.
•
비동기 Zip 알고리즘은 표준 라이브러리에서 Zip과 유사하게 작동하나, 동시에 베이스 각각을 반복하고 반복 시 오류가 발생하면 다시 throw 합니다.
반복과 오류 rethrow를 동시에 하는 것은 더 복잡해질 수 있습니다.
하지만 Swift Async Algorithm은 메시징 앱에서 그걸 모두 다룰 수 있었죠.
[Zip 영상]
효율적인 저장과 전송을 위해 영상을 다양한 크기로 트랜스코딩하면서 비동기 조정하는 코드가 이전에도 있었는데요.
•
Zip 자체가 어떤 쪽에서 먼저 값을 생성하는지 더 선호하는 것이 없기 때문에 영상이나 미리보기 둘 중 아무거나 먼저 생성되고 어느 쪽이든 간에 다른 쪽이 튜플을 완료할 때까지 기다릴 겁니다.
•
Zip은 튜플을 구축하기 위해 서로를 기다릴 수 있기 때문에 함께 업로드될 수 있도록 짝을 기다릴 수 있습니다.
우리는 입력 메시지를 AsyncSequence로 모델링하는 것이 꽤 타당할 것이라는 결론을 내렸습니다.
AsyncSequence는 명령을 보존하여 콜백을 메시지 비동기 시퀀스로 바꿀 수 있기 때문에 메시지 처리에 비동기 스트림을 사용하기로 결정했습니다.
각 계정에서 입력 메시지의 AsyncSequence를 만들었으나 막상 구현하려고 하니 하나의 AsyncSequence로 처리해야 했거든요.
Merge 알고리즘이 갖춰져 있는데요.
•
이 알고리즘은 다중 AsyncSequence를 동시에 반복한다는 점에서 Zip과 비슷하게 작동합니다.
•
하지만 다른 점이 있다면,
◦
짝을 이루는 튜플 대신 베이스가 동일 요소 타입을 공유해야 합니다.
◦
베이스 AsyncSequence를 해당 요소의 특이 AsyncSequence 하나에 병합합니다.
[Merge 영상]
병합 알고리즘은 반복 시 한 쪽 계정에서 먼저 생성된 요소를 취합니다. 모든 베이스 AsyncSequence가 반복자에서 아무것도 도출하지 않을 때까지 반복은 계속 됩니다. 베이스에서 오류가 발생한다면 다른 반복은 취소됩니다. 이를 통해 메시지를 AsyncSequence를 취하고 병합할 수 있죠. 이 Merge 알고리즘은 값 생성 시 동시에 작동하지만 간혹 시간과 상호 작용하는 게 유용할 때가 있습니다.
Clock, instant, duration
이번 패키지는 특정 알고리즘을 도입해 Swift 내 새로운 Clock API를 이용함으로써 시간과 작동합니다. 시간 그 자체는 굉장히 복잡한 주제로 Swift 5.7의 clock, instant, duration 타입은 시간을 안전하고 일관되게 하는 일련의 API입니다.
Clock 프로토콜은 2개의 프리미티브를 정의합니다.
Clock에는 여러 가지 구조가 들어가 있는데 흔히 쓰이는 구조 중 두 가지는 바로 continuous clock과 Suspending Clock입니다.
continuous clock
스톱워치처럼 시간 측정 시 사용 가능하며 사물 상태 측정 여부와는 상관없이 시간은 흘러 갑니다.
suspending clock
머신이 정지하면 멈추죠.
우리는 데드라인 이후 경고를 무시할 수 있도록 앱에서 새로운 시계 API를 사용하였는데요.
지연하고자 하는 초를 구체적으로 가리키는 duration 값을 추가해 데드라인을 만들 수 있었습니다.
작동 실행 경과 시간을 측정하는 방법도 있구요.
[측정 결과 영상]
이 두 시계의 주요 차이점은 머신이 정지했을 때의 반응에서 나타나는데요.
보통 이 차이는 주요 특징이 실행 시간을 중단함으로써 애니메이션과 같은 것들을 예상한대로 작동하게 하는 주요 세부 사항이 될 수 있습니다.
Algorithms using time
신형 패키지에서는 시간과 관련하여 이벤트가 전개되는 개념들을 처리하기 위해 이 타입들을 사용합니다.
우리가 가장 시간을 많이 활용하는 분야는 메시지 검색 입니다.
우리는 결과 채널을 관리하는 컨트롤러를 만들었죠. 이 채널은 검색 테스크에서 UI로 검색 결과를 모읍니다. 검색 테스크 자체에는 시간과 관련된 구체적인 특성이 필요했죠.
Debounce 알고리즘은 반복 시 다음 값을 내기 전 정지 기간을 대기합니다.
검색 필드의 유저 입력이 빠르게 바뀔 때 우리는 검색 컨트롤러가 각 변화의 검색 요청을 급하게 하길 원치 않습니다. 대신에 특정 타이핑이 완료되려고 할 때까지의 정지 기간 동안 대기하길 원하는 거죠.
Debounce 알고리즘은 기본적으로 Continuous Clock을 사용할 겁니다.
Swift 알고리즘 패키지에는 값을 나누는 일련의 알고리즘이 있는데요. Swift Async Algorithm 패키지에서도 이들을 제공합니다. 다양한 Clock 및 duration과 상호 운용하는 버전들도 추가되었음은 물론이고요.
특정 Chunk 알고리즘들을 통해 카운트, 시간, 콘텐츠에 따라 통제가 가능합니다.
*만약 여기서 오류가 발생하면 다시 throw하기 때문에 오류와 관해서는 안전한 코드입니다.
[Chunk 영상]
우리는 메시지 청크가 특정 경과 시간마다 직렬화되어 전송될 수 있도록 chunked(by:) API를 사용했습니다.
이 방식으로 클라이언트가 보낸 효율적인 패키지를 받았죠. 대량 메시지를 50밀리초마다 입력할 때도 이 API를 사용할 수 있었습니다.
Collections
컬렉션이나 시퀀스로 작업할 때는 요소를 게으르게(lazy) 처리하는 게 유용하며 잘 작동합니다.
AsyncSequence는 Swift 표준 라이브러리에서 이런 작동 방식과 유사하게 작동하죠. 하지만 lazy 알고리즘처럼 종종 컬렉션 세계로 돌아가야 할 때가 있습니다.
신규 패키지는 AsyncSequence로 컬렉션을 구축하기 위한 일련의 이니셜라이저를 제공합니다. 이들을 통해 유한하다고 알려진 AsyncSequence를 입력하여 Dictionary, Set, Array를 구축할 수 있습니다.
Swift 동기성 사용을 위해 최신 정보를 사용할 수 있는 특색이 다양했기 때문에 굉장히 도움이 되었습니다.
지금까지 Swift Async Algorithm 패키지의 하이라이트 중 극소수만 보여드렸는데요.
오늘 본 것보다 훨씬 더 많은 알고리즘이 있습니다.
Swift Async Algorithms 패키지는 시간에 따라 처리할 수 있도록 알고리즘 집합을 사용하고 이를 앱에 도움이 될 수 있는 광범위한 고급 기능으로 확대합니다.