[Android-Manifest-Interview] Jetpack Library

Pardess·2025년 6월 3일
1

Q1) Jetpack Navigation 라이브러리는 백스택을 어떻게 처리하며, NavController를 사용하여 프로그래밍 방식으로 백스택을 어떻게 조작할 수 있나요?

백스택 처리 방식

Jetpack Navigation은 내부적으로 FragmentManager나 Compose의 NavHostController를 이용해 자체적으로 백스택을 관리합니다. 각 목적지(Destination)는 NavGraph에 등록되며, navigate() 호출 시 새로운 목적지를 스택에 push하고, popBackStack() 호출 시 pop 합니다.

  • Fragment 기반이라면 FragmentManager의 백스택과 별도로 Navigation 전용 백스택을 유지합니다.
  • Compose 기반에서는 NavHostController가 백스택을 BackStackEntry 목록으로 보존합니다.

NavController로 백스택 조작하기

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 } }로 대체 가능

🔎 예시 (Compose 기준)

kotlin
복사편집
navController.navigate("home") {
    popUpTo("login") {
        inclusive = true
    }
}

→ login을 포함해서 그 아래 백스택을 제거하고 home으로 이동


Q2) Safe Args는 무엇이며, Jetpack Navigation Component에서 목적지 간에 데이터를 전달할 때 타입 안전성을 어떻게 향상시키나요?

① Safe Args란?

Safe Args는 Jetpack Navigation의 공식 Gradle Plugin으로, 목적지 간에 데이터를 전달할 때 타입 안전성을 보장하는 도구입니다. arguments를 사용하는 전통적인 방법은 Bundle 기반이므로 타입 오류가 런타임에 발생할 수 있지만, Safe Args는 컴파일 타임에 타입을 체크합니다.

② Safe Args의 작동 방식

  • nav_graph.xml에서 <argument>로 목적지에 전달할 값을 정의하면
  • 빌드시 자동으로 Directions 클래스와 Args 클래스를 생성합니다
    • FragmentADirections.actionToFragmentB(param1, param2) 형태로 호출
    • FragmentBArgs.fromBundle(arguments)로 수신

🔒 Safe Args의 타입 안전성

  • Int, String, Boolean, Parcelable, Serializable 등 타입을 명시하면, 컴파일 시 잘못된 타입 전달 시 에러 발생
  • Nullable 여부, 기본값 여부까지 명확히 관리 가능

🔎 예시

xml
복사편집
<fragmentandroid:id="@+id/fragmentB"
    android:name="com.example.FragmentB">
    <argumentandroid:name="userId"
        app:argType="string"
        android:defaultValue="guest" />
</fragment>
  • 보내기 (FragmentA):
kotlin
복사편집
val action = FragmentADirections.actionToFragmentB("user123")
findNavController().navigate(action)
  • 받기 (FragmentB):
kotlin
복사편집
val args: FragmentBArgs by navArgs()
val userId = args.userId

✅ 요약

항목요약
백스택 처리Jetpack Navigation은 NavHost 기반으로 자체 백스택 유지. navigate, popBackStack, popUpTo로 조작
Safe Args목적지 간 안전한 데이터 전달 방식. 컴파일 시 타입 검사를 통해 오류 방지

Q) Paging 라이브러리는 데이터 로딩 중 발생하는 오류를 어떻게 처리하며, 페이징된 데이터 흐름에서 오류 처리 및 재시도 메커니즘을 구현하기 위한 권장 전략은 무엇인가요?


1. 오류 발생 지점

Paging 3 라이브러리는 세 가지 로딩 상태(load type)에서 각각 오류가 발생할 수 있습니다:

LoadType설명
REFRESH첫 페이지 로딩 시
APPEND다음 페이지 로딩 시
PREPEND이전 페이지 로딩 시 (대부분 사용 안 함)

2. 오류 감지 방식: LoadState

PagingDataAdapterLoadStateListener를 통해 각 로딩 상태의 결과를 알려줍니다:

kotlin
복사편집
adapter.addLoadStateListener { loadStates ->
    val refreshState = loadStates.refresh
    val appendState = loadStates.append

    if (refreshState is LoadState.Error) {
        // 초기 로딩 오류
    }

    if (appendState is LoadState.Error) {
        // 스크롤 시 오류 (네트워크 등)
    }
}

3. 재시도 메커니즘: adapter.retry()

  • PagingAdapter에는 자동으로 재시도 기능이 내장되어 있습니다.
  • LoadState.Error 상태가 감지되면, 재시도 버튼을 보여주고 adapter.retry() 호출로 실패한 요청을 다시 시도할 수 있습니다.
kotlin
복사편집
retryButton.setOnClickListener {
    adapter.retry()
}

4. ViewModel 내부에서 오류 잡기 (Flow 기반)

PagingData는 Flow<PagingData<T>>로 전달되므로, Flow에서 catch 연산자를 사용해 오류를 잡을 수도 있습니다.

kotlin
복사편집
val pagingFlow = repository.getPagingData()
    .catch { e ->
        // 전체 스트림 수준의 오류 처리
    }
    .cachedIn(viewModelScope)

다만 이 catch는 PagingSource 내부에서 throw된 예외가 아니라, Flow 전체에서 발생하는 예외를 잡는 용도입니다. 대부분의 오류 처리는 LoadState 기반으로 UI에서 하는 것이 바람직합니다.


5. PagingSource에서의 오류 처리

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)
    }
}

6. 권장 전략 요약

목적전략
오류 UI 노출adapter.loadStateFlow 또는 addLoadStateListener로 상태 감지 후, 오류 메시지 + 재시도 버튼 노출
재시도 처리adapter.retry() 호출 (자동 실패 요청 재시도)
네트워크 요청 내 예외PagingSource.load() 내에서 try-catch로 감싸고 LoadResult.Error(e) 반환
전체 스트림 오류Flow.catch {} 사용 (예: repository → ViewModel 연결 시)

7. Compose 예시

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() 호출 기반의 자동 재시도 메커니즘으로 분리하고 있으며, PagingSourceViewModel, UI 각 계층에서 역할에 맞는 처리를 하는 것이 중요합니다.

필요하시면 Compose 기반 RetrySection 컴포저블, 더 복잡한 ErrorView 구성도 예시로 드릴 수 있습니다.

0개의 댓글