좋은 아키텍처란?
요구사항에 맞게 변경에 있어서 용이하고, 코드를 이해하거나, 유지보수 작업, 문제가 발생하였을 때 왜 이런 결과가 나왔는지에 대해서 이해하기 쉬운 아키텍처를 의미한다.
좋은 아키텍처를 방해물 : 복잡성
복잡성은 작성된 긴 코드가 아니라 변경하거나 이해하거 어려운 소프트웨어 구조를 의미한다.
작은 변경사항이라도 많은 곳의 수정을 요구하거나, 알 수 없는 결과가 도출되었을 때 복잡성을 가진다고 표현할 수 있다.
복잡성을 높이는 요인
- 의존성 : 코드가 독립적으로 이해되고 수정될 수 없음.
- 불명확함 : 중요한 정보가 불명확할 때를 의미하며, 과정을 이해하지 못하여 결과를 예측할 수 없는 경우.
- 전술적 프로그래밍 : 빠르게 구현을 하는 경우로. 기술의 부재 및 추후 문제가 발생할 수 있다.
복잡성을 낮추는 방법
- 추상화 : 불필요한 정보를 감추는 방법으로, 모듈을 깊게, 인터페이스는 넓게 설계하여 구현할 수 있다.
- 깊은 모듈 : 모듈은 무언가를 구현하고 인터페이스를 제공하는 것을 의미한다. 추상화를 구현할 수 있는 방법으로 모듈을 깊게 설계하면 사용자 입장에서 인터페이스의 용도만 알고도 사용할 수 있다.
- 범용 인터페이스 : 다른 곳에 재사용이 가능하게 세부적인 요소를 감춘 인터페이스를 의미한다. 재사용을 목적으로 한 곳에서만 사용하는 것을 지양한다.
- 추상화의 경계 : 깔끔한 추상화를 위해서 합치거나 나누는 경우를 명확히 파악해야 한다.
- 합치는 경우는 사용되는 정보가 공유되거나 코드가 중복되는 경우, 인터페이스를 단순하게 만들어주는 경우를 의미한다.
- 나누는 경우는 특정 목적을 위한 API가 범용 클래스 안에 있는 경우, 다른 종류의 범용 메커니즘이 함께 있을 때 나눈다.
- 재사용성 : 독립적인 요소로 타 모듈에서 참조할 수 있음을 의미하며, 단순하게 코드중복을 줄이는 것이 아닌 사용성을 개선시키는 것을 의미한다.
클린 아키텍처
소프트웨어 아키텍처는 선을 긋는 기술이며, 이는 경계라고 한다. 경계는 소프트웨어 요소를 분리하고 경계 한편에 있는 요소가 반대편의 요소를 알지 못하도록 막는다.
클린 아키텍처는 명확한 형체로 존재하는 것이 아닌 지속적인 합의로 만들어진 개념으로, 관심사의 분리를 위해 경계를 정의한 아키텍처이다.
경계선의 구분하는 기준은 아래와 같다.
- 단일 책임 원칙 : 하나의 모듈은 하나의 액터에 대해서만 책임을 져야 한다.
- 공통 폐쇄 원칙 : 단일 책임 원칙과 개방 폐쇄 원칙을 융합한 개념으로, 각 계층은 하나의 큰 액터(이용자, 로직 실행자...)만을 가지며, 가장 높은 수준의 계층은 하위의 계층의 변화로 부터 보호받아야 하는 것을 의미한다.
클린 아키텍처 다이어그램
엔티티(노랑색)
- 앤터프라이즈 업무 규칙을 캡슐화한다.
- 모든 플랫폼 어플리케이션에서 재사용이 가능해야 한다.
- 외부로부터 영향을 받지 않는 영역이다.
유스 케이스(주황색)
- 어플리케이션에 특화된 업무 규칙을 캡슐화 한다.
- 시스템에 모든 유스 케이스를 캡슐화하고 구현한다.
- 엔티티로 들어오고 나가는 데이터의 흐름을 조정하며, 엔티티가 자신의 업무 규칙을 사용하여 유스 케이스의 목적을 달성하도록 한다.
인터페이스 어댑터(초록색)
- 일련의 어댑터로 구성한다.
- 인터페이스와 유스 케이스, 엔티티에서 주고 받는 데이터를 각자 편하는 방식에 맞게 변환한다.
- MVP, MVVM과 같은 아키텍처(ViewModel, Presenter)가 속한다.
프레임워크와 드라이버(파랑색) :
- 일반적인 안드로이드 프레임워크, 데이터베이스, 웹 서버가 해당된다.
계층간 의존성
- 화살표의 방향은 의존성을 의미하며, 해당 아키텍처에서 의존성은 밖에서 안으로 향하며, 바깥의 원은 안쪽 원에 영향을 미치지 않는다. 경계의 안쪽으로 갈 수록 높은 수준(추상화)으로 보호되는 계층이며, 바깥으로 갈 수록 세부적인 영역으로 표현된다.
안드로이드 아키텍처
위의 다이어그램을 기준으로 안드로이드에 적용한 사례를 잘 나타내서 NHN포스트에서 가져왔다.
프레젠테이션 계층
- 화면의 표시, 에니메이션, 사용자 입력처리 등 UI와 관련된 처리를 담당한다.
- 자주 변경되는 계층이며, 다른 계층에서 해당 계층을 의존적으로 구현해서는 안된다.
- View는 Context를 통해서 시스템에 접근하며, Context에 의존성을 가지며, ViewModel이 Context를 가지지 않도록 주의 해야한다.(ViewModel이 Context를 가지면 둘의 경계가 없어짐.)
- 프레젠터(Presenter, ViewModel)은 View 관점에서 비즈니스 로직을 담당한다.
도메인 계층
- 가장 최상위 계층으로, 모든 비즈니스 로직을 담고 있는 계층이다.
- 모든 계층이 도메인 계층을 의존하며, 도메인 계층은 아무 계층을 의존하지 않는다.
- 유스 케이스는 개발에 대한 깊은 지식이 없는 사용자, 기획자가 봐도 알수 있는 로직을 담고 있다.
- 엔티티는 어플리케이션의 논리적인 엔티티 데이터이다.
- 도메인 계층의 분리를 통해서 비즈니스 로직을 격리하고 팀원들이 비즈니스 로직을 확인하기 쉽도록 구성한다.
데이터계층
- 레포지토리는 데이터 계층의 인터페이스이며, 안에 데이터 처리를 완전히 감춰주는 기능을 제공한다. 또한 유스케이스가 필요로 하는 데이터의 저장 및 수정의 기능을 제공한다. 데이터 소스를 인터페이스 형태로 참조하기에, 레포지토리에서 데이터 소스 객체를 갈아끼우는 형태로, 외부 API, 로컬DB, Mock 데이터 출력을 자유롭게 전환이 가능하다.
- 데이터 소스는 실제 데이터의 입출력이 실행되는 곳이다.
- 엔티티는 데이터 소스에서 사용되는 데이터를 정의한 모델로, Android에서 JSON, DataClass가 일반적이다.
- 재미있는 점은 레포지토리 구현체는 데이터 계층에, 레포지토리 인터페이스는 도메인 계층에서 관리하는데, 이는 만약 레포지토리에서 데이터 소스를 갈아끼울 때 도메인 계층에 영향이 없기에 비즈니스 로직이 안전하기 때문이다.
데이터의 흐름
사용자의 입력이 들어오면, View -> Presenter -> UseCase -> Entity -> Repository -> DataSource로 흐름이 발생한다.
도메인 계층에서의 엔티티가 다이어그램에서의 엔티티이며, 데이터 계층의 엔티티는 DTO, DAO를 의미한다.
해당 흐름으로 따지면 도메인이 하위 계층을 참조할 수 있는 상황이 발생하는데, 이럴 때 의존성의 역전을 이용한다.
의존성 역전 원칙은 안드로이드에서 인터페이스를 만들고, 인터페이스를 참조하는 방식으로 사용할 수 있다.
내 생각
아키텍처 패턴을 저번 학기부터 하고 있는 프로젝트에 도입했다. 계속 공부를 하고 있었지만, 실제 적용하는데 있어서 어떻게 구성해야 할지 막막하기도 해서 다른 사람이 아키텍처를 적용한 프로젝트의 코드와 유튜브의 클론 코딩(나의 사랑 필립,,)을 보면서 이해했었다. 적용하면서 느낀점은 많은 패키지와 파일을 생성했어야 했다.(같은 팀원 형님이 이 패키지들을 보고 '늪'이라고 표현할 정도,,) 해당 패키지들을 구성하는데도 많은 시간이 소요되고, 관리하기도 힘들었다. 하지만 아키텍처를 적용하면서 좋았던 점은 기능을 확장할 때나 수정할 때 였다.어디에 어떤 것을 넣어야 할지 정해져 있어서 별다른 문제 없이 확장과 수정이 가능했다. 확장과 수정이 편하다는 느낌이 약간 방 청소에 비유하자면, 방 한 구석에 쌓아놓고 "오늘 청소 끝"을 외치는 것보다 장롱과 서랍을 만들어 장롱에는 외투를, 서랍마다 넣을 것들을 정해놓고 분류하는 느낌으로 편했다.
참고 자료
https://meetup.nhncloud.com/posts/345
[Android] 요즘 핫한 Clean Architecture 왜 쓰는 거야? : NHN Cloud Meetup
[Android] 요즘 핫한 Clean Architecture 왜 쓰는 거야?
meetup.nhncloud.com
https://www.charlezz.com/?p=45391
안드로이드에 클린 아키텍처를 도입한다면 어떻게 될까? | 찰스의 안드로이드
안드로이드와 클린 아키텍처 안드로이드 앱 아키텍처에 관심을 갖게 되면 필히 접하는 키워드, 클린 아키텍처에 대해서 알아보려고 한다. 클린 아키텍처 "If you want to go fast, if you want to get done qui
www.charlezz.com