안드로이드 개발팀 내 공통 기능 제공하는 방법에 대한 고민

4678 단어 android
나는 안드로이드 개발팀 내에서 공통 기능 제공을 맡고있다. 각 사업별 라이브러리 모듈이 필요로 하는 기능을 제공해야 한다.
다른 모듈에 기능을 제공하는 방법은 정말 다양하다. 내가 생각한 기능 제공 방법과, 그 중에서 AAC 뷰 모델로 기능을 제공하려다 난관에 봉착한 얘기를 해보겠다.
생각나는대로 다른 팀, 모듈에 기능을 제공하는 방법을 생각해봤다.
  • SDK:가장 거창하고, 거대하고, 손이 많이 가는 방식이라고 볼 수 있겠다. SDK라 하면 대부분 초기화 과정에서 온갖 옵션을 설정해서 SDK 클라이언트인스턴스를 만들어내고, 그 인스턴스가 제공하는 메서드들을 이용해 기능을 제공받는다. 그런데 같은 프로젝트 내부라면 SDK는 너무 거창하고 불편하겠지.
  • 용례: SDK와 반대로, 생각해볼 수 있는 가장 작은 단위의 기능 제공 형태이다. 사용자는 비수나 코인등의 DI도구를 이용해 자신의 가상 시스템등등에서 용례를 주입받아 사용하고, 용례가 제공하는 단 하나의 펑션의 결과를 이용해 원하는 결과를 받는다. 사용자 인터페이스관련 작업은 못하겠지.
  • 저장소: 예가 개별 기능을 제공한다면, 저장소는 몇개의 기능을 묶어서 제공한다. 사용자 저장소등등. 내가 가장 많이 제공하는 형태이다. DI를 통해 사용측에서 가져다 쓴다.
  • 그럼 사용자 인터페이스가 엮인 기능은 어떻게 제공할까? 커스텀 뷰 하나로 끝나는게 아니라 화면 전체를 제공해야 한다면 활용단어참조나 조각조각를 제공해야 한다. 사용하는 측에선 별촉각를 하거나, 자신의 활용단어참조에서 조각조각를 덧붙이다해서 활용한다. 활용단어참조의 경우엔 호출할 때 의 의도에 몇가지 인자를 넣어서 입맞에 맞게 동작을 바꿀 순 있지만 제약이 많고, 조각조각의 경우엔 조각조각의 함수를 호출할 순 있지만, 안드로이드 특성 상 시스템이 조각조각를 언제 재생성할 지 모르기 때문에 안전하게 만든다면 활용단어참조와 마찬가지로 조각조각의 논쟁하다를 이용하는 수준에서 커스터마이징할 수 밖에 없다.
    굉장히 복잡한 로직이 담긴 다이얼로그를 제공하게 되었는데, 어떻게 제공할 수 있을까 고민했다. 쓰는 쪽에서도 내부 로직 일부를 커스텀해야 하기 때문에 단순 의 의도나 매개 변수 패키지수준으로 대응할 수 없었다.
    그럼 뷰 모델과 조각조각를 함께 제공하면 어떨까? 라는 생각에 도달했다. 조각조각는 사용자 인터페이스를 제공하고, 뷰 모델을 통해서 기능을 제공하고, 필요한 경우 로직을 커스터마이징 한다. 뷰 모델은 사용하는 측의 활성/세션에서 생성하고, 내가 만든 조각조각는 그 뷰 모델을 그대로 사용하면 된다. 활용단어참조레벨의 뷰 모델로 만들면 가능하다.
    로직을 커스터마이징해야 한다면, 인터페이스의 구현체를 직접 넣으라고 만들면 된다. 대충 이런 식이다. 코인을 쓴다고 가정한다.
    //사용측
    class CustomerActivity {
      val featureViewModel by viewModel<FeatureViewModel> {
          parametersOf( CustomLogicImpl() )
      }
    
      onCreate() {
         featureViewModel.someLiveData.observe() {...}
    
         FeatureDialogFragment().show()
      }
    }
    
    //기능 제공측
    class FeatureDialogFragment {
      val featureViewModel by sharedViewModel<FeatureViewModel>()
    
      onCreate() {
          featureViewModel.doSomething()  
      }
    }
    
    이렇게 하니, 사용측에서 요구하는 로직 커스터마이징도 지원하고, 사용자 인터페이스도 제공할 수 있었다.
    하지만 위 사용측 코드는 자칫 잘못하면 메모리릭이 나기 쉽다. 예를 들어 위 코드를 아래와 같이 익명 내부 클래스로 짜게 되면, 액티비티 메모리릭이 날 수 있다. 그 이유는 액티비티보다 수명이 긴 뷰모델이 액티비티에 대한 참조를 들고 있기 때문이다.
    class CustomerActivity {
      val featureViewModel by viewModel<FeatureViewModel> {
          parametersOf( object: CustomLogic { ...} ) //이러면 못써!
      }
    }
    
    
    어제부터 이 문제로 고민하고 있는데, 현재 내린 결론은 그냥 저런 방식으로 기능을 제공하면 잠재적인 문제가 발생할 소지가 많으니 하지 말자이다. 만약 위와 같이 사용자 인터페이스도 제공하고, 커스터마이징 가능한 로직도 제공해야 한다면 어떻게 해야 할까? 지금 드는 생각은 그냥 추상적인 부분와 추상 뷰 모델을 제공해서, 사용측에서 알아서 커스터마이징 부분을 채워넣으라고 하는 방법 밖에 모르겠다. 라이프사이클이 얽혀들어가면 모든 문제가 백배 어려워지는 듯. ㅠㅠ
    참고로 하나의 DI컨테이너 안에서 돌아가는 경우라면 범위을 잘 조정해서 원하는 뷰 모델구현체를 조각조각에서 주입받도록 구현할 수도 있다. 코인의 경우 한정어를 사용하면 된다. 이 경우엔 뷰 모델을 요약하게 만들어서 알아서 구현부분을 채워넣으라고 하고, 이 뷰 모델을 조각조각에서 사용하면 된다. 하지만 내 프로젝트의 경우엔 한 프로젝트 내부에서 여러개의 Koin 응용 프로그램을 사용하기에, 단순한 한정어로도 해결이 되지 않아 이 방식도 쓰지 못한다. 어떻게 어떻게 해결을 한다손 치더라도 배보다 배꼽이 더 커질 것 같아 포기했다.

    좋은 웹페이지 즐겨찾기