[팀 프로젝트] Yu Market 첫번째 코드 리뷰 1편 (1)

쓰리원·2022년 2월 2일
0

TeamProject_Yu Market

목록 보기
1/5
post-thumbnail

1. 글 작성 이전에 박카스팀 팀플 진행 과정을 간략히 설명하겠습니다.

안녕하세요 박카스 팀의 팀장 쓰리원 입니다! 우리 팀은 MVVM 디자인 패턴을 기반으로 하는 설계를 바탕으로 아이디어를 기획하였습니다. 기획 아이디어는 쿠팡과 같은 오픈 마켓에 당근 마켓과 같은 근거리 기준 지역 거래앱을 혼합한 것으로 우리 주변의 마켓과 고객을 이어주는 컨셉으로 진행하였습니다.

위와 같이 큰 컨셉을 정해놓고 구체적인 UI 와 비즈니스 로직을 구상한 뒤 MVVM 디자인 패턴으로 base를 설계하였습니다. 위 과정에서 MVVM 디자인패턴에 대한 학습을 같이 하였습니다.

팀원 공동으로 MVVM 디자인 패턴 base 설계를 완료한 이 후 비즈니스 로직을 프래그먼트 혹은 액티비티 단위로 나누어 각자의 영역분담 하게 하였습니다. 그리고 코드리뷰를 통해서 검증하는 절차를 가지게 되었습니다.

코드리뷰 방식은 코드를 작성한 1명의 팀원이 액티비티나 프래그먼트에 기능을 3~4개 정도 완성합니다. 그러면 우리는 1주일동안 그것과 관련된 모든 것에대해 모르는 것들은 질문하고 현 코드가 최선의 코드인지 코드를 작성한 팀원과 대화를 하면서 계속 수정 및 보완을 합니다.

그리고 1주일 ~ 2주일간 코드 스터디 기간 후 다음날에 원래 코드를 작성한 팀원은 자신이 개발을 맡은 부분의 수정 보완된 부분을 포함해서 발표를 진행합니다. 위와 같은 과정을 통해서 모두가 전체 코드를 숙지하고 이해하는 과정을 가지게 되었습니다.


2. 코드 리뷰를 통해 무엇을 공부하고 배웠는지 같이 알아보겠습니다.


박카스팀 첫번째 코드 리뷰를 발표를 진행한 치타개발자 팀원의 velog 입니다.

https://velog.io/@18k7102dy/team-projectcode-review1


Model의 정의와 ListAdapter 분석하기

1. 문제인식

abstract class BaseModel(
    open val id : Long,
    open val type : CellType
) {
    companion object {
        val DIFF_CALLBACK = object: DiffUtil.ItemCallback<BaseModel>() {

            override fun areItemsTheSame(oldItem: BaseModel, newItem: BaseModel): Boolean {
                    return oldItem.id == newItem.id && oldItem.type == newItem.type
            }
            
            @SuppressLint("DiffUtilEquals")
            override fun areContentsTheSame(oldItem: BaseModel, newItem: BaseModel): Boolean {
                return oldItem == newItem
            }
        }
    }
}

위와 같이 코드를 작성을 할 때, BaseModel의 id와 type 만으로 비교하여 DiffUtil를 사용하지 않는 경우에 대해서 BaseModel을 새롭게 만들어야하는 문제가 생겼습니다.

예를들어, 비교대상이 3개가 된다면

abstract class BaseModel2( //BaseModel를 새롭게 하나더 만든다.
    open val id : Long,
    open val type : CellType
    open val category : Category
)

override fun areItemsTheSame(oldItem: BaseModel2,newItem: BaseModel2)
: Boolean {
		return oldItem.id == newItem.id && oldItem.type == newItem.type 
&& oldItem.category == newItem.category
}           

위의 코드와 같이 BaseModel2를 만들어야 한다는 것 입니다. 이것은 model의 abstract class의 갯수가 계속 증가할 수밖에 없는 방법이었습니다. 그리고 BaseModel을 새롭게 만드는 경우 그에 맞는 Adpater를 새롭게 만들어야 했습니다.

예를 들어

class ModelRecyclerAdapter<M : BaseModel, VM : BaseViewModel>(
    private var modelList: List<BaseModel>,
    private val viewModel: VM,
    private val adapterListener: AdapterListener
) : ListAdapter<BaseModel, ModelViewHolder<M>>(BaseModel.DIFF_CALLBACK) {
class ModelRecyclerAdapter2<M : BaseModel2, VM : BaseViewModel>(
    private var modelList: List<BaseModel2>,
    private val viewModel: VM,
    private val adapterListener: AdapterListener
) : ListAdapter<BaseModel2, HomeModelViewHolder<M>>(BaseModel2.DIFF_CALLBACK) {

BaseModel과 BaseModel2 에 대한 어댑터를 새롭게 만들어줘야 하는 상황이 생겼다는 것 입니다. 그래서 Adpater와 BaseModel class를 하나로 통합할 수 없는가에 대한 문제 의식이 생겼습니다.

2. 문제해결

관련 코드에 대한 고민 끝에 문제에 대한 해결책을 찾았고, 그 해결책으로 나온 것이 코틀린 문법과 DiffUtil에서 제공하는 함수를 활용하는 방법이었습니다.

abstract class BaseModel(
    open val id : Long,
    open val type : CellType
) {

    open fun isTheSame(item: BaseModel) : Boolean {
        return this.id == item.id && this.type == item.type
    }

    companion object {
        val DIFF_CALLBACK = object: DiffUtil.ItemCallback<BaseModel>() {
            override fun areItemsTheSame(oldItem: BaseModel, newItem: BaseModel): Boolean {
                return oldItem.isTheSame(newItem)
            }

            @SuppressLint("DiffUtilEquals")
            override fun areContentsTheSame(oldItem: BaseModel, newItem: BaseModel): Boolean {
                return oldItem == newItem
            }
        }
    }
}

구체적으로는 위와 같은 코드 형태이며 Data 들이 각각 비교 조건이 다르다면 Data를 메서드를 이용하여 DiffUtil에서 비교하게 하였습니다.

data class CategoryModel(
    override val id: Long,
    val homeListCategory: HomeListCategory,
    override val type: CellType = CellType.HOME_ITEM_CELL
): BaseModel(id, type) {

    override fun isTheSame(item: BaseModel) =
        if (item is CategoryModel) {
            super.isTheSame(item) && this.homeListCategory == item.homeListCategory
        } else {
            false
        }

그리고 위와 같이 구현할 시에 BaseModel의 isTheSame 메서드를 Override 하게 된다면 각각의 data model에 대하여 다른 비교 조건을 적용 할 수 있었습니다.

위와 같이 쓸 수 있었던 이유가 areItemsTheSame(oldItem: BaseModel, newItem: BaseModel) : Boolean 의 생성자 파라미터의 반환형이 BaseModel 이었기 때문에 BaseModel의 객체를 반환할 수 있었고 data model들은 그 객체를 상속하여 메서드를 Override 해서 다른 조건으로 비교 할 수 있게 된 것 입니다.

3. Outro

이렇게 해서 우리는 Model 과 Adapter 관계에 대한 좀 더 깊은 공부를 할 수 있었습니다. 그리고 덕분에 Adapter의 갯수를 줄여서 중복 구현 하지 않기 때문에 좀 더 코드 재활용 성이 높은 구현이 가능해 졌습니다.

1편은 여기서 마치고 2편은 다음 블로그글에서 이어서 작성하겠습니다. 다음 팀프로젝트 블로그 글의 주제는 DiffUtil 구현에 대한 test입니다.

4. reference

https://developer.android.com/reference/kotlin/androidx/recyclerview/widget/ListAdapter

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글