안녕하세요. 리멤버 안드로이드 개발자 김평호 입니다.
리멤버 안드로이드 코드는 클린아키텍처 + MVP 로 되어 있습니다. 하지만 2016년도의 부터 이어진 코드이다 보니 이전에 작성된 코드와 현재에 작성된 코드가 다른 형태를 보이고 있는 경우가 발생 하고 있었습니다. 새롭게 작성 되는 코드는 PR 을 통해서 점점 비슷한 구조로 작성 되고 있지만 더 명확하게 가는게 좋겠다고 생각하여 구조 개선 여정을 시작 했습니다. 구조개선을 위해서 당시 고민 했던 내용을 공유과 어떤 방법으로 진행 한지를 공유하기 하기 위한 내용의 게시글 입니다. 해당 게시글은 아래의 내용들이 있습니다.
- 왜 구조 개선을 생각 하게 되었는지에 대한 고민
- 구조 개선은 어떻게 진행 했는지에 대한 내용
왜 우리는 매주 2시간을 함께 연구 하게 되었는가?
인내하며 타협하며 개발
타협 :
- 코드는 클린아키텍처 + MVP 로 개발 되어 있었습니다. 구조 와 패턴이 잘 구현 되고 있고 온보딩 기간의 학습으로 코드에 적응하고 개발하는데는 큰 불편함은 없습니다. 코드 리뷰도 기존의 구조 단단하게 하고 유지하는 방향을 가지고 진행 하였습니다. 코드에 적응이 된거라 생각하게 되었고 그 적응 잘못되었다고 생각 하진 않고 있었습니다. 유지보수가 어렵지 않고 새로운 기능 개발하는 속도도 문제 없었습니다.
인내:
- 학습에 필요한 샘플을 보면 거의 대부분이 MVVM 으로 작성 되어 있어 과거(MVP)와 현재(MVVM) 라고 생각 될때가 많았습니다. 파트원 모두 MVVM 으로 과제를 진행 해 주셔서 개념과 사용에 대해서 알고 있지만 장시간 MVP 코드를 보다 보니 MVVM 을 유니콘 처럼 생각 하는 느낌을 많이 받게 되었습니다.
- 파트시간에 안드로이드 기술에 대한 다양한 학습 시간을 만들면서 문제가 있다고 생각을 전환 하였습니다. 파트에서는 매주 2시간정도의 시간을 이용해서 새로운 기술 또는 기존에 사용하는 기술을 깊게 학습하고 있습니다. 새로운 기술은 이론 학습, 샘플 생성, 리멤버 코드에 적용후 장단점 파악 하는 형태로 진행 하고 있습니다. 리멤버 코드에 학습한 기술이 적용되지 않고 학습으로 끝나는 경우가 발생하기 시작 했습니다.
- Dagger 사용 에 대한 어색함은 처음 부터 있었지만 사용 하다 보면 편하다는 이유로 같은 방식으로 사용하고 있습니다. Dagger 처음 학습 할때와 실제 리멤버 코드에서 사용은 다르게 보입니다.
- 요구 조건 변화에 유연하게 대응하고 재 사용을 늘리기 위해서 클린 아키텍처와 MVP 는 발전 시키며 개발을 진행 하고 있습니다.
결론
- 우리는 모두 클린아키텍처 + MVVM + Hilt 를 사용하고 싶다. 기술 부채를 계속 쌓아가지 않고 새롭게 추가되는 화면이라도 원하는 방향으로 개발하고 싶다 였습니다.
그래서 구조 개선을 결정
- 구조 개선을 결정하고 많은 고민이 시작 되었습니다. 파트원은 모두 각각의 목적 조직에서 업무를 진행하고 있어서 해당 일을 전담 할 수 있는 인력이 없었습니다. 만약 전담할 인력이 있다고 하더라도 혼자서는 너무 힘든 일이라는 생각이 들었습니다. 여러사람의 의견을 모아서 작업을 해야 할거고 많은 의견 충돌이 있을거라 예상하고 있었기 때문입니다.게다가 우리가 생각 하는 클린아키텍처+ MVVM 은 동일한 내용이 맞을까도 생각 하게 되었습니다. 수많은 생각을 하다가 문득 같은 “팀” 이라는게 떠 올랐습니다. 한개의 repository 에서 함께 개발하는 구성원들이라는 생각이 들었고 “함께” 라는 용어가 생각 이 났습니다. 단순한 단어가 많은 생각을 정리 할 수 있게 도와 주었습니다.
“함께 하는 여정”
- 우리는 같은 파트에서 함께 개발하는 동료라고 생각하고 모두가 만족하는 코드를 향해 간다고 생각하니 결론에 쉽게 도달 할 수 있었습니다.
- 파트원 모두는 아름다운 코드를 만들기 위해 노력 하고 있지만 각자의 생각이 다르기 때문에 파편 화가 되어 간다. 그러면 최종적으로 생각하는 코드를 협의해서 함께 만들어 가면 되겠다는 결론을 내렸습니다.
- 협의하는 시간을 강제적으로 매주 만들어서 이야기하면 해결이 될거 같았습니다.
- 레거시 처리는 앞으로 작성하는 코드에 대한 대비, 그리고 정말 레거시 코드에 대한 리팩토링이 있다고 생각합니다. 저는 구 버전에 있는 레거시 코드보다 현재 작성하고 있는 코드에 대한 내용을 만들어 가는게 중요하다고 생각해서 우리 모두가 만족 하는 구조를 함께 만들어 보기로 했습니다.
- 회의 시간도 딱딱 하지 않게 아름다운 코드를 위한 대화의 시간으로 “뷰티톡“ 으로 시간을 정하고 2021년12월 부터 일정을 시작 했습니다.
중요: 함께 발전 시킬 수 있습니다. 안드로이드 개발자 채용 중입니다. Android 개발자
“함께 시작 된 뷰티톡 6개월의 시간”
- 시작
- 어떻게 시작 할지 고민이 많았습니다. 시작을 잘 풀어야 앞으로 회의 시간이 일방 적이지 않고 다양한 의견을 함께 나누는 시간이 될거라 예상 했습니다.
- 처음은 역시 서로의 생각을이야기 할 수 있게 각자가 생각 하는 “클린아키텍처“의 구조를 발표 하는 시간으로 정했습니다. 파트원 4명 모두가 자신이 생각하는 구조를 그려오고 간단하게 설명하고 질문을 받는 시간으로 가졌습니다. 4명 모두가 발표가 끝나고 “클린아키텍처“에 대해 논의를 하면서 다른 부분을 이야기 하며 다른 부분을 이해하고 통일화 하는 시간으로 일정을 시작 했습니다.
- 진행
- 통일된 “클린아키텍처” 개념을 모두가 동일한 내용으로 맞추니 다음으로 할일은 실제 패키지, 모듈 등을 어떻게 할지에 대한 내용을 찾아서 하나씩 진행하게 되었습니다.
- 이상적인 내용으로 dummy 코드를 생성하는것 보다 현재 동작 하고 있는 화면을 우리가 생각하는 구조로 만들기로 결정 했습니다.
- 내용 :
- 모듈 분리
- DataBinding 추가 및 MVVM 사용
- UseCase 사용
- Dagger -> Hilt 로 변경
- Rx 버전 업데이트 및 사용 방법
- 응답 객체와 Entity 객체 분리
- 신규 화면 생성시 해당 구조 사용을 권장
- 결과 :
- 구조와 샘플 까지 작성하는데 6개월 이라는 시간이 걸렸지만 행복한 시간이었습니다. 서로의 의견을 조율하고 이야기 하는 시간 모두가 좋았습니다. 운용 되고 있는 코드에서 통합된 구조를 만들다 보니 다양한 상황에 직면 하게 되었지만 함께 해결해 나가고 최종 화면 보일때 파트의 승리라 생각 했습니다.
- 현재 우리가 만족 하는 클린아키텍처 의 MVVM 코드를 사용 할 것이고 이어 나갈 것이다. 함께 하는 시간의 힘이고 노력이다. 다같이 협의한 내용이고 만족 있습니다. 설계는 정답이 없다고 생각하기에 기본적인 구조에 맞게, 회사 상황에 맞게, 변형하고 재 조립 이라고 생각합니다. 그래서 우리만의 설계를 만들어 가고 있고 앞으로도 계속 발전 시킬 예정입니다.
우리와 함께할 파트원을 채용 중입니다. Android 개발자
다음편: 안드로이드 파트에서 진행한 구조를 적용하기
여기서 잠깐
잠시 가장 적용 하고 싶었던 것중 하나인 MVVM 도입을 결정하게 된 내용을 조금 이야기하고 넘어가 보겠습니다. 레거시 리멤버의 코드의 구조에 대한 내용을 포함 하고 싶어서 입니다.
패턴 사용의 파편 화
- 같은 구조를 목표로 개발 하고 있지만 각각의 이해도가 다르고 사용하면서 조금 씩 발전하고 있어서 파편 화가 진행이 되고 있습니다. 구조에 대한 파편 화는 초기 MVP, 향상 된 MVP, UseCase 사용의 추가등 조금씩 진행 되었고 시간이 지남에 따라 차이가 커지고 있습니다. 아래 내용은 점차 파편화 되어가는 코드를 개략적으로 보여주는 내용입니다.
- 초기 MVP:
- 코드중 일부가 아래 처럼 사용자가 동작을 하면 presenter.updateName 호출하고 presenter 에서는 API 사용을 위한 비동기 처리와 간단한 예외만 처리 하고 있었습니다. (2016년도 정도의 코드 형태이고 이해를 돕기 위해 작성된 내용입니다.
// activity 코드
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCommunityBinding.inflate(layoutInflater)
setContentView(binding.root)
initCommunity()
presenter.loadCommunityList(argument.communityId)
}
private fun initCommunity() {
this.communityId = communityId
binding.appBar.title = R.string.community_title
binding.communityName.setOnClickListener {
...
}
}
// presenter 코드
fun loadCommunityList( communityId: Long) {
//api call
}
- 향상된 MVP
- kotlin 으로 작성하는 시기의 MVP 코드의 형태가 변화 하였습니다.
- activity, fragment 에서 발생하는 모든 이벤트(사용자 액션 포함 )는 presenter 로 전달하고 모든 비즈니스 로직은 presenter 에서 처리 하는 개념으로 처리하고 있습니다. presenter 에서는 ‘view.setCommunityName(“커뮤니티 이름”)’ 처럼 각각의 view 하나 하나를 설정 하도록 개발 하고 있습니다.
// activity 코드
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCommunityBinding.inflate(layoutInflater)
setContentView(binding.root)
val argument: Argument = intent.getParcelableExtra(COMMUNITY_ACTIVITY_INTENT_KEY) ?: let {
finish()
return
}
presenter.onCreate(
argument.communityId,
argument.community,
argument.boardId
)
}
// presenter 코드
fun onCreate(
communityId: Long,
community: Community?,
boardId: Long?
) {
this.communityId = communityId
view.setAppbarTitle(R.string.community_title)
view.setCommunityNameClickListener {
view.goToCommunity(communityId)
}
view.setCommunityName("커뮤니티 이름")
loadCommunityList(communityId)
...
}
fun onClickFromCommunityName() {
view.goToCommunity()
}
- 클린아키텍처의 UseCase 사용
- repository 의 helper 를 만들어서 useCase 처럼 사용 했지만 새롭게 입사하신 분들은 생소하게 보일 수 있습니다.
- useCase 도입을 하기로 결정 했지만 파트에서 생각하는 useCase 내용이 다르고 구현하는 방법도 다양하게 되었습니다.
- 결국 맞춰 가는 시간의 부족
- PR을 통해서 코딩컨벤션을 맞추고 이야기를 했지만 아쉬운 느낌이 많았습니다. PR 전에 구조가 잡혀 있었다면, 컨벤션이 잘 정의 되어 있었다면 이런 생각이 많이 졌습니다.
- 드라마앤컴퍼니 조직 구조가 미션 조직 형태의 “Crew” 로 분리 되어 있다 보니 파트가 함께 하는 시간이 부족하여 구조 개선이라는 큰 방향에 대한 처리는 어떻게 해야 할 지 방법을 찾지 못 했습니다.
- 그래서 찾은 결론이 일주일에 최소 2시간 씩이라도 함게 하는시간을 만들어 발전 시키는 방향을 생각 하게 되었습니다.