네트워크_상태에_따라_MyPage_UI_변경_작업 - boostcampwm-2024/and04-Nature-Album GitHub Wiki

네트워크 상태에 따라 MyPage UI 변경 작업

✅ 진행한 기능

  • MyPage 화면에서 동기화 기능친구 기능 UI를 네트워크 연결 상태에 따라 동적으로 변경.
  • 네트워크 상태를 실시간으로 추적하여 UI 업데이트를 반영하도록 설계.

💡 학습 내용

1. ConnectivityManager의 활용

  • ConnectivityManager는 네트워크 상태를 확인하거나 상태 변화를 감지할 수 있는 주요 클래스이다.
  • 네트워크 상태를 확인하려면 getNetworkCapabilities()를 사용하며, 실시간 상태 변화를 감지하려면 NetworkCallback을 등록하여 이벤트를 수신한다.

2. 주요 API

API 설명 사용 시기
getNetworkCapabilities() 현재 네트워크 상태를 즉시 확인. 단순한 상태 확인이 필요할 때
registerDefaultNetworkCallback() 앱이 사용하는 기본 네트워크의 상태 변화를 모니터링. 앱의 현재 연결 상태를 추적해야 할 때
registerNetworkCallback() 특정 네트워크 조건(Wi-Fi, 모바일 데이터 등)에 따라 상태 변화를 모니터링. 특정 조건의 네트워크 상태를 세부적으로 모니터링해야 할 때

3. registerDefaultNetworkCallback() vs. registerNetworkCallback()

메서드 특징 사용 이유
registerDefaultNetworkCallback() 앱의 기본 네트워크 상태를 추적하며, 단일 네트워크 상태를 중심으로 동작. 현재 연결 상태를 즉시 반영하며, 모든 UI에서 단일 네트워크 상태를 필요로 할 때 적합.
registerNetworkCallback() 특정 조건에 맞는 네트워크를 추적 가능. 예: Wi-Fi, 셀룰러 데이터, VPN 등. 특정 네트워크 조건을 구분하여 여러 네트워크를 동시에 관리해야 하는 경우 적합.

4. NetworkCapabilities

  • NetworkCapabilities는 네트워크의 전송 유형과 기능 정보를 캡슐화하는 객체이다.
    • 전송 유형 확인: TRANSPORT_WIFI, TRANSPORT_CELLULAR와 같은 상수를 사용하여 네트워크 전송 유형을 확인.

      caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
    • 기능 확인: 네트워크가 지원하는 고급 기능(FULL_DUPLEX, VPN 등)을 확인.

5. 학습 자료


💡 문제 해결 과정 기록

1. 초기 문제 정의

  • 팀원이 작성한 NetworkState는 네트워크 상태를 즉시 확인할 수 있는 간단한 유틸리티로, Context에 의존하지 않고 앱 어디에서든 사용할 수 있었다.
  • 그러나 네트워크 연결 상태가 변할 때 이를 실시간으로 감지하거나 UI에 반영하는 기능이 없었다.
  • 동기화 기능과 친구 기능은 네트워크 상태에 따라 즉각적인 UI 변경이 필요하였으나, 기존 NetworkState는 이 요구를 충족하지 못했다.

2. 설계 및 고려 사항

  1. 실시간 네트워크 상태 관리 필요
    • 네트워크 상태가 변할 때 이를 감지하고 UI에 반영해야 한다.
    • 이를 위해 StateFlow와 네트워크 콜백을 사용하는 구조가 필요하다.
  2. 결합도 관리
    • NetworkManager를 UI에서 직접 호출하면 Navigation 등 다른 요소와 결합도가 높아질 수 있다.
    • 따라서 ViewModel을 통해 네트워크 상태를 전달하여 결합도를 낮추는 방식으로 설계했다.
  3. 다양한 요구사항 대응
    • 단순히 네트워크 상태를 확인해야 하는 경우와 실시간 추적이 필요한 경우를 구분하여 관리.
    • NetworkStateNetworkManager를 병행 사용하여 각각의 요구사항에 대응할 수 있도록 설계했다.

3. NetworkManager 직접 사용 시도의 문제

  • 초기에는 NetworkManager를 UI에서 직접 호출하려고 했다.
  • Navigation에서 NetworkManager를 주입하기 위해 매번 명시적으로 전달해야 했으며, 이로 인해 코드 결합도가 높아지고 유지보수가 어려워졌다.
  • 또한, NetworkManager의 기능을 확장하거나 여러 상태를 관리해야 할 경우, 이 방식은 확장성 측면에서 한계를 보일 가능성이 있었다.

4. ViewModel을 통한 설계 변경

  • Navigation과의 결합도를 낮추고 유지보수를 용이하게 하기 위해 NetworkViewModel을 도입했다.
  • ViewModel을 통해 NetworkManager의 상태를 구독하고 UI에 전달함으로써 UI와 네트워크 로직을 분리할 수 있었다.
  • 현재는 네트워크 상태만 관리하지만, 추후 네트워크 상태에 따라 작업을 수행하는 로직을 추가할 경우에도 쉽게 확장 가능하도록 설계되었다.

4. 최종 설계

(1) NetworkManager

  • 싱글톤으로 설계되어 앱 전역에서 네트워크 상태를 관리한다.
  • ConnectivityManager.NetworkCallback을 등록하여 네트워크 상태 변화를 감지하고, StateFlow를 통해 상태를 전달한다.
@Singleton
class NetworkManager @Inject constructor(
    @ApplicationContext private val context: Context
) {
    private val _networkState = MutableStateFlow(NetworkState.DISCONNECTED)
    val networkState: StateFlow<Int> get() = _networkState

    private val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: android.net.Network) {
            updateNetworkState()
        }

        override fun onLost(network: android.net.Network) {
            updateNetworkState()
        }
    }

    init {
        connectivityManager.registerDefaultNetworkCallback(networkCallback)
        updateNetworkState()
    }

    private fun updateNetworkState() {
        val currentNetwork = connectivityManager.activeNetwork
        val caps = connectivityManager.getNetworkCapabilities(currentNetwork)

        _networkState.value = when {
            caps == null -> NetworkState.DISCONNECTED
            caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NetworkState.CONNECTED_WIFI
            caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> NetworkState.CONNECTED_DATA
            else -> NetworkState.DISCONNECTED
        }
    }
}

(2) NetworkViewModel

  • NetworkManager를 주입받아 네트워크 상태를 UI에 전달.
  • UI는 ViewModel을 구독하여 상태 변화를 반영한다.
@HiltViewModel
class NetworkViewModel @Inject constructor(
    private val networkManager: NetworkManager
) : ViewModel() {
    val networkState: StateFlow<Int> get() = networkManager.networkState
}

5. 결론 및 고민

1️⃣ 결론

  1. 실시간 반영의 필요성
    • 네트워크 상태를 실시간으로 반영해야 하는 UI에서는 NetworkManagerViewModel을 통해 사용하는 방식이 적합하다.
    • 이를 통해 상태 관리와 UI 업데이트를 분리하고, 네트워크 상태의 변화를 UI에 즉시 반영할 수 있다.
  2. 간단한 확인의 필요성
    • 네트워크 상태 확인만 필요한 경우에는 기존의 NetworkState를 사용할 수 있다.
    • NetworkState는 Context 의존성이 없으므로 간단한 작업에서 독립적으로 사용하기 용이하다.
  3. 병행 사용의 필요성
    • 두 가지 방식을 병행 사용하여, 요구사항에 따라 적합한 방법을 선택할 수 있도록 설계하였다.

2️⃣ 고민 및 확장성

  1. NetworkViewModel 확장 가능성
    • 현재 NetworkViewModel단일 네트워크 상태만 전달하지만, 추후 네트워크 상태에 따라 작업을 수행하는 로직을 추가할 때도 유연하게 확장할 수 있다.
    • 예를 들어, 특정 상태에서 동기화 작업을 자동으로 수행하거나, 네트워크가 복구되었을 때 데이터를 다시 로드하는 등의 로직을 추가할 수 있다.
  2. NetworkState와 NetworkManager 통합
    • NetworkStateNetworkManager는 현재 다른 요구사항에 대응하기 위해 분리된 상태로 유지되고 있다.
    • 그러나 두 코드가 중복되는 부분이 있어 하나의 관리 클래스로 통합하는 방안도 고려할 수 있다. 🤔
  3. 분리 유지의 필요성
    • 화면별로 실시간 네트워크 상태가 필요한 화면즉시 상태 확인만 필요한 화면이 나뉘기 때문에, 현재와 같이 분리하여 사용하는 것이 적합하다는 판단도 가능하다.
    • 실시간 추적이 필요한 화면에서는 NetworkManager를 사용하고, 단순 확인이 필요한 화면에서는 NetworkState를 사용하는 것이 더 효율적일 수 있다.
  4. 통합 vs. 분리
    • 통합하면 코드 중복을 줄이고, 네트워크 상태 관리가 중앙 집중화되는 이점이 있다.
    • 분리하면 요구사항에 따라 적합한 방식을 선택적으로 사용할 수 있어 코드가 더 유연하게 동작한다.

3️⃣ 최종 고민

  • 현재는 분리된 상태를 유지하며 요구사항별로 적합한 방식을 사용하는 것이 바람직하다.
  • 추후 프로젝트의 복잡도가 증가하거나 네트워크 상태 관련 요구사항이 확장되면, 통합 가능성을 다시 검토할 수 있다.
  • 이와 함께 NetworkManager에 상태 관리 외의 추가적인 기능을 제공하여, 실시간 네트워크 이벤트 기반 작업을 더 쉽게 관리할 수 있는 구조로 발전시킬 여지가 있다.

🎥 동작 영상

default.mp4
⚠️ **GitHub.com Fallback** ⚠️