[Team 박카스] 팀 프로젝트 2022.02.08 코드 리뷰 - 2

ErroredPasta·2022년 2월 19일
0

코드리뷰

목록 보기
3/4
post-custom-banner

이번 글에서는 제가 작성한 코드를 팀원에게 설명 후 받은 질문들에 대해서 다루겠습니다.

질문 내용

카테고리별 버튼이 제대로 동작하지 않는 버그가 있는 것을 인지하고 있는가?

// HomeFragment
override fun observeData() = with(binding) {
    LocationData.locationStateLiveData.observe(viewLifecycleOwner) {
        when (it) {
            is LocationState.Success -> {
                initViewPager()
                // smoothScroll이 true
                viewPager.currentItem = args.goToTab.ordinal
            }
        }
    }
}

기존의 코드는 위와 같이 viewPager.currentItem = args.goToTab.ordinal로 ViewPager의 현재 아이템을 설정하였습니다. 이렇게 아이템을 설정하게되면 smoothScroll이 기본값인 true로 설정됩니다.

override fun observeData() = with(binding) {
    activityViewModel.locationData.observe(viewLifecycleOwner) {
        when (it) {
            is MainState.Success -> {
                initViewPager()
                // smoothScroll이 false
                viewPager.setCurrentItem(args.goToTab.ordinal, false)
            }
        }
    }
}

ViewPager의 currentItem을 설정할 때 smoothScroll을 false로 설정하여 해결하였습니다.

HomeMainFragment에서 observe(viewLifecycleOwner)와 observe(this@HomeMainFragment)의 차이점을 알고 있는가?

viewLifecycleOwnerthis@HomeMainFragment은 다른 Lifecycle을 가지고 있습니다. [1]

화면이 전환 되어 Fragment의 View가 detach된 경우 Fragment의 View 상태는 DESTROYED로 전환되지만 Fragment는 CREATED가 됩니다.
즉, observe(this@HomeMainFragment)를 하게되면 화면이 전환되더라도 Observer가 제거되지 않고 여전히 LiveData를 observe하게 됩니다.
그리고 해당 프로젝트에서는 Observer를 onViewCreated에서 추가하기 때문에 observe(this@HomeMainFragment)를 사용하면 Observer가 계속 추가되는 문제가 있습니다.

AAC ViewModel과 일반적인 ViewModel의 차이점을 알고 있는가?

ViewModel contains data-transformers that convert Model types into View types, and it contains Commands the View can use to interact with the Model. [2]

ViewModel은 View에서 사용하는 Model로 View에서 나타낼 데이터와 View가 Model과 상호작용할 수 있는 Method가 존재합니다.

// ComponentActivity
getLifecycle().addObserver(new LifecycleEventObserver() {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            // Clear out the available context
            mContextAwareHelper.clearAvailableContext();
            // And clear the ViewModelStore
            if (!isChangingConfigurations()) {
                getViewModelStore().clear(); // ViewModelStore를 Clear
            }
        }
    }
});

여기에서 AAC ViewModel은 View의 Lifecycle을 LifecycleEventObserver로 observe하며 View에 ON_DESTROY 이벤트가 발생하면 clear() 자체적으로 호출하여 해당 View의 ViewModelStore를 정리하면서 ViewModel이 clear됩니다.
단, configuration change로 인한 ON_DESTROY의 경우는 예외입니다.
Fragment의 경우 직접 Lifecycle을 observe하는 것이 아닌 FragmentStateManager에서 destroy()의 내부에서 ViewModelStore을 clear하는 method를 호출합니다.

HomeMainViewModel에서 liveData = _liveData와 liveData get() = _liveData의 차이점이 무엇인가?

Backing field의 생성 유무의 차이점이 존재합니다. Kotlin은 backing field를 필요할 때만 생성하게 됩니다. [3]

private val _marketData = MutableLiveData<HomeMainState>(HomeMainState.Uninitialized)
val marketData: LiveData<HomeMainState> = _marketData

위와 같은 경우 marketData의 backing field가 생성되어 _marketData의 reference를 저장하고 있습니다.

private final MutableLiveData _marketData;
@NotNull
private final LiveData marketData;

...

@NotNull
public final LiveData getMarketData() {
    return this.marketData;
}

위 Kotlin의 코드를 Java로 decompile해보면 marketData의 field가 생성되어 있는 것을 확인할 수 있습니다.

private val _marketData = MutableLiveData<HomeMainState>(HomeMainState.Uninitialized)
val marketData: LiveData<HomeMainState> get() = _marketData

하지만 getter만 설정해주게되면 backing field가 생성되지 않고 getMarketData()만 생성됩니다.

private final MutableLiveData _marketData;

...

@NotNull
public final LiveData getMarketData() {
   return (LiveData)this._marketData;
}

getMarketData()에서 _marketData를 LiveData로 변환하여 가져오는 것을 볼 수 있습니다.

HomeFragment로 화면을 전환할 때마다 통신을 하는데 어떻게 network 통신 최적화를 할 수 있는가?

이는 OkHttp3의 Cache를 이용하거나 Local Dababase를 이용하여 최적화를 할 수 있습니다.

val urlIterator = cache.urls()
while (urlIterator.hasNext()) {
    if (urlIterator.next().startsWith("https://www.google.com/")) {
    	urlIterator.remove()
    }
}

위와 같이 force update가 필요한 경우에 Cache를 삭제 후 network 통신을 하고 그 외의 경우에는 local에 저장된 Cache를 불러오는 방식으로 구현이 가능합니다. [4]

혹은 remote API에서 받아온 데이터를 local database에 caching하여 필요할때 불러오는 방법도 가능합니다.

Reference

[1] "Fragment lifecycle," Android Developers, last modified Oct 27, 2021, accessed Feb 22, 2022, https://developer.android.com/guide/fragments/lifecycle#states.

[2] "Introduction to Model/View/ViewModel pattern for building WPF apps," Microsoft technical documentation, last modified n.d., accessed Feb 20, 2022, https://docs.microsoft.com/en-us/archive/blogs/johngossman/introduction-to-modelviewviewmodel-pattern-for-building-wpf-apps.

[3] "Properties," Kotlin Programming Language, last modified Jan 25, 2022, accessed Feb 20, 2022, https://kotlinlang.org/docs/properties.html#backing-fields.

[4] "Caching," OkHttp, last modified n.d., accessed Feb 22, 2022, https://square.github.io/okhttp/features/caching/#pruning-the-cache.

profile
Hola, Mundo
post-custom-banner

0개의 댓글