Jetpack Navigation은 내부적으로 FragmentManager나 Compose의 NavHostController를 이용해 자체적으로 백스택을 관리합니다. 각 목적지(Destination)는 NavGraph
에 등록되며, navigate()
호출 시 새로운 목적지를 스택에 push하고, popBackStack()
호출 시 pop 합니다.
FragmentManager
의 백스택과 별도로 Navigation 전용 백스택을 유지합니다.NavHostController
가 백스택을 BackStackEntry
목록으로 보존합니다.NavController
혹은 NavHostController
를 사용해 다음과 같이 백스택을 조작할 수 있습니다:
메서드 | 설명 |
---|---|
navigate(destinationId) | 새로운 목적지로 이동하며, 백스택에 추가됨 |
popBackStack() | 현재 목적지를 백스택에서 제거 (뒤로가기 효과) |
popBackStack(destinationId, inclusive) | 특정 목적지까지 백스택 제거 가능(inclusive=true면 해당 destination도 제거됨) |
navigate(route) { popUpTo(...) { inclusive = ... } } | Compose에서는 popUpTo 로 백스택 조절 가능 |
clearBackStack() (직접 지원은 없음) | navigate(start) { popUpTo(graph.startDestinationId) { inclusive=true } } 로 대체 가능 |
kotlin
복사편집
navController.navigate("home") {
popUpTo("login") {
inclusive = true
}
}
→ login을 포함해서 그 아래 백스택을 제거하고 home으로 이동
Safe Args는 Jetpack Navigation의 공식 Gradle Plugin으로, 목적지 간에 데이터를 전달할 때 타입 안전성을 보장하는 도구입니다. arguments
를 사용하는 전통적인 방법은 Bundle 기반이므로 타입 오류가 런타임에 발생할 수 있지만, Safe Args는 컴파일 타임에 타입을 체크합니다.
nav_graph.xml
에서 <argument>
로 목적지에 전달할 값을 정의하면Directions
클래스와 Args
클래스를 생성합니다FragmentADirections.actionToFragmentB(param1, param2)
형태로 호출FragmentBArgs.fromBundle(arguments)
로 수신Int
, String
, Boolean
, Parcelable
, Serializable
등 타입을 명시하면, 컴파일 시 잘못된 타입 전달 시 에러 발생xml
복사편집
<fragmentandroid:id="@+id/fragmentB"
android:name="com.example.FragmentB">
<argumentandroid:name="userId"
app:argType="string"
android:defaultValue="guest" />
</fragment>
kotlin
복사편집
val action = FragmentADirections.actionToFragmentB("user123")
findNavController().navigate(action)
kotlin
복사편집
val args: FragmentBArgs by navArgs()
val userId = args.userId
항목 | 요약 |
---|---|
백스택 처리 | Jetpack Navigation은 NavHost 기반으로 자체 백스택 유지. navigate , popBackStack , popUpTo 로 조작 |
Safe Args | 목적지 간 안전한 데이터 전달 방식. 컴파일 시 타입 검사를 통해 오류 방지 |
Paging 3 라이브러리는 세 가지 로딩 상태(load type)에서 각각 오류가 발생할 수 있습니다:
LoadType | 설명 |
---|---|
REFRESH | 첫 페이지 로딩 시 |
APPEND | 다음 페이지 로딩 시 |
PREPEND | 이전 페이지 로딩 시 (대부분 사용 안 함) |
LoadState
PagingDataAdapter
는 LoadStateListener
를 통해 각 로딩 상태의 결과를 알려줍니다:
kotlin
복사편집
adapter.addLoadStateListener { loadStates ->
val refreshState = loadStates.refresh
val appendState = loadStates.append
if (refreshState is LoadState.Error) {
// 초기 로딩 오류
}
if (appendState is LoadState.Error) {
// 스크롤 시 오류 (네트워크 등)
}
}
adapter.retry()
LoadState.Error
상태가 감지되면, 재시도 버튼을 보여주고 adapter.retry()
호출로 실패한 요청을 다시 시도할 수 있습니다.kotlin
복사편집
retryButton.setOnClickListener {
adapter.retry()
}
PagingData는 Flow<PagingData<T>>
로 전달되므로, Flow에서 catch
연산자를 사용해 오류를 잡을 수도 있습니다.
kotlin
복사편집
val pagingFlow = repository.getPagingData()
.catch { e ->
// 전체 스트림 수준의 오류 처리
}
.cachedIn(viewModelScope)
다만 이 catch는 PagingSource 내부에서 throw된 예외가 아니라, Flow 전체에서 발생하는 예외를 잡는 용도입니다. 대부분의 오류 처리는 LoadState 기반으로 UI에서 하는 것이 바람직합니다.
PagingSource의 load()
함수에서 예외가 발생하면 Paging은 자동으로 LoadState.Error
로 전파합니다.
kotlin
복사편집
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> {
return try {
val data = api.getItems(page = params.key ?: 1)
LoadResult.Page(
data = data,
prevKey = null,
nextKey = if (data.isEmpty()) null else params.key!! + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
목적 | 전략 |
---|---|
오류 UI 노출 | adapter.loadStateFlow 또는 addLoadStateListener 로 상태 감지 후, 오류 메시지 + 재시도 버튼 노출 |
재시도 처리 | adapter.retry() 호출 (자동 실패 요청 재시도) |
네트워크 요청 내 예외 | PagingSource.load() 내에서 try-catch 로 감싸고 LoadResult.Error(e) 반환 |
전체 스트림 오류 | Flow.catch {} 사용 (예: repository → ViewModel 연결 시) |
kotlin
복사편집
val loadState = lazyPagingItems.loadState
when (val refreshState = loadState.refresh) {
is LoadState.Loading -> CircularProgressIndicator()
is LoadState.Error -> RetrySection { lazyPagingItems.retry() }
}
Jetpack Paging은 오류 처리를 LoadState 기반의 UI 처리 + retry()
호출 기반의 자동 재시도 메커니즘으로 분리하고 있으며, PagingSource
와 ViewModel
, UI 각 계층에서 역할에 맞는 처리를 하는 것이 중요합니다.
필요하시면 Compose 기반 RetrySection 컴포저블, 더 복잡한 ErrorView 구성도 예시로 드릴 수 있습니다.