소프트웨어 아키텍처
소프트웨어 시스템을 추론하기 위해 필요한 구조들의 모임이며, 그러한 시스템과 구조를 만드는데 필요한 규율이다.
소프트웨어 시스템
- 클래스
- 함수..
- 인터페이스
- 함수..
- 등등....
좋은 아키텍처를 만드는법 - 함수, 클래스, 컴포넌트
소프트웨어 본연의 목표를 충족
- 기능적: 원하는 기능을 제공
- 구조적: 기능의 변화에 따라 유연하게 변경되어 제공
좋은 아키텍처는 소프트웨어의 목표를 최소한의 비용으로 가능하도록 하는 구성요소와 규칙의 모임 (Uncle bob https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
중요성
현대 소프트웨어는 지속적으로 변화가 필요함 (요구사항의 증대... 변경사항....)
변경이 필요할 때, 아키텍처가 고려되어 있지 않다면 최악의 경우 새로 만드는게 나을 수도 있음
아키텍처는 결국 구성요소와 생성 방법의 모임
크게 3가지를 볼 수 있음 이것들을 변경하기 쉽게 구성하면 ... 좋은 아키텍처라고 할 수 있음
- 함수: 기능의 최소 단위
- 클래스: 데이터와 함수의 모임
- 컴포넌트: 클래스의 모임
함수
일반적으로는 부분적인 코드 기법에 대해서는 아키텍처 이야기에 포함하지 않음
하지만 기초가 튼튼해야 이들로 쌓아올린 고수준의 관계가 튼튼할 수밖에 없음
좋은 코드에 대한 여러 이론을 공부하길 권장 -- Clean Code
클래스
SOLID 패턴 목표: 변경에 유연해짐, 이해하기 쉬워짐, 재사용이 쉬워짐
- S : Single Responsibility Principle / 단일 책임 원칙
- O : Open-Closed Principle / 개방-폐쇄 원칙
- L : Liskov Substitution Principle / 리스코프 치환 원칙
- I : Interface Segregation Principle / 인터페이스 분리 원칙
- D : Dependency Inversion Principle / 의존성 역전 원칙
S - 단일 책임 원칙
- 모든 클래스는 변경에 대해 오직 하나의 이유를 가져야함
- 클래스는 다양한 관점의 기능을 모으면 안됨
- 상호작용을 하는 대상에 따라 분류해서 구성해야함
- 상호작용을 하는 대상의 요구조건이 바뀔 때에만 클래스가 변경되도록 구성하는게 주 목적
- 중요한 것은 조직이 커뮤니케이션 하는 단위를 잘 생각해서 구성해야함.. (실무..?)
O - 개방 폐쇄 원칙
- 확장에는 열려있어야하고, 변경에는 닫혀 있어야 한다.
- 확장 열려 있음 -> 개체가 수행하는 기능은 확장이 가능 (함수 단위)
- 변경에 닫혀 있음 -> 개체 자체는 변경이 안되어야
- 일반적으로 추상화를 통해 이루어짐
- 구현체가 추상 클래스, 인터페이스를 모르는 것이 포인트
- 변경을 최소화하기 위해서는 구현체에 대한 직접 참조는 피해야
L - 리스코프 치환 원칙
- 하위 클래스 객체는 상위 클래스 객체의 역할을 그대로 수행할 수 있어야 한다.
- 부모 자식 클래스를 설계할 때 부모의 행위가 모든 하위 클래스에서 동일하게 작동하는지 고민이 필요..
- 문제가 될 경우, 부모 클래스의 일부를 나눠서 중간 클래스를 만들어 구분하거나, 따로 인터페이스로 기능을 분리하는 등의 방식 사
I - 인터페이스 분리 원칙
- 클라이언트가 자신이 이용하지 않는 메소드에는 의존하기 않아야한다.
- 의존한다는 말은, 의존하는 것에 영향을 받을 수 있다는 뜻 (의존 대상이 변경 되었을 때, 나도 바뀔 수 있다...)
- 자신의 의존 범위를 최소화 해서, 외부 변경으로부터 보호하는
D - 의존성 역전 원칙
- 상위 계층이 하위 계층에 의존하는 전통적인 의존관계를 반전시켜 하위 계층이 상위 계층을 의존하게 하는 방법
- (A가 B를 의존하고 있던것을 B가 A를 의존하도록 바꾸는 것)
- -> 의존성이란 결국 내가 해당 대상의 변경에 영향을 받겠다는 의미. 의존성이 역전되면 변경에 영향을 받는 방향이 반대로 됨. 이를 이용해서 변경이 많은 부분이 변경이 적은 부분을 의존하도록 반전시키는 방법
- 추상화를 통해 구현 가능
- 변동성을 기준으로 해서, 변동성이 큰 부분이 변동성이 작은 부분을 의존하도록 설계
컴포넌트
컴포넌트는 배포단위이다 --> 한 번에 배포가 가능한 클래스들의 그룹
이를 따르면 안드로이드에서 컴포넌트는 Gradle 모듈을 의미
개념적으로 필요에 따라 코드를 정리해도 패키지를 구분하는 정도도 충분하 할 수 있음
중요한 것은 어떻게 컴포넌트를 정의하고 나눠야 하는지에 대한 부분
응집도를 높이고 결합도를 낮춰라
응집도: 컴포넌트 안 클래스, 함수, 데이터가 얼마나 밀접한가(연관되어 있는가?)
응집도 높이기 -> 밀접하게 연관되도록 설계
결합도: 컴포넌트가 다른 컴포넌트에 얼마나 의존하는가
결합도 낮추기 -> 컴포넌트가 다른 의존하는 부분을 최소화하도록 설
Clean Architecture
아키턱체의 패턴화 및 역사
- 수 십년간 함수, 클래스들을 어떻게 구성해야 하는지 논의와 패턴화에 대한 시도가 있었음
- 그 중 대표적으로 패턴화된 아키텍처
- Hexagonal Architecture
- DCI(Data, Context and Interaction)
- BCE(Boundary Control Entity)
- 이들은 공통적으로 목표하는 것은 관심사를 분리하는 것
좋은 아키텍처들이 갖고 있던 공통 특성
- 프레임워크 독립성 : 프레임워크에 API에 의존하지 않는 구조
- 테스트 용이성 : 각 계층은 서로 분리되어서 테스트가 가능함
- UI 독립성 : 시스템 UI에 의존하지 않는 구조
- 데이터베이스 독립성 : 데이터베이스에 의존하지 않는 구조
- 모든 외부 에이전시에 대한 독립성 : 외부 세계에 의존하지 않는 구조
클린 아키텍처
포인트
- 의존성은 안으로 흐름
- 각 계층에 대한 이해
- Entities: 전사적인 핵심 업무 규칙을 캡슐화
- 클래스, 데이터 구조, 함수 모두 가능
- 중요한 것은 외부 의존없이 순수하게 데이터와 행위를 기반으로 정의
- 엔티티는 어떠한 계층도 알아선 안됨
- 회사의 비지니스 로직을 설명하는 개념을 코드로 정의
UseCases: 앱에 특화된 업무 규칙을 캡슐화
- 유즈케이스는 엔티티를 알아도 되지만 외부원에 있는 컴포넌트를 알면 안됨
- 앱에서 발생하는 비즈니스 로직을 코드로 정의
Interface Adapters: 데이터베이스나 웹 같은 외부 에이전시와 UseCase를 연결 (like ViewModel)
- 인터페이스는 유즈케이스 를 알아도 되지만 외부원에 있는 컴포넌트를 알면 안됨
- 비즈니스 로직과 UI, 웹, 데이터베이스등을 연결해서 기능을 제어
FrameWork & Driver: 데이터베이스, 웹 프레임워크, 안드로이드 프레임워크 등과 같은 도구들로 구성
- 가장 바깥 원에 위치하기 때문에 변경이 있어도 내부 원에 영향을 미치지 않음
가장 중요한 것은 원으로 계층을 나눴을 때 바깥의 원이 안쪽의 원을 의존하는, 의존성의 방향이 유지되어야 함
또한 안쪽의 원으로 갈수록 더욱 추상화되고 높은 수준의 정책으로 구성되어야 함
Presentation Architecture
데이터를 이용해 View를 보여주기 위한 아키텍처
- MVC
- MVP
- MVVM - 안드로이드에서 주로 사용
MV
최초에는 뷰가 데이터를 직접 참조해서 기능 수행
BUT, 모델 하나가 변경되었을 때도 의존하는 모든 뷰가 변경되어야함 - 복잡성이 너무 증가
MVC
Controller라는 중계자가 중간에 들어가게 됨 (변경의 전파를 막음)
이벤트는 Controller로 전달 되며 View와 Model을 이용해서 기능을 수행
이제 모델이 바뀌어도 Controller 부분만 수정하면 View 까지 변경하지 않아도 됨
BUT, MVC는 Controller 내부에서 View와 Model이 밀접하게 결합되기 때문에 코드의 재사용이 어려워지고
전체적으로 Controller가 과도하게 거대해지는 문제를 야기
MVP
View와 Model의 결합을 끊기 위해 Presenter의 등장
이벤트는 View로 진입, Presenter로 보내면 내부적으로 Model과 비즈니스 로직이 실행
BUT, View와 Model은 결합이 줄었지만 Presenter와의 결합도가 상승함
MVVM
Presenter와View간의 결합을 끊기 위해 Observer Pattern 사용
View는 ViewModel의 상태만 구독
ViewModel은 자신의 상태와 비즈니스 로직만 집중하고, View를 아예 알 필요가 없음
Repository, Mapper
Repository Patterm
데이터에 대한 접근 및 처리를 실제 인프라와 분리하여 추상화하는 패턴
Domain 레이어에서 실제 프레임워크에 대한 구현 없이 Data 로직을 작성할 수 있음
Domain에서는 Repository의 인터페이스로써 행위만 정의하고, 실구현은 Data 영역에서 행위를 구현
Mapper
Domain Layer에서 DomainModel은 Database나 Network에서 사용하는 DataModel과 분리되어야 함
Domain 내부의 Domain Model은 외부 Database나 API 스펙이 어떻게 변하든 일관되게 비즈니스 로직을 표현해야 함.(Data Model을 참조하면 안됨 - Data Model은 비즈니스 로직을 표현하지 않고 데이터 원본 그 자체)
Mapper는 Data Model -> Domain Model로 변경해주는 역할
'Android' 카테고리의 다른 글
[Gradle] toml이란? (0) | 2024.07.04 |
---|---|
[Android] Compose - SwipeToDismiss (1) | 2024.06.03 |
[Android] Compose (RE) (0) | 2024.03.04 |
[Android] Dagger (basic) (1) | 2024.01.29 |
[Android] Compose (basic) (1) | 2024.01.15 |