Flow
란 코루틴에서 단일 값만 반환하는 Suspend fun 과 달리 여러 값을 순차적으로 내보낼 수 있는 유형입니다.
기본적으로 코루틴 기반으로 동작하며, 데이터베이스 조회, 파일 입출력 등과 같은 비동기 작업을 처리할 때 Flow를 사용하면 콜백 헬
을 피하고 코드의 가독성과 유지보수성을 높일 수 있습니다.
Flow에서 사용하는 주요 함수는 다음과 같습니다.
emit
: Flow를 생성하거나 중간 연산자에서 값을 내보낼 때 사용하는 함수입니다. emit 함수를 호출하여 값을 Flow로 내보내면, Flow를 수집하는 코루틴이 해당 값을 받게 됩니다.
flow
: Flow를 생성하기 위한 빌더 함수입니다. flow 빌더 함수 내에서 emit 함수를 사용하여 값을 Flow로 내보낼 수 있습니다.
collect
: Flow를 수집하는 함수로, 값을 기다리고 받을 때까지 현재 코루틴을 일시 정지시킵니다. collect 함수를 사용하여 Flow에서 값을 받으면서 해당 값을 처리할 수 있습니다.
map
: Flow의 값을 변형하기 위한 중간 연산자입니다. 각 요소를 변형하여 새로운 Flow를 반환합니다.
filter
: Flow의 값을 필터링하기 위한 중간 연산자로, 주어진 조건에 맞는 요소만 포함된 새로운 Flow를 반환합니다.
onEach
: Flow의 각 요소에 대해 추가 작업을 수행하기 위한 중간 연산자입니다. 주로 로깅 또는 부가적인 작업에 사용됩니다.
catch
: Flow에서 예외 처리를 하기 위한 중간 연산자입니다. 예외가 발생할 경우 처리할 작업을 정의할 수 있습니다.
flowOn
: Flow의 상위 스트림에서 CoroutineContext를 변경하기 위한 중간 연산자입니다. Flow의 값을 emit하는 CoroutineContext를 변경하고자 할 때 사용됩니다.
flattenConcat
: 여러 개의 Flow를 연결하여 순차적으로 처리하는 함수입니다. 첫 번째 Flow가 완료된 후에 두 번째 Flow를 처리합니다.
flattenMerge
: 여러 개의 Flow를 병합하여 동시에 처리하는 함수입니다. 모든 Flow를 동시에 처리하며, 결과를 수집하는 Flow를 반환합니다.
combine
: 두 개의 Flow를 결합하여 새로운 값을 생성하는 함수입니다. 각 Flow가 값을 내보낼 때마다 조합 함수를 사용하여 결과를 생성합니다.
zip
: 두 개의 Flow를 병렬로 결합하는 함수입니다. 각 Flow가 동일한 인덱스의 요소를 가지고 있을 때, 조합 함수를 사용하여 결과를 생성합니다.
예제
사용자 목록을 불러오는 간단한 예제를 보도록 하겠습니다.
class UserRepository {
private val apiService = ApiService()
// Flow를 사용하여 사용자 이름 목록을 가져오는 함수
fun getUserNames(): Flow<List<String>> = flow {
// 네트워크 요청을 비동기적으로 실행
val names = apiService.fetchUserNames()
emit(names) // 사용자 이름 목록을 Flow로 내보냄
}
}
네트워크 요청을 비동기적으로 실행하는 fetchuserNames를 호출하고, 해당 요청의 결과로 얻은 사용자 이름 목록을 emit
하여 Flow로 보냅니다.
class UserViewModel : ViewModel() {
private val userRepository = UserRepository()
// 사용자 이름 목록을 담을 MutableLiveData
private val _userNames = MutableLiveData<List<String>>()
val userNames: LiveData<List<String>>
get() = _userNames
// Flow를 수집하여 사용자 이름 목록 업데이트
fun fetchUserNames() {
viewModelScope.launch {
userRepository.getUserNames()
.collect { names ->
_userNames.value = names
}
}
}
}
collect
를 사용해서 Flow에 있는 사용자 목록을 수집합니다.
class MainActivity : AppCompatActivity() {
private lateinit var userViewModel: UserViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
// 사용자 이름 목록의 LiveData를 관찰하여 UI 업데이트
userViewModel.userNames.observe(this, Observer { names ->
// UI 업데이트 코드
// names를 사용하여 RecyclerView 등에 사용자 이름 목록을 표시
})
// 사용자 이름 목록 가져오기 요청
userViewModel.fetchUserNames()
}
}
UserRepository
에서 Flow를 생성하여 네트워크 요청을 비동기적으로 실행하고, 결과를 Flow로 내보내며, viewModel
에서 해당 Flow에 있는 정보를 비동기적으로 수집합니다. Flow의 값이 업데이트 되면 LiveData에 의해 MainActivity
의 UI가 업데이트 됩니다.