안드로이드/앱개발(Android)

Clean Architecture) MemoApp만들기 - 1

김염인 2022. 8. 8. 18:19

 

 


1-1. 클린 아키텍처란?

클린 아키텍처

클린 아키텍처의 개념은 우리가 잘 알고 있는 Uncle Bob인 로버트.C 마틴의 Clean Architecture에서 시작되었습니다. 

 

 

1-2. 클린 아키텍처의 구조 및 특징

 

 

Dependecy Rule

하위 계층으로 갈수록 상위 계층을 몰라야 합니다. 내부 원의 어떤것도 외부 원의 어떤것에 대해 전혀 알 수 없습니다.

특히, 외부 원에 선언된 이름(class, function, variable 등)은 내부 원에 있는 코드에서 언급되지 않아야 합니다. 

 

CA의 각 계층

  • Entities : 비지니스 규칙(business rule) (예: 근무시간에 따라 급여를 계산하는 공식, 직원에 대한 가장 기본적인 데이터가 들어있는 POJO)
  • Use cases : 단순히 실행 가능한 작업. Intereactor라고도 함. android에서는 UI와 상호작용하여 Repository에서 데이터를 꺼내온다. getRandomFactUseCase등 이름으로 기능을 알수 있어야 한다.
  • Presenters(Interface Adapters) : 데이터를 Entity, UseCase의 편리한 형식에서 데이터베이스 및 웹에 적용 할 수 있는 형식으로 변환한다. MVP의 Presenter, MVVM의 ViewModel으로, UI에서만 사용된다.
  • Frameworks & Drivers  : 데이터베이스나 Web Framework, UI등

 

 

안드로이드 클린아키텍처 구조

 

총 3개의 계층이 존재하게 된다, Data -> Domain -> Presentation 각 레이어는 의존성 주입을 역으로 해주는 구조를 가지고 있다. 그리고 꼭 한방향만 지향한다

  • presentation layer : data를 화면에 표시하고 user 상호작용을 다룸. UI, Presenter, ViewModel등.
  • domain layer : 비지니스 프로세스와 관련된 가장 핵심 계층. 어떤 다른 계층에도 독립적임. Entities, Use Case, Repository Interface등.
  • data layer : 어플리케이션의 데이터 관리(네트워크에서 데이터 검색, 데이터 캐시 관리 등). Repository Implementation, Local & Remote Data source등.

 

핵심은 (dependency rule에 따르면)domain 계층이 다른 계층에 독립적이라는 것입니다.

domain 계층에서는 다른 외부 계층에서 정의된 클래스에 접근하지 않아야 합니다.

이상적으로는, 도메인 계층은 라이브러리 및 프레임 워크와 독립적이여야 합니다.

Kotlin Standard Library나 몇가지 DI(Dependecy Injection) library는 괜찮지만, 다른 라이브러리나 framework (특히 Android Framework)는 피해야 합니다.

 

 

2-1. 기초 구현

Memo ToDoApp을 만들기 위해 Clean Architecture를 지향하여 만들었다.

 

 

 

우선 계층 3개의 Package를 설정하여 각 레이어에 맞는 구축을 해주어야 한다.

 

- Data에는 Entity Model과 RoomDatabase를 구축하여 Repository화 해준다

 

- ViewHolderList를 구축하여 RecyclerView 형태로 아래와 같이 보이도록 구현을 해주었다.

 

 

viewholder_item.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/cv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:animateLayoutChanges="true"
    app:cardCornerRadius="20dp"
    app:cardElevation="4dp"
    app:cardUseCompatPadding="true">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:orientation="vertical">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="6dp">
            <CheckBox
                android:id="@+id/checkbox"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_gravity="center"
                android:paddingLeft="5dp"
                android:layout_alignParentLeft="true" />
            <LinearLayout
                android:id="@+id/linearLayout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_marginStart="4dp"
                android:layout_marginTop="2dp"
                android:layout_toEndOf="@+id/checkbox"
                android:orientation="vertical"
                android:layout_marginLeft="4dp"
                android:layout_toRightOf="@+id/checkbox">
                <TextView
                    android:id="@+id/titleTextView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="2dp"
                    android:text="Codelia"
                    android:maxLines="5"
                    tools:text="Title"
                    android:textColor="@android:color/black"
                    android:textSize="24sp"
                    android:textStyle="bold"/>
                <TextView
                    android:id="@+id/cityTextView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    tools:text="Descript"
                    android:textSize="18sp"/>
            </LinearLayout>

            <Button
                android:id="@+id/statusTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginEnd="6dp"
                android:layout_marginRight="6dp"
                android:text="Delete"
                android:textSize="20sp"
                android:textStyle="bold"
                android:background="@drawable/button_stroke"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"/>

        </RelativeLayout>
    </LinearLayout>

</androidx.cardview.widget.CardView>

 

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:text="List"
        android:textColor="@android:color/black"
        android:textSize="36sp"
        android:textStyle="bold"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/listRecyclerView"
        tools:listitem="@layout/viewholder_memo_item"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="1dp"
        android:layout_marginLeft="1dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="1dp"
        android:layout_marginRight="1dp"
        android:layout_marginBottom="1dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/titleTextView" />
</androidx.constraintlayout.widget.ConstraintLayout>

 


Clean Architecture 형식의 구현을 하기위에 가장 기초적인 View화 RoomDB구조를 구현해주었다.

다음은 Domain Usecase설정과 Recyclerview를 설정해주는 adapter구현, presentation의 Viewmodel을 통한 LiveData를 Observing하는 구조를 구현해보아야겠다.