Flow의 생명주기 연산자(Lifecycle Operator)는 데이터 스트림의 시작, 값의 방출, 완료 및 취소, 비어 있는 스트림 처리 등 Flow 파이프라인의 전반적인 생명주기 흐름을 감시하고 제어할 때 사용됩니다. onStart (수집 시작 시점 제어)onStart는 Flow의 수집(collect)이 시작되기 직전에 특정 동작을 수행하도록 정의하는 연산자입니다.대상 Flow가 실제 수집을 시작하기 전에 초기화 작업이나 사전 대기 상태 등을 구현할 때 활용하기 좋습니다. val flow = flowOf(1, 2, 3) .onStart { println("Flow 수집을 시작합니다") }flow.collect { println(it) }/*[실행 결과]Flow 수집을 시작합니다123*/ onCo..
Flow에서 비동기 데이터 스트림을 처리할 때 예외(Exception)를 어떻게 다루는지는 어플리케이션의 안정성에 직접적인 영향을 미칩니다. 예외 투명성(Exception Transparency) 원칙Flow의 예외 처리 설계에서 가장 중요한 원칙은 예외 투명성입니다.예외 투명성: 예외를 처리하는 연산자가 자신보다 상위인 업스트림에서 발생한 예외만 처리하고, 다운스트림에서 발생한 예외는 처리하지 않도록 설계하는 원칙만약 중간 연산자가 내부에서 무분별하게 try-catch 블록으로 방출 및 소비 흐름 전체를 감싸버리면, 최종 소비자(collect)가 데이터를 처리하다가 터뜨린 예외까지 의도치 않게 삼켜버리거나 꼬이게 됩니다.이는 예외가 발생한 실제 위치와 예외를 처리하는 위치를 명확하게 구분하기 어렵게 ..
Flow 취소의 기본 메커니즘Flow의 collect는 일시 중단 함수(suspend function)이므로, 수집을 호출한 코루틴(Job)을 취소하는 것이 기본적인 취소 방식입니다. 코루틴이 취소되면 Flow의 데이터 방출과 수집도 함께 중단됩니다.val job = launch { numberFlow.collect { value -> println(value) }}delay(1000L)job.cancel() // 코루틴을 취소하여 수집 중단 SafeFlow의 자동 취소 검증flow { ... } 빌더로 생성되는 SafeFlow는 내부적으로 SafeCollector를 사용합니다. SafeCollector는 emit이 호출될 때마다 코루틴의 활성 상태(ensureActive()..
코틀린에서 flow { ... }를 선언하고 collect를 호출하는 과정은 간결해 보이지만, 실제로는 단순한 인터페이스 호출의 연속입니다. Flow와 FlowCollector 인터페이스는 매우 심플하게 정의되어 있습니다.public interface Flow { public suspend fun collect(collector: FlowCollector)}public interface FlowCollector { public suspend fun emit(value: T)}이 추상화된 관계를 이해하기 위해, 익명 객체를 활용해 Flow의 실체를 직접 구현해 보면 다음과 같습니다.// 1. Flow 인터페이스를 직접 구현하는 객체 생성 (생산자)val numberFlow: Flow = obje..
중간 연산자는 Flow에서 발행되는 데이터를 가공하거나 필터링하여 새로운 Flow를 반환하는 역할을 합니다. map 함수 (데이터 변환하기)map 함수는 기존 Flow에서 방출된 각 원소를 변환하여 새로운 Flow를 만들 때 사용합니다.val numberFlow: Flow = flow { emit(1) delay(1000L) emit(2)}// 각 원소에 2를 곱해 새로운 Flow 생성val doubledFlow: Flow = numberFlow.map { it * 2 }map의 동작 원리는 아래와 같이 정리할 수 있습니다.inline fun Flow.map( crossinline transform: suspend (value: T) -> R): Flow = flow { // ..
Flow 객체를 생성하는 방법과 flowOn을 활용한 실행 컨텍스트 전환에 대해 정리해 보겠습니다. flow 함수flow 함수는 가장 기본이 되는 Flow 빌더입니다. 매개변수로 전달되는 block 람다식 안에서 emit 함수를 호출하여 데이터를 방출합니다.val numberFlow: Flow = flow { for (number in 1..3) { emit(number) // 데이터 방출 delay(1000L) // 1초 지연 (일시 중단 함수 사용 가능) }}실제 내부 코드를 살펴보면, flow 빌더는 내부적으로 SafeFlow 객체를 생성하여 반환하도록 구현되어 있습니다.public fun flow(block: suspend FlowCol..
리액티브 프로그래밍(Reactive Programming)이란 데이터의 변화와 흐름을 연속적인 스트림으로 처리하는 선언형 프로그래밍 패러다임입니다.선언적 반응: 데이터 흐름을 정의하고 변화에 자동으로 반응하는 구조를 가집니다.시스템 안정성과 배압(Backpressure): 발행자와 구독자가 독립적으로 동작하며, 특히 배압을 통해 데이터 처리 속도를 조절합니다. 발행 속도가 너무 빠를 때 이를 제어해 시스템 과부하를 막는 필수적인 메커니즘입니다. 강력한 도구임은 분명하지만 학습 곡선이 높고 전체 아키텍처를 수정해야 하는 부담이 있습니다. 모든 로직에 적용하기보다는 복잡한 비동기 데이터 흐름이 필요한 영역에만 리액티브를 활용하는 전략적 균형이 필요합니다. 리액티브 프로그래밍의 3대 핵심 개념리액티브 프로..
멀티 모듈 프로젝트에서는 보통 app 모듈이 Application을 가지고 있고, core나 base 모듈에 공통 코드가 들어갑니다. app 모듈은 앱 전역에서 공유되는 객체를 제공하는 컴포넌트를 두기에 적절합니다.구조 잡는 기준멀티 모듈에서 같은 레벨의 모듈끼리는 서로 의존하지 않는 것이 일반적인 규칙입니다. 같은 레벨에서 의존이 생기면 공통 로직을 상위 모듈로 올리거나, 상위 모듈을 확장하는 새 모듈을 만들고 두 모듈이 그 모듈에 의존하도록 리팩터링하는 방식을 권장합니다.컴포넌트를 모듈에 만드는 경우는 보통 두 가지입니다. 필드 주입이 필요하거나, 특정 흐름에서 객체를 스코프 처리해야 할 때입니다. 둘 다 아니라면 컴포넌트 대신 @Provides나 @Binds를 가진 Dagger 모듈을 노출하는 방식..
Android에서 Dagger를 적용할 때는 Application 수명주기에 맞는 그래프를 만들고, Activity와 Fragment에는 필드 주입으로 연결하는 방식이 기본입니다. 그래프는 Dagger가 관리하는 객체 관계라고 보면 됩니다.가능하면 생성자 주입을 사용생성자 주입이 어려운 타입은 모듈로 제공모듈은 컴포넌트에서 한 번만 선언하는 구조로 작성스코프는 기능 이름보다 수명주기 기준으로 작성 Android의 DaggerApplication에 그래프 두기Application에 컴포넌트를 만들어 두면 앱 실행 동안 그래프를 공유할 수 있습니다. 앱 전역에서 재사용할 객체는 이 그래프에 두는 것이 자연스럽습니다.// Application 그래프 정의@Componentinterface Application..
Dagger는 Java 및 Kotlin 애플리케이션에서 사용하는 정적 의존성 주입(Dependency Injection) 프레임워크입니다.Android 환경에서 객체 생성 및 종속성 관리를 자동화하여, 유지보수성과 테스트 용이성을 향상시킵니다.컴파일 타임 코드 생성으로 리플렉션 기반 DI보다 성능 우수의존성 그래프 자동 생성 및 검증Factory 클래스 생성을 통한 객체 생성 관리스코프 어노테이션을 활용한 객체 생명주기 관리서브컴포넌트(Subcomponent) 지원으로 특정 흐름 단위 DI 가능주요 구성 요소@Inject: 생성자 또는 필드에 붙여 의존성을 주입@Module: Dagger에 바인딩을 제공하는 클래스 정의@Provides: 생성자 주입이 불가능한 타입을 수동으로 제공@Component: 의..

