코틀린 코루틴 리액티브 프로그래밍: Flow와 Channel
- 지은이조세영
- ISBN : 9791161759524
- 36,000원
- 2026년 05월 19일 펴냄
- 페이퍼백 | 488쪽 | 188*235mm
- 시리즈 : 모바일 프로그래밍
책 소개
요약
이 책은 전작 『코틀린 코루틴의 정석』에 이어, 코루틴의 Flow API를 활용한 리액티브 프로그래밍과 Channel을 활용한 코루틴 간 통신을 기초부터 심화까지 체계적으로 다룬다.
풍부한 시각화 자료와 실행 가능한 예제 코드를 통해 동작 원리를 직관적으로 이해할 수 있도록 구성했으며, 안드로이드, 스프링 등 코틀린을 사용하는 개발자 분들 중 Flow와 Channel을 기초부터 심화까지 제대로 배워보고 싶은 독자들에게 추천한다.
추천의 글
이 책은 전작(『코틀린 코루틴의 정석』(에이콘, 2024))에서 다진 코루틴의 기초 위에 Flow와 Channel이라는 심화 개념을 자연스럽게 확장해 주는 안내서입니다. 심화 내용을 다루면서도 쉬운 설명 스타일을 그대로 유지하고 있으며, 특히 그림과 도식을 활용한 직관적인 설명이 추상적인 개념을 명확하게 이해하는 데 큰 도움을 줍니다.
비동기 데이터 스트림 처리에 대한 이해를 넓히고 싶은 개발자, 전작을 읽고 다음 단계로 나아가고자 하는 독자, 실무에서 Flow와 Channel을 활용하려는 개발자 모두에게 강력히 추천합니다.
—강대규, 당근마켓 안드로이드 개발자
내용이 너무 술술 읽혀서 놀랐습니다. ‘분명 어려운 내용이었는데 왜 이렇게 쉽지?’ 싶었거든요. 저도 예전에 동료들의 코드를 어깨너머로 보며 Flow를 익혔던 터라, 그때 어설프게 이해하고 넘어갔던 개념들을 이번에 제대로 정리할 수 있어 무척 반가웠습니다. 특히 동작 원리부터 연산자, 예외 처리, 뜨거운 Flow와 테스트까지 단계적으로 이어지는 구성이 인상적입니다. 저자의 가이드를 따라가다 보면 개별 개념에만 머무르지 않고, 서로 어떻게 연결되는지도 자연스럽게 이해하게 됩니다. 쉽게 읽히면서도 깊이를 놓치지 않는 점이 특히 좋았습니다. Flow 학습 시간을 줄이고 싶은 분들에게 강력히 추천하고 싶습니다.
—이대건, 하이퍼커넥트 백엔드 개발자
전작인 『코틀린 코루틴의 정석』이 비동기 작업 하나하나를 ‘어떻게 만들고 멈추고 제어할 것인가’라는 기초를 탄탄히 다져줬다면, 이번 책은 그 작업들이 연속적으로 이어지는 ‘데이터의 흐름(data stream)’을 어떻게 설계할 것인지에 집중합니다.
단순히 기능을 나열하는 방식에서 벗어나 리액티브 프로그래밍의 등장 배경부터 코루틴 내부에서 데이터가 어떻게 흘러가는지 그 원리를 깊이 있게 파고듭니다. 공식 문서가 어떤 기능을 써야 하는가를 알려준다면, 이 책은 왜 그렇게 설계됐고 내부에서는 실제로 어떻게 동작하는가를 논리적으로 증명해 나갑니다.
데이터가 한꺼번에 몰리는 배압(Backpressure) 상황을 제어하거나 복잡한 데이터를 변환하는 과정을 도식화하여 설명한 부분은, 자칫 어렵게 느껴질 수 있는 개념을 직관적으로 이해하는 데 큰 도움이 됩니다. 실무에서 가장 혼란을 겪는 차가운 Flow와 뜨거운 Flow의 차이, 상황별 변환 전략을 풍부한 예제와 함께 제시하여 독자가 실질적인 해결책을 얻도록 돕습니다.
더불어 안드로이드 개발자에게 필수적인 젯팩 컴포트(Jetpack Compose) 상태 관리와 에러 처리 시나리오를 깔끔하게 해결하는 설계 패러다임을 제공합니다. 실무에 즉시 적용 가능한 Flow 테스트 전략과 코루틴 간의 통신을 돕는 Channel까지 상세히 다루고 있어, 비동기 로직이 복잡해질수록 제어의 한계를 느꼈던 개발자들에게 이 책은 확실한 가이드가 될 것입니다.
—김영빈, 쿠팡 Staff Software Engineer
복잡하고 학습 곡선이 가파른 리액티브 프로그래밍과 코루틴 Flow의 결합을 친절하고 직관적으로 풀어낸 책은 찾아보기 힘듭니다. 풍부한 시각화 자료를 통해, 머릿속으로만 그리던 추상적인 비동기 데이터의 동작 원리를 명확하게 이해할 수 있다는 점이 이 책의 가장 큰 장점입니다. Flow의 기초적인 생성 방식부터 배압 처리, 차가운 Flow와 뜨거운 Flow, 그리고 Channel과 테스트 기법 같은 실무 필수 심화 주제까지 아주 체계적으로 다루고 있습니다. 코루틴을 활용한 리액티브 프로그래밍을 실무에 자신 있게 적용하고 싶은 모든 개발자에게 이 책을 강력히 추천합니다.
—이창국, 쿠팡 Senior Software Engineer
이 책에서 다루는 내용
◆ 명령형 프로그래밍의 한계와 리액티브 프로그래밍의 등장 배경을 설명하고, 코틀린의 Flow API가 기존 리액티브 라이브러리와 어떤 차별점을 갖는지 소개한다.
◆ Flow를 생성하는 방법부터 다양한 중간 연산자와 터미널 연산자의 활용 방식을 배우고, Flow의 취소, Flow의 예외 처리, Flow의 결합까지 다뤄 기초를 탄탄히 다질 수 있도록 한다.
◆ 데이터 생산 속도와 소비 속도 간 불균형이 발생했을 때 Flow의 배압 처리를 통해 시스템을 안정적으로 유지하는 방법을 설명한다.
◆ 차가운 Flow와 뜨거운 Flow의 개념을 다룬다. 차가운 Flow만을 사용할 때 발생하는 문제에 대해 알아보고 이를 뜨거운 Flow를 통해 어떻게 해결할 수 있는지를 알아본다.
◆ Flow를 테스트하는 방법을 기초부터 심화까지 알아본다.
◆ Channel을 활용해 코루틴 간에 데이터를 주고받는 방법을 알아본다.
목차
목차
- 1장 개발 환경 설정
- 1.1. 인텔리제이 아이디어 설치 및 둘러보기
- 1.1.1. 인텔리제이 아이디어 설치하기
- 1.2. 코틀린 프로젝트 생성하고 화면 구성 살펴보기
- 1.2.1. 프로젝트 생성하기
- 1.2.2. IDE 구성 살펴보기
- 1.3. 첫 Flow 만들고 수집하기
- 1.3.1. 코루틴 라이브러리 추가하기
- 1.3.2. 첫 Flow 만들기
- 1.4. Flow 디버깅 환경 설정하기
- 1.4.1. 실행 중인 스레드 출력하기
- 1.4.2. 실행 중인 코루틴 이름 출력하기
- 1.5. 요약
- 2장 리액티브 프로그래밍의 등장
- 2.1. 복잡해지는 애플리케이션
- 2.2. 이전의 방식과 한계
- 2.2.1. 명령형 프로그래밍
- 2.2.2. 명령형 프로그래밍의 한계
- 2.3. 리액티브 프로그래밍의 등장
- 2.3.1. 리액티브 프로그래밍
- 2.3.2. 비동기 메시지 통신
- 2.3.3. 리액티브 프로그래밍의 의의와 한계
- 2.4. 리액티브 프로그래밍 도입 전 고려해야 할 사항
- 2.4.1. 가파른 학습 곡선
- 2.4.2. 높은 도입 비용
- 2.5. 명령형과 리액티브 방식의 적절한 균형의 필요성
- 2.6. 요약
- 코루틴의 비동기 처리와 리액티브 프로그래밍의 비동기 처리
- 코루틴의 Flow API와 기존의 리액티브 프로그래밍 라이브러리의 차이
- 3장 리액티브 프로그래밍과 코루틴 Flow
- 3.1. 리액티브 프로그래밍을 구성하는 핵심 개념들
- 3.1.1. 데이터 스트림
- 3.1.2. 옵저버 패턴
- 3.1.3. 선언형 프로그래밍 패러다임
- 3.2. 코루틴 Flow API의 구성 요소
- 3.2.1. Flow 빌더 사용해 데이터 방출하기
- 3.2.2. 터미널 연산자 사용해 데이터 수집해 처리하기
- 3.2.3. 중간 연산자와 선언형 프로그래밍
- 3.2.4. 전체 과정 정리
- 3.3. 요약
- 리액티브 스트림의 등장
- 리액티브 스트림과 코루틴 Flow
- 4장 Flow 빌더
- 4.1. Flow 빌더로 Flow 만들기
- 4.1.1. flow 함수
- 4.1.2. flowOf 함수
- 4.1.3. asFlow 함수
- 4.1.4. 람다식에 대한 asFlow 함수
- 4.2. Flow 인터페이스 구현해 Flow 생성하기
- 4.3. Flow의 실행 컨텍스트 확인하고 전환하기
- 4.3.1. Flow가 동작하는 방식
- 4.3.2. flowOn 함수 사용해 업스트림의 실행 컨텍스트 전환하기
- 4.4. flowOn 함수 심화
- 4.4.1. CoroutineDispatcher가 변경될 때만 새로운 코루틴이 생성된다
- 4.4.2. 새롭게 생성된 코루틴은 기존의 코루틴과 구조화된다
- 4.4.3. flowOn의 인자로 Job을 넘기면 안 된다
- 4.5. 요약
- 5장 중간 연산자
- 5.1. 변환 연산자
- 5.1.1. map 함수 사용해 데이터 변환하기
- 5.1.2. map 함수의 사용
- 5.1.3. map 함수 직접 만들어 보기
- 5.2. 필터 연산자
- 5.2.1. filter 함수 사용해 데이터 필터링하기
- 5.2.2. filterIsInstance 함수로 특정 타입의 데이터만 필터링하기
- 5.2.3. filter 함수의 사용
- 5.2.4. filter 함수 직접 만들어 보기
- 5.3. 중복 제거 연산자
- 5.3.1. distinctUntilChanged 함수 사용해 중복 제거하기
- 5.3.2. distinctUntilChanged의 사용
- 5.3.3. distinctUntilChanged 함수 직접 구현해 보기
- 5.4. take, drop 연산자
- 5.4.1. take 함수
- 5.4.2. drop 함수
- 5.5. -notNull 연산자
- 5.5.1. mapNotNull 함수
- 5.5.2. filterNotNull 함수
- 5.6. 중간 연산자의 동작
- 5.6.1. 중간 연산자의 실제 동작 방식
- 5.6.2. 지연 실행되는 중간 연산자
- 5.7. 요약
- 6장 터미널 연산자
- 6.1. collect 일시 중단 함수 사용해 Flow에서 방출된 원소 처리하기
- 6.1.1. collect 일시 중단 함수
- 6.1.2. collect 일시 중단 함수 사용 시 주의할 점
- 6.1.3. collectIndexed 일시 중단 함수
- 6.2. first 일시 중단 함수 사용해 첫 값 가져오기
- 6.2.1. first 일시 중단 함수
- 6.2.2. first 일시 중단 함수 사용 시 주의할 점
- 6.2.3. firstOrNull 일시 중단 함수
- 6.3. last 일시 중단 함수 사용해 마지막 값 가져오기
- 6.3.1. last 일시 중단 함수
- 6.3.2. lastOrNull 일시 중단 함수
- 6.4. 수집된 값을 컬렉션으로 만드는 함수
- 6.4.1. toList 일시 중단 함수
- 6.4.2. toSet 일시 중단 함수
- 6.4.3. toCollection 일시 중단 함수
- 6.5. 누적 계산을 통해 하나의 값을 반환하는 함수
- 6.5.1. reduce 일시 중단 함수
- 6.5.2. fold 일시 중단 함수
- 6.6. 원소의 총 개수를 반환하는 함수
- 6.6.1. count 일시 중단 함수
- 6.6.2. count 일시 중단 함수 사용해 특정 조건을 만족하는 원소의 개수 구하기
- 6.7. 터미널 연산자 사용 시 주의할 점
- 6.8. 요약
- 7장 Flow의 이해
- 7.1. Flow의 동작 원리
- 7.1.1. 추상화에 감춰진 Flow의 동작 원리
- 7.1.2. Flow 동작 원리 이해하기
- 7.2. Flow의 특징
- 7.2.1. collect 일시 중단 함수가 호출돼야 데이터 생성과 방출이 시작된다
- 7.2.2. 동일한 코루틴 내에서 방출과 수집이 이뤄지면 Flow의 방출과 수집은 순차적으로 실행된다
- 7.3. 생산자 코루틴과 소비자 코루틴 분리하기
- 7.3.1. flowOn 함수 사용해 생산자 코루틴과 소비자 코루틴 분리하기
- 7.3.2. 버퍼 통해 방출된 원소 저장하기
- 7.3.3. buffer 함수 사용해 생산자 코루틴과 소비자 코루틴 분리하기
- 7.3.4. buffer 함수 사용 시 주의할 점
- 7.4. Flow의 완료
- 7.4.1. Flow의 완료 시점 이해하기
- 7.4.2. 무한히 방출되는 Flow에 collect를 호출하면 다음 라인의 코드가 실행되지 않는다
- 7.5. Flow의 컨텍스트 보존
- 7.6. flow 함수의 컨텍스트 변경 제한
- 7.7. SafeFlow와 unsafeFlow
- 7.8. 요약
- 8장 Flow 취소
- 8.1. Flow 취소하기
- 8.2. flow 함수의 취소 확인
- 8.3. 취소 확인 지점이 없는 Flow의 취소 확인
- 8.3.1. flowOf, asFlow 함수를 통해 만들어지는 Flow의 문제
- 8.3.2. 취소 확인 지점 넣어 취소 확인되게 만들기
- 8.3.3. cancellable 함수 사용해 취소 확인하도록 만들기
- 8.4. 요약
- 9장 Flow 예외 처리
- 9.1. try-catch 문을 사용한 Flow 예외 처리
- 9.1.1. try-catch 문으로 collect 호출부 감싸 예외 처리하기
- 9.1.2. 중간 연산자에서 데이터 처리 시 발생하는 예외 처리
- 9.1.3. try-catch 문의 동작을 handleException 중간 연산자로 만들어 보기
- 9.1.4. 예외 투명성을 위반하는 handleException 함수
- 9.2. catch 함수를 사용한 Flow 예외 처리
- 9.2.1. catch 함수 사용해 예외 처리하기
- 9.2.2. catch 함수에서 예외를 처리한 후 다시 전파하기
- 9.2.3. catch 함수가 다운스트림에서 발생한 예외를 구분하는 방법
- 9.3. retry 함수를 통해 예외 발생 시 재시도하기
- 9.3.1. retry 함수
- 9.3.2. retryWhen 함수
- 9.4. 요약
- 10장 생명주기 연산자
- 10.1. onStart 함수
- 10.2. onCompletion 함수
- 10.2.1. onCompletion 함수 사용해 Flow가 완료된 후에 실행될 동작 정의하기
- 10.2.2. onCompletion 함수 사용해 Flow가 취소됐을 때 실행될 동작 정의하기
- 10.3. onEach 함수
- 10.4. onEmpty 함수
- 10.4.1. onEmpty 사용해 방출될 원소가 없을 때 동작 정의하기
- 10.4.2. onEmpty 함수의 람다식에서 값 방출하기
- 10.5. launchIn 함수
- 10.5.1. onEach 함수와 함께 사용하기
- 10.5.2. 코루틴을 생성하는 launchIn 함수
- 10.6. 요약
- 11장 배압
- 11.1. 배압 처리가 필요한 경우 이해하기
- 11.1.1. 배압 처리가 필요하지 않은 경우
- 11.1.2. 배압 처리가 필요한 경우
- 11.2. collectLatest 일시 중단 함수를 활용한 배압 처리
- 11.2.1. collectLatest 일시 중단 함수 사용해 배압 처리하기
- 11.2.2. collectLatest 일시 중단 함수의 동작 원리
- 11.2.3. collectLatest 일시 중단 함수를 사용한 배압 처리의 한계
- 11.3. conflate 함수를 활용한 배압 처리
- 11.3.1. conflate 함수의 동작 방식
- 11.3.2. 코루틴을 분리하는 conflate 함수
- 11.4. buffer 함수를 사용한 배압 처리
- 11.4.1. buffer 함수의 기본 동작
- 11.4.2. capacity 사용해 버퍼 크기 설정하기
- 11.4.3. onBufferOverflow 사용해 버퍼가 가득 찼을 때 실행 전략 설정하기
- 11.5. 요약
- 12장 결합 연산자
- 12.1. zip 함수
- 12.1.1. zip 함수 사용해 Flow 결합하기
- 12.1.2. zip 함수의 동작 방식
- 12.1.3. zip 함수의 완료
- 12.2. combine 함수
- 12.2.1. combine 함수 사용해 Flow 결합하기
- 12.2.2. 여러 개의 Flow 결합하기
- 12.2.3. combine 함수의 완료
- 12.3. 요약
- 13장 평탄화 연산자
- 13.1. 평탄화 이해하기
- 13.1.1. 평탄화가 필요한 경우
- 13.1.2. 평탄화 연산자 사용하지 않고 평탄화해 보기
- 13.2. flattenConcat 함수와 flatMapConcat 함수
- 13.2.1. flattenConcat 함수
- 13.2.2. flatMapConcat 함수
- 13.2.3. flattenConcat, flatMapConcat 함수의 한계
- 13.3. flatMapMerge 함수
- 13.3.1. flatMapMerge 함수의 동작 방식
- 13.3.2. flatMapMerge가 동시에 실행할 수 있는 작업 수 제한하기
- 13.4. flatMapLatest 연산자
- 13.5. 요약
- 14장 차가운 Flow와 뜨거운 Flow
- 14.1. 차가운 Flow와 뜨거운 Flow
- 14.1.1. 차가운 Flow
- 14.1.2. 뜨거운 Flow
- 14.2. SharedFlow
- 14.2.1. SharedFlow의 개념과 기본 동작
- 14.2.2. SharedFlow 만들고 데이터 방출하기
- 14.2.3. replayCache 사용해 collect 호출 이전에 방출된 값 수집하기
- 14.2.4. 완료되지 않는 SharedFlow
- 14.3. StateFlow
- 14.3.1. StateFlow의 개념과 기본 동작
- 14.3.2. StateFlow 만들고 데이터 방출하기
- 14.3.3. 최신 상태만 처리하는 StateFlow
- 14.3.4. 완료되지 않는 StateFlow
- 14.3.5. MutableStateFlow의 동시성 제어
- 14.4. 요약
- 15장 뜨거운 Flow의 활용
- 15.1. 차가운 Flow를 뜨거운 Flow로 변환하기
- 15.1.1. 차가운 Flow를 SharedFlow로 직접 변환해 보기
- 15.1.2. shareIn 함수 사용해 Flow를 SharedFlow로 변환하기
- 15.1.3. stateIn 사용해 Flow를 StateFlow로 변환하기
- 15.2. MutableSharedFlow에 버퍼 설정하기
- 15.2.1. MutableSharedFlow에 대한 emit 일시 중단 함수의 동작
- 15.2.2. MutableSharedFlow에 버퍼 설정하기
- 15.3. MutableSharedFlow의 emit 일시 중단 함수와 tryEmit 함수의 차이
- 15.4. 뜨거운 Flow 캡슐화 전략
- 15.4.1. MutableSharedFlow로 방출하고 SharedFlow로 노출하기
- 15.4.2. MutableStateFlow로 방출하고 StateFlow로 노출하기
- 15.4.3. 외부로 노출하는 객체를 asSharedFlow, asStateFlow를 사용해 변환해야 하는 이유
- 15.5. 요약
- 16장 Flow 테스트
- 16.1. 테스트 환경 설정하기
- 16.2. 코루틴 라이브러리와 코루틴 테스트 라이브러리로 Flow 테스트하기
- 16.2.1. toList 일시 중단 함수 사용해 Flow 테스트하기
- 16.2.2. toList 일시 중단 함수를 사용한 테스트의 한계
- 16.2.3. take 함수 사용해 원소를 무한히 방출하는 Flow 테스트하기
- 16.2.4. first 일시 중단 함수 사용해 Flow 테스트하기
- 16.2.5. 시간의 흐름에 따라 방출되는 값이 변화하는 Flow 테스트
- 16.2.6. 코루틴 테스트 라이브러리 사용해 일시 중단 지점이 포함된 Flow 테스트하기
- 16.2.7. 원소가 무한히 방출되는 Flow 테스트하기
- 16.2.8. backgroundScope 사용해 테스트하기
- 16.3. Turbine을 사용한 코루틴 테스트
- 16.3.1. test 터미널 연산자09
- 16.3.2. awaitItem과 awaitComplete 사용해 방출되는 원소 순차적으로 확인하기
- 16.3.3. cancelAndIgnoreRemainingEvents 사용해 남은 이벤트 무시하기
- 16.3.4. expectMostRecentItem 사용해 특정 시점의 최신 원소 테스트하기
- 16.3.5. awaitError 사용해 예외 테스트하기
- 16.3.6. awaitEvent 사용해 수신된 이벤트 확인하기
- 16.3.7. test 일시 중단 함수에 name 설정하기
- 16.3.8. test 일시 중단 함수에 timeout 설정하기
- 16.3.9. turbineScope와 testIn 사용해 테스트 구성하기
- 16.3.10. SharedFlow, StateFlow 테스트하기
- 16.4. 요약
- 17장 Channel
- 17.1. Channel 기초
- 17.1.1. Channel 인터페이스
- 17.1.2. Channel 만들고 코루틴 간 통신하기
- 17.1.3. for 문 사용해 데이터 수신하기
- 17.1.4. close 함수 호출해 Channel 닫기
- 17.2. Channel에 버퍼 설정하기
- 17.2.1. 기본값이 설정된 Channel의 동작
- 17.2.2. capacity 설정해 버퍼 크기 지정하기
- 17.2.3. onBufferOverflow 통해 버퍼가 가득 찼을 때의 동작 설정하기
- 17.2.4. onUndeliveredElement 통해 수신되지 못한 데이터 처리하기
- 17.3. Channel 취소와 닫기의 이해
- 17.3.1. cancel 함수 사용해 Channel 취소하기
- 17.3.2. Channel을 닫는 것과 취소하는 것의 차이
- 17.3.3. 닫힌 Channel에는 원소를 송신하지 못한다
- 17.3.4. isClosedForSend 프로퍼티 확인해 닫힌 Channel 확인하기
- 17.4. Channel에 데이터를 송신하는 다양한 방법
- 17.4.1. send 일시 중단 함수
- 17.4.2. trySend 함수
- 17.4.3. trySendBlocking 함수
- 17.5. 특수한 동작을 하는 Channel
- 17.5.1. CONFLATED가 설정된 Channel
- 17.5.2. BUFFERED가 설정된 Channel
- 17.6. consume 함수
- 17.6.1. consume 함수 사용해 특정 작업 후 Channel 취소하기
- 17.6.2. consumeEach 일시 중단 함수 사용해 데이터 수신하기
- 17.7. produce 함수
- 17.7.1. produce 함수 사용해 Channel 생성하기
- 17.7.2. produce 함수 이해하기
- 17.8. Fan-in과 Fan-out
- 17.8.1. Fan-in
- 17.8.2. Fan-out
- 17.9. 요약
- 18장 channelFlow와 callbackFlow
- 18.1. channelFlow 함수 사용해 복수의 코루틴에서 원소 방출하기
- 18.2. awaitClose 일시 중단 함수 사용해 Channel이 닫힐 때의 동작 정의하기
- 18.3. callbackFlow 사용해 콜백 기반 API를 Flow로 래핑하기
- 18.4. awaitClose 호출을 강제하는 callbackFlow
- 18.5. 요약


