[Android] 클린 아키텍처 따라잡기 1편

sundays·2023년 4월 6일
0

cleanArchitecture

목록 보기
1/6

저는 개인프로젝트를 혼자 진행하면서 클린 아키텍처를 드디어 도입하게 되었습니다. 아무래도 읽으시는 분들과 함께 리팩토링을 하게 되지 않을까 생각합니다. 개인적으로 공부하면서 왜 클린 아키텍처 인가 ? 특히 어디까지 기능을 추상화 해야 하는가 ? 에 대해서는 정말 많은 생각이 있었습니다.

솔직히 저는 추상화를 많이 한다고 개발을 잘한다고 생각하진 않습니다. 저는 한눈에 들어오는 개념을 정말 좋아하는데 추상화가 너무 많이 되면 한눈에 비지니스 로직을 파악하기가 힘든 단점도 있습니다.

저의 의문이었던 것

  • 왜 클린 아키텍처 패턴 이어야 하나요?
  • 그럼 어디까지(어떻게) 추상화를 해야하나요?

왜 사용해야 할까요

추상화를 함으로써 얻는 이점이 꽤 큽니다. 만약 DB를 Sqlite를 쓰다가 Room으로 변경하였는데 일선상의 이유로 갑자기 realm을 쓰게 될 수도있습니다. 이 모든 API들이 사용법이 전부다르지만 같은 기능을 쓰게 되는데도 불구하고 모든 비지니스 로직을 변경해야한다면 많은 인력이 필요하게 될지도 모릅니다. 그렇기 때문에 비지니스 로직들을 정규화하는 작업이 필요한데 이것을 클린 아키텍처 패턴을 적용하게 되면 DB가 어떤것으로 변경되던간에 상관없이 적용할 수 있습니다

소프트웨어 아키텍처는 선을 긋는 기술이며, 나는 이러한 선을 경계(boundary)라고 부른다.
경계는 소프트웨어 요소를 서로 분리하고, 경계 한편에 있는 요소가 반대편에 있는 요소을 알지 못하도록 막는다. - Robert C. Martin, Clean Architecture

이것이 가능하게 되는 이유는 의존성을 제거(경계를 긋는 기술) 하기 때문입니다. 그럼 언제 의존성을 주입하게 되나요? 그것은 자식이(Child) 선언되는 시점에 적용될 것입니다. 부모-자식 관계의 상속 여부에 따라서 어떠한 기능은 부모에서 오버라이드 할 수 있기 때문입니다.

의존성 주입의 예시

open class Bird(var name: String, var wing: Int, var beak: String) {
    open fun fly() {
        println("Fly")
    }
}

class Lark(name: String, wing: Int, beak: String) : Bird(name, wing, beak) {
    override fun fly() {
        println("quick fly")
    }

    fun singHightone() {
        println("sing hightone")
    }
}

fun main() {
	// 이 시점에서 의존성이 주입됩니다.
    val lark : Bird = Lark("lark", 2, "short")

    //lark.singHightone() // 에러 발생
    lark.fly() // quick fly
}

어떻게 추상화 해야 하나요

안드로이드 클린 아키텍처 에서는 의존성 주입이 되는 시점에 따라 계층을 나누어 놓았습니다.
화살표 방향으로 의존성을 추가 하기 때문에 엔티티가 전체 구조에 있어서 가장 추상화 된 부분이라고 볼 수 있습니다.

프레젠테이션 계층

View

  • 플랫폼 의존적인 구현
  • 화면 UI 와 사용자 입력을 담당
  • 단순히 프레전터가 명령 하는 일만 수행하도록 합니다.

Presenter

  • MVVM의 ViewModel과 같이 어떤 반응을 해야하는지에 대한 판단을 하는 영역입니다.
  • 무엇을 그려야 할지도 알고 있습니다.

도메인 계층

UseCase

  • 비지니스 로직이 들어 있는 영역입니다.

Entity

  • 앱의 실질 데이터 입니다

데이터 계층

Repository

  • UseCase에서 필요한 데이터 저장 수정 기능을 제공합니다
  • 데이터 소스를 인터페이스로 참조 합니다
  • Local DB 및 네트워크 통신을 담당합니다

DataSource

  • 실제 데이터 입출력을 담당합니다.

프로젝트 구조


해당 구조 처럼 package를 나누어서 구성하였습니다. 위에 구성에서 빠진 내용중에는
common은 공통 유틸리티 들이 들어가게 될것이며
di는 실제로 repository에 inject할때 필요한 singleton provider를 추가 할 것입니다.

결론

인기없는 블로그라 다행인게 있다면 좀 느긋하고 오탈자가 생겨도 조금 덜 쪽팔리다는 것 같습니다. 헛소리를 적어놓았더라도 뭔가 좀 덜 부담감이 생긴달까요. 그래도 제가 잘못알고 있는게 있을수 있으니 많은 참견이 있으면 좋겠습니다.
다음 코드 부터는 실제 코드들이랑 함께 비교해서 작성하겠습니다.

Reference

profile
develop life

0개의 댓글