Apple/WWDC 2021

[WWDC 2021] Meet AsyncSequence

Dev_Pingu 2023. 3. 7. 01:42
반응형

안녕하세요 Pingu입니다.🐧

 

요즘 async/await를 다시금 공부 중이라 오늘은 WWDC 2021에 발표된 Meet AsyncSequence라는 영상을 보고 정리한 글을 써보려고 합니다.

 

AsyncSequence를 간단하게 요약하면 기존에 있던 Sequence에 비동기 기능을 추가한 녀석인 것 같습니다.

Meet AsyncSequence

영상은 일단 간단하게 AsyncSequence의 새로운 기능을 설명하기 위한 예제 코드로 시작됩니다.

위 코드를 보면 endPointURL을 통해 csv파일을 다운로드하는 작업인 것을 알 수 있습니다.

 

오래 걸리는 작업이라 모두 다운로드된 후 파일을 처리하기보다는 다운로드되는 대로 처리하고 싶을 때 async/await 와 AsyncSequence를 사용하면 된다고 합니다.

 

csv파일이니 각각의 라인이 하나의 데이터이니 위 코드의 반복문에서 라인단위로 처리를 해주는 것을 볼 수 있어요. 그리고 이때 사용한 for문은 기존 for-in이 아닌 for-try-await-in 구문인 것을 볼 수 있습니다.

 

아마 기존에 위 작업을 처리하려면 csv파일이 모두 다운된 뒤 작업을 처리해야 했을 뿐 아니라 completion handler와 같은 콜백함수로 작업을 처리했을 건데 async 함수를 사용하면 위와 같이 직관적이고 효율적으로 코드를 작성할 수 있다는 것을 볼 수 있습니다.

 

게다가 dropFirst()와 같이 기존 Sequence 타입에 사용하던 api를 그대로 사용하는 것도 볼 수 있습니다.

 

아까 코드에서 async function에 대해  알아낸 걸 요약해 보면 아래와 같습니다.

  • Async Function
    • await 키워드를 사용해서 콜백 없이 concurrent 코드를 작성할 수 있다.
    • Async Function을 호출하면 값이나 에러가 발생할 때 suspend 되고 다시 resume 된다.

여기서 AsyncSequence와의 차이점은 AsyncSequence는 각각의 Element에서 suspend 되고 iterator가 값을 생성하거나 에러를 던질 때 다시 resume 된다는 것입니다.

 

AsyncSequence

그럼 AsyncSequence를 좀 더 자세히 알아볼게요

 

  • AsyncSequence
    • 이름에서 알 수 있듯 기존 Sequence와 동일한 부분이 있지만 차이점이 있다.
    • 차이점은 각 Element가 비동기로 전달된다.
    • AsyncSequence는 에러를 던질 수 있다.
    • 0개 이상의 값이 될 수 있고 Sequence와 마찬가지로 iterator에서 nil을 반환하면 완료된다.
    • 에러가 발생하면 iterator의 이후 next 호출에서 nil이 반환되어 완료된다.

그럼 기존 iteration은 어떻게 동작했는지 먼저 살펴보겠습니다.

아마 정말 많이 본 흔한 for-in 코드입니다.

실제 컴파일 단계에서는 위 코드가..

아래 코드처럼 변환되어 실행된다고 합니다.

iterator를 만들어서 nil이 나올 때까지 while 루프를 돌리는 거죠.

 

그런데 이제 여기에 async/await 기능을 사용하고 싶다! 라고 하신다면 아래와 같이 간단하게 사용할 수 있습니다.

그리고 이걸 다시 for 루프로 바꾸면

이렇게 되겠네요. 만약 에러를 던지는 AsyncSequence라면 for-try-await-in 구문을 사용하면 됩니다.

물론 기존 for-in 구문과 마찬가지로 break, continue도 사용할 수 있습니다.

 

코드를 위와 같이 작성하면 에러를 처리할 수도 있게 됩니다. 함수가 throw 함수일 때와 마찬가지로 throw AsyncSequence라면 try를 쓰지 않았을 때 컴파일러가 친절하게 알려줍니다.

 

위 코드를 보면 반복문이 두 개인데요, 첫 번째 반복문이 완전히 실행된 뒤에 두 번째 반복문이 실행됩니다. 즉 순차적으로 실행된다는 건데.. 이렇게 말고 각각의 반복문을 동시에 실행시키고 싶을 때가 있죠.

 

그럴 땐 반복문을 캡슐화해서 아래와 같이 코드를 작성하면 된다고 합니다.

이렇게 해서 위 코드에서와 같이 cancel()을 호출하면 명시적으로 취소할 수도 있습니다. 

 

근데 위 코드는 2021년에 나와서 async인 거 같은데 현재는 아래와 같이 Task로 사용해줘야 합니다.

let iteration1 = Task {
	for await quake in quakes {
    	//...
    }
}

이렇게 하면 AsyncSequence가 무기한으로 실행될 수 있는 걸 알고 있을 때 유용하게 사용할 수 있다고 합니다.

 

AsyncSqeucnce API in iOS 15

iOS 15에서 AsyncSequence가 적용된 API를 소개하네요.

파일을 읽는 것은 비동기 작업을 적용하기 좋은 작업입니다.

이제 FileHandle에는 해당 FileHandle의 비동기 바이트 시퀀스에 대한 접근을 제공하는 새로운 프로퍼티인 bytes가 있다고 합니다.

새로운 bytes 프로퍼티를 위와 같이 비동기 바이트 시퀀스를 라인단위로 변환하는 새로운 api인 lines와 함께 사용하면 라인단위로 파일을 처리할 수 있다고 하네요.

 

로컬 파일에만 적용할 수 있는 것이 아닌 네트워크에서 콘텐츠를 라인 단위로 처리하고 싶을 때도 사용할 수 있다고 합니다.

 

또 다른 예시로 네트워크로 정보를 가져올 때 응답 및 인증에 대한 처리 작업을 보여줍니다.

위와 같이 URLSession에는 URL, URLRequest가 주어졌을 때 비동기 바이트 시퀀스를 가져오는 bytes 메서드가 있다고 합니다. 위와 같이 이를 활용하여 응답 및 인증을 잘 처리할 수 있다고 간단히 알려줍니다.

 

URLSession의 새로운 기능을 자세히 알고 싶으면 "Use async/await with URLSession" 이라는 영상을 보라고 하네요.

 

Notification에도 AsyncSequence를 적용할 수 있다고 합니다.

위와 같이 새로운 API가 생겼고 이를 활용하면 된다고 하네요. 위 코드를 보면 기존 Sequence 타입에 사용하던 first(where:)와 같은 기능들도 사용할 수 있어 적용하기도 쉽고 적용 시 코드가 간결하고 읽기 쉬워질 거라고 합니다.

이렇게 많은 api를 만들어뒀으니 잘 사용해 달라고 하네요.

 

Build your own

그래서 새로운 AsyncSequence가 뭔지도 알겠고 뭐가 새로 나왔는지도 알겠는데, 그래서 기존 코드에 어떻게 쓰라고?라는 생각이 들 때쯤 그 방법을 알려줍니다.😄

기존 코드는 아마도 위와 같이 콜백이나 델리게이트 패턴을 활용해서 비동기 작업을 처리하고 있었을 거예요.

기존 코드에 AsyncSequence를 어떻게 적용하는지 보여주기 위해 위와 같은 코드를 예시로 보여줍니다.

일반적인 핸들러 패턴을 사용한 코드인걸 알 수 있어요. 동일한 인터페이스를 새로운 AsyncStream 타입을 사용해 작성해 보겠습니다.

AsyncStream을 사용해서 기존 코드를 위와 같이 쉽게 바꿀 수 있다고 합니다. 

continuation이라는 애가 나오는데 얘는 값을 두 번 이상 yeield 하거나 finish 혹은 termination을 처리하는 애입니다.

즉 위와 같이 코드를 작성하면 하나의 콘텍스트에서 monitor를 생성 및 quake를 yield 하고 작업의 termination을 처리하고, 작업을 시작할 수 있게 됩니다.

그리고 위와 같이 직접 만든 AsyncStream을 사용하면 됩니다.

모든 처리에 대한 코드가 하나의 콘텍스트에 존재하므로 코드가 이해하기 쉬워진 걸 알 수 있어요.

이렇게 AsyncStream은 여러분이 직접 Async Sequence를 만들기 쉽게 해 줍니다.

위와 같이 safety, iteration, cancellation와 같이 Async Sequence에서 기대하는 모든 것을 처리할 수 있고 buffering도 처리할 수 있기 때문에 AsyncStream은 기존 코드를 Async Sequence로 만들 때 사용하기 좋습니다.

 

만약 에러를 처리해야 할 땐 AsyncThrowingStream을 사용하면 됩니다. AsyncThrowingStream은 다른 건 다 AsyncStream과 동일하지만 iteration에서 에러를 던져서 실패를 처리할 수 있다는 차이점만 있습니다.

 

이렇게 영상은 끝이 나게 됩니다.

 

영상을 다 정리하고 나니 처음 요약한 대로 Sequence 타입에 비동기 기능만 추가한 것이 AsyncSequence였던 거 같습니다.

기존 비동기 코드를 이걸 활용해 고쳐나가면 코드를 훨씬 깔끔하고 직관적으로 작성할 수 있을 거 같아요.

 

이번 영상을 정리하면서 비동기 처리 하는 예시들을 많이 볼 수 있어 바로 적용해 볼 수 있을 거 같네요.

 

도움이 되셨길 바라며, 감사합니다.

 

반응형