일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 직장영어
- non conventional NFR
- re-engineering
- Android
- 쓰레드 이미지 다운로드
- 라이브아카데미
- conventional NFR
- 파이썬
- jenkins
- RecyclerView
- 자료구조
- git
- Python
- 메타 쓰레드
- Firebase
- skeleton architecture
- 객치지향프로그래밍
- cloud firestore
- meta threads
- 쓰레드 비디오 다운로드
- django
- 이모티콘
- 젠킨스
- Realtime Database
- 특수문자
- 안드로이드
- endless scrolling
- 특수기호
- firestore
- 영어회화
Archives
- Today
- Total
Owl Life
Coroutines & Flows - 다섯가지 안티 패턴 본문
반응형
1. UI 업데이트를 메인 스레드에서 하면 안 되는 이유
- 여러분 앱의 화면(UI)은 마치 상점의 간판처럼 항상 손님(사용자)에게 최신 정보를 보여줘야 합니다.
- 그런데 이 간판을 업데이트하는 작업이 너무 복잡하고 오래 걸리면(CPU를 많이 사용하면), 간판이 잠시 멈춰버리겠죠?
- 이처럼 뷰 모델에서 UI 상태를 업데이트하는 복잡한 로직을 실행할 때, 기본적으로는 메인 스레드라는 중요한 길에서 작업하게 됩니다.
- 이 길이 막히면 앱 화면 전체가 멈추는 것처럼 느껴져서 사용자가 불편함을 느낍니다.
- 이 문제를 해결하려면 복잡한 UI 업데이트 작업은 Default Dispatcher라는 다른 작업자에게 맡겨야 합니다.
- 이렇게 하면 메인 스레드가 막히지 않고, UI는 계속 부드럽게 움직일 수 있습니다.
2. 화면 이동 시 데이터 저장이 날아가 버리는 문제
- 앱에서 화면을 이동할 때(예: 노트 작성 화면에서 목록 화면으로 돌아갈 때), 현재 화면과 연결된 View Model Scope에서 실행 중인 코루틴은 자동으로 취소됩니다.
- 만약 이 코루틴안에서 데이터를 저장하거나 서버와 동기화하는 중요한 작업을 하고 있었다면, 화면을 이동하는 순간 그 작업이 중간에 멈춰버릴 수 있습니다.
- 예를 들어, 작성 중인 노트를 저장 버튼을 눌러 저장하고 바로 이전 화면으로 돌아갔는데, View Model Scope가 취소되면서 서버에 저장되지 않는 문제가 발생할 수 있습니다.
- 이 문제를 막으려면 두 가지 방법이 있습니다.
- 첫째, 데이터 저장이 완료될 때까지 UI에게 기다리라고 알려주고, 저장이 끝난 후에 화면 이동을 시킵니다.
- 둘째, View Model Scope 대신 앱이 살아있는 동안 계속 유지되는 Application Scope를 사용해서 중요한 백그라운드 작업을 실행합니다.
3. 병렬로 할 수 있는 일을 순서대로 시키는 문제
안티 패턴 | 문제점 | 올바른 해결 방법 | 예시 |
병렬 작업 순차 실행 | 여러 코루틴 작업을 병렬로 실행하여 시간을 단축할 수 있음에도 불구하고, 순서대로 실행하게 만듦. | 모든 코루틴 작업을 먼저 시작(launch)시킨 후, 마지막에 모든 코루틴이 완료될 때까지 한꺼번에 기다림(join). | 프로필 사진 업로드와 프로필 정보 업데이트를 동시에 진행. |
`launch` 사용 후 바로 `join` 호출 | 코루틴을 시작하고 바로 완료될 때까지 기다리므로, 다음 작업이 시작되기 전에 현재 작업이 끝나야 함. | 모든 `launch` 호출을 먼저 완료한 후, 마지막에 모든 `join` 호출을 수행. | `launch { 작업1() }; launch { 작업2() }; 작업1.join(); 작업2.join()` (잘못된 방식) vs `launch { 작업1() }; launch { 작업2() }; joinAll()` (올바른 방식) |
`async` 사용 후 바로 `await` 호출 | 비동기 작업을 시작하고 바로 결과를 기다리므로, 비동기 작업의 이점을 살리지 못하고 순차적으로 실행됨. | 모든 `async` 호출을 먼저 완료한 후, 마지막에 모든 `await` 호출을 수행. | `val 결과1 = async { 작업1() }.await(); val 결과2 = async { 작업2() }.await()` (잘못된 방식) vs `val 작업1 = async { 작업1() }; val 작업2 = async { 작업2() }; val 결과1 = 작업1.await(); val 결과2 = 작업2.await()` (올바른 방식) |
- 여러 코루틴작업을 동시에(병렬로) 실행하면 전체 작업 시간을 크게 줄일 수 있습니다. 예를 들어, 프로필 사진 업로드와 프로필 정보 업데이트는 서로 기다릴 필요 없이 동시에 진행될 수 있습니다.
- 하지만 launch를 사용해서 코루틴을 시작하자마자 바로 join을 호출하거나, async를 사용하고 바로 await를 호출하면, 마치 한 사람씩 줄 서서 일을 처리하는 것처럼 순서대로 실행되게 만들어 버립니다.
- launch는 코루틴을 시작하라는 명령이고, join은 그 코루틴이 끝날 때까지 기다리라는 명령입니다.
- launch 다음에 바로 join을 쓰면, 첫 번째 작업이 끝날 때까지 기다렸다가 두 번째 작업을 시작하게 됩니다.
- 올바르게 병렬 실행을 하려면, 모든 코루틴작업을 먼저 다 시작( launch)시켜 놓습니다.
- 그리고 마지막에 모든 코루틴이 완료될 때까지 한꺼번에 기다리도록( join) 하면 됩니다. 이렇게 해야 비로소 여러 작업이 동시에 착착 진행됩니다.
4. while true 반복문에서 취소 요청이 무시되는 문제
- 데이터를 주기적으로 가져오는 작업처럼 while true 무한 반복문 안에서 코루틴을 실행할 때 주의해야 할 점이 있습니다.
- 만약 이 반복문 안에 try-catch 블록을 사용하여 일반적인 예외를 잡도록 코드를 작성하면 문제가 생길 수 있습니다.
- 코루틴이 취소될 때 발생하는 취소 예외(Cancellation Exception)는 try-catch 블록에 의해 일반 예외처럼 잡혀버릴 수 있습니다.
- 이렇게 되면 코루틴은 사실상 취소된 상태인데, 반복문은 취소 사실을 인지하지 못하고 계속 돌게 됩니다. 마치 "취소됐는데 취소 안 된 척"하며 무한 루프에 빠지는 거죠.
- 이 문제를 해결하려면 try-catch 블록 안에서 예외를 잡은 후, coroutineContext.ensureActive()를 호출하여 현재 코루틴이 활성 상태인지 다시 확인해야 합니다.
- 이 함수는 만약 코루틴이 취소된 상태라면 취소 예외를 다시 발생시켜서 반복문이 제대로 종료되도록 돕습니다.
5. Supervisor Job을 잘못 사용하는 문제
안티 패턴 | 문제점 | 올바른 사용법 | 설명 |
Supervisor Job을 launch 함수에 직접 전달 | 자식 코루틴들이 Supervisor Job의 독립적인 실패 처리 혜택을 받지 못하고 부모의 실패에 영향을 받음 | `supervisorScope` 블록 안에서 코루틴 실행 | `supervisorScope` 안에서 시작된 코루틴은 서로 독립적으로 실패 처리됨 |
- supervisor job은 특정 코루틴이 실패하더라도 다른 자식 코루틴들이 영향받지 않고 계속 실행될 수 있도록 해주는 특별한 Job입니다.
- 하지만 launch 함수에 Supervisor Job을 직접 인자로 전달하는 것은 올바른 사용법이 아닙니다.
- 코루틴의 Job은 자식 코루틴에게 상속되지 않는 유일한 컨텍스트 요소입니다.
- 그래서 launch에 Supervisor Job을 직접 넘겨주면, 코틀린 내부적으로 이상한 계층 구조가 만들어져서, 자식 코루틴들이 supervisor job의 혜택(실패 시 독립적인 실행)을 제대로 받지 못합니다. 즉, 따로 놀아야 할 자식들이 부모 때문에 같이 실패하게 되는 거죠.
- supervisor job의 기능을 제대로 사용하려면 supervisorScope라는 특별한 함수 블록 안에서 코루틴을 실행해야 합니다.
- supervisorScope 안에서 launch로 시작된 코루틴들은 서로 독립적으로 실패할 수 있게 됩니다. 마치 각자 알아서 책임지고 실패하는 자식들처럼 말이죠.
출처 : https://youtu.be/JyBq76N4Zc4?si=OEGCPCf9D0_37NTY
반응형
'Android Dev > dev' 카테고리의 다른 글
안드로이드 Dalvik과 ART, JIT과 AOT 차이점 (0) | 2019.10.09 |
---|---|
AndroidQ 다크테마 적용방법 (0) | 2019.09.27 |
[개발자 폰트] 나눔고딕코딩 에서 D2 Coding 글꼴로 변경하다. (0) | 2017.12.03 |
Comments