DispatchQueue는 어떻게 사용할까?

DongJin Lee
nbt-tech
Published in
6 min readApr 25, 2020

--

안녕하세요. 오늘은 iOS Dispatch Queue를 어떤 상황에 사용해야 하는지 알아보려합니다. Dispatch Queue를 이용하면 많은 이점이 있다는 것은 알고 있지만, 정작 사용된 모습을 찾아보기는 힘들었습니다. 그래서 제가 경험한 Dispatch Queue 사용법을 소개해 드리려고 합니다.

그전에…

Grand Central Dispatch

이미 많은 자료들이 GCD에 대한 이야기를 하고 있습니다. 여기서는 간략하게 알아보고 가려합니다. GCD는 Apple에서 만든 멀티 코어환경에서의 Application을 개발할때 도움을 주는 라이브러리입니다. Task라는 작업을 대기열을 주어 순서대로 실행을 시키거나, 작업을 동시에 실행 시켜주는 등 많은 작업을 할 수 있게 도와줍니다.

GCD를 쓰면 Thread를 다루는데 개발자가 직접 관리할 일이 많이 줄어듭니다. GCD는 모든 Task를 FIFO(first-in, first-out)로 관리하며, 시스템에 의해 관리되는 pool이 queue들을 실행합니다. 그러니 개발자는 pool을 관리할 일이 없어지게 되는거죠.

그럼 생각을 해 봅시다. 왜 이런게 도입 되었을까? 단지 멀티 코어 환경이라서 나왔다는데 그럼 어떤 상황에 쓰여진다는 거지? 바로 Synchronously, Asychronoulsy 한 상황 때문에 쓰여진 것이지요.

예를 들어 동기적인 상황에서 앱이 어떤 Task을 실행 하고 있다고 합니다.

1. Task실행

2. 동기적으로 실행해야하기 때문에 다른 Task들은 실행되고있는 Task가 끝날 때 까지 기다려야합니다.(여기서 불필요한 시간 낭비가 있는거죠.)

그래서 비동기에서의 실행이 생겼습니다. 어떤 Task가 실행되어도 다른 Thread에서 이전에 실행되었던 Task가 끝날때 까지 기다리지 않고 독립적으로 새롭게 실행 할 수 있게되죠. 하지만 FIFO구조에서의 Queue는 내가 제출한 Task의 순서대로 실행되고 끝나는걸 보장 할 수 없습니다. 이미 있는 Task가 끝난 다음 Task가 시작되는게 아니라 Task가 시작이되면 다음걸 시작하게되는 거죠.

중요한점, 비동기는 그렇다고 Concurrency 의미는 아닙니다!! 비동기 Task를 Serial queue 제출(submit)하느냐 Concurrent queue에 제출하냐에 따라 달라지게 됩니다.

The Main queue

좋습니다. 이제 GCD가 뭔지 간략하게 알아 봤으니 좀더 깊게(?) 들어가보도록하죠. Main Thread 많이 들어보셨을겁니다. iOS에서 UI의 업데이트를 담당하고 있는 Thread입니다.

그래서 만약 Main Thread에서 UI업데이트 말고 다른 코드들을 실행 시킨다면 자칫 잘못해 UI Freezing 이 발생하는 것이지요. 저또한 같은 실수를 했었습니다.

코드1. uploadAction

유저의 이미지와 글을 서버에 업로드 하는 메소드입니다.

1. 업로드시 보여줘야하는 로딩 View를 더하고 있습니다.

2. 동시에 ViewModel에게 이미지를 첨부 파일로 업로드하는 API를 요청 하고 있습니다.

이때, Main Thread에서 락이 발생합니다. 어떤식으로 보여지게 되냐면, 분명 로딩 View가 보여줘야 할 것 같은데 한참 뒤에 뷰가 생성됩니다.

처음엔 몰랐지요 왜 그랫는지….

Dispatch queues

iOS에서 여러 thread를 가지고 작업을 하기위해선 DispatchQueue 를 써야합니다.(여러 방법들이 있겠지만 지금은 이것만 보도록 하겠습니다.)

iOS에서 queue를 만든다면, 자동으로 하나 혹은 그 이상의 thread에 제가 만든 queue를 배정해 줍니다. 만약에 사용 가능한 thread가 있다면 다시 재사용 할 것이고, 아니면 OS에 필요에 따라 생성하기도 합니다. 이렇게 사용자가 thread를 관리할 필요가 없으니 GCD를 사용하는 거겠죠?

생성 방법은 아주 쉽습니다.

위와 같이 쓰면 끝납니다. attributes에 아무 설정을 안해주면 기본적으로 serial 동작을 합니다. Serial속성은 다음 작업이 시작되기 전에 각 작업을 완료해야 하는 queue로 만들게 됩니다.

Quality of service

이제 손쉽게 queue를 만들었으니 각 queue의 우선순위를 정해보도록 하겠습니다.

.userInteractive
제일 높은 우선순위입니다. 유저에게 직접적으로 반응을 보여줘야 할때 사용하게되죠. UI업데이트나 애니메이션작업에 사용하게 됩니다.

.userInitiated
유저의 UI입력으로 시작되며, 거의 바로 결과를 보여줘야 할때 사용합니다. 하지만 작업이 비동기적으로 끝 맞칠 수 있습니다. 예를 들면 로컬에 저장되어있는 문서를 불러올때, 유저는 클릭 후 거의 바로 결과를 얻길 기대할때 말이죠.

.utility
자 이게 바로 우리가 사용하길 원했던 QoS인 것 같은데요. 네트워크나, 긴 실행 시간을 가졌을때 사용되는 우선순위입니다. 첨부파일을 업로드하고 결과를 받아 처리하는건 이것을 사용하면 될 것 같네요. OS는 적절한 응답성, 혹은 성능을 높여 에너지를 많이 쓰게 하는지를 스스로 정해서 사용합니다.

.background
직접적으로 보여지지 않아도 될때 사용하면 됩니다. OS는 속도보다는 에너지 효율에 초점을 맞춰 작업을 진행하게 될꺼구요.

.default, .unspecified
두가지 선택지가있지만 직접 사용하지는 않은 옵션입니다. default옵션은 userInitiated와 utility의 중간 정도라고 보시면 됩니다.

어떻게 사용할까?

위의 코드1을 고쳐보겠습니다.

단순히 DispatchQueue를 생성하고, 속성을 concurrent로 주고 qos를 주었습니다.
그랬더니 View는 바로 유저가 업로드 버튼을 누르자마자 생성되었고, 별도로 첨부파일을 업로드하는 로직이 실행되었습니다!

신기..

결론

어찌보면 당연히 알고 있어야할 기본적인 내용이지만, 이번 기회에 코드에 직접 적용해 개선해보니 언제 사용하고 어떻게 사용해야 하는 지를 알게 되었습니다. 생각보다 어려운 내용을 iOS에서는 손쉽게 사용할 수 있게 만들어 놓아 조금만 알면 이렇게 개선할 수 있다는게 신기하더라구요.

저처럼 DispatchQueue는 알고 있으나 어떨때 필요한건지 도대체 어떻게 그럼 사용하는건지 궁금하셨던 분들께 조금이나마 도움이 되었으면 합니다.

잘못된 내용이나 틀린게 있다면 꼭 알려주세요! 감사합니다.

참고자료
Concurrency by Tutorials -By Scott Grosch

--

--