OOP에 대해 나름의 정의를 내려보았던 지난 게시글에서,
왜 객체지향 프로그래밍인가?
에 대한 개인적인 의견을 기록해두었었습니다.
"현실의 문제 및 클라이언트의 요구사항은 매번 변하며, 문제를 해결하는 동료도 수시로 변한다.
그렇기 때문에, 협업과 유지보수는 프로그래밍을 잘 하는데 있어 아주 중요한 고려사항이다."
- 나, 2022 😅
그동안 접해온 개발 관련 서적이나 토막글들 & 다양한 분야에 걸친 다양한 방법론은 일관적으로 변경이 쉬운
, 유지 보수하기 쉬운
프로그래밍을 말하고 있다고 느꼈습니다. 실무에서 부딪히게 되는 상황도 변경의 연속
이었고요.
실무로서 개발을 한 지는 갓 1년이 지난 시점에서,
그리고 누군가에게 프로그래밍이란 이런 것이다~
라고 설명해야 하는 입장에서,
앞선 개인적인 견해를 검증해보고, 제 생각을 더 공고히 해야겠다 싶었습니다.
그렇게 클린 아키텍처를 읽어보았습니다.
나름 제대로 느끼며 개발해왔구나 ~ 😌 싶으면서, 어떻게 해야 잘 할 수 있는지 구체적으로 배울 수 있었던 것 같습니다.
도메인 - 세부사항 분리
그리고 의존성 규칙
을 통해 변경사항에 유연하게 대처하기 위한 원칙을 제시해줍니다.좋은 얘기만 해뒀는데,
정확히 어떤 구체적인 이야기를 해뒀는지 짤막하게 정리해두고자 합니다.
클린 아키텍처 4부 이후: 컴포넌트 원칙과 아키텍처의 내용을 간단히 정리 & 기록해두었습니다.
어떤 클래스가 어느 컴포넌트에 들어갈 지는 신중하게 결정되어야 합니다.
컴포넌트의 응집도를 잘 지켜나가기 위해 저자는 다음과 같은 원칙을 제시합니다.
REP
를 버리면 재사용이 어렵고,CCP
를 버리면 변경이 너무 잦고,CRP
를 버리면 쓸 데 없는 릴리스가 많아진다.응집도를 위한 세 가지 요소를 모두 충족하는 것은 불가능합니다.
CRP를 준수하다보면 컴포넌트 크기가 작아질 테지만, 나머지 두 원칙은 컴포넌트 크기를 키우는 원칙들입니다.
무엇이 중요할까요?
컴포넌트 끼리는 어떠한 관계를 맺어야 할까?
컴포넌트 설계는 절대로 프로젝트 초기에 완성할 수 없습니다.
컴포넌트 의존성은 프로젝트의 기능을 기술하는 것과는 큰 관련이 없고, 유지보수성 & 빌드가능성을 비추는 관점입니다.
소프트웨어가 존재하지도 않는데 빌드나 유지보수를 잘 한다는 것은 어불성설입니다.
그러나 점차 프로젝트가 성장해감에 따라, 의존성 관리 & 변경사항의 임팩트 줄이기에 대한 요구사항은 증가할 수밖에 없습니다.
컴포넌트의 의존관계에 대한 3원칙은 아래와 같습니다.
고수준
(비즈니스 정책 등) 원칙 ⇒ 변동이 많지 않음
=> 안정적이면서도 유연하게 관리되어야 한다. ⇒ 어떻게? ⇒ 추상화를 통해!
추상화와 안정성
주계열
아키텍처의 주된 목적은, 시스템의 생명주기를 지원하는 것입니다.
시스템의 수명과 관련된 비용은 최소화하고, 생산성을 최대화하기 위해 아키텍처가 필요합니다.
좋은 아키텍처는 위에서 말했든 운영, 개발, 배포를 지원해야 합니다.
그리고 당연하지만 시스템의 기능과 의도, 즉 UseCase를 지원하는 데 방해가 되어서는 안됩니다.
유즈케이스는 아키텍처를 수직적으로 분할합니다. (UI - 업무 로직 - DB에 대해 수직적으로 걸쳐 있다는 의미)
그러나 서로 다른 유즈케이스의 경우, 서로 다른 속도로 발전합니다.
이런 경우 개발, 배포, 운영 관점에서 서로 다른 유즈케이스는 독립적으로 조직되어야 할 수 있습니다.
좋은 시스템 아키텍처는 유즈케이스의 핵심 규칙들을 제외한 세부사항들을 유연하게 바꿀 수 있도록 지원합니다.
저자는 이들 사이에 적절한 경계선을 긋는 것이 필요하다고 말합니다.
핵심 규칙이란 무엇이고, 그렇지 않은 세부사항들은 무엇일까요?
Entity
UseCase
결론적으로 우리는..
저수준 세부사항
은 고수준 추상화 컴포넌트
를 향해 배치되어야 합니다.DIP, SAP
를 활용하면, 소스 코드 레벨에서 이것을 가능하게 할 수 있습니다.시스템 (유즈케이스)
을 이야기하고 있어야 합니다.Hexagonal Architecture
DCI (Data, Context, and Interaction), BCE (Boundary-Control Entity)
등등의 아키텍처는 모두..
궁극적으로 관심사의 분리를 추구합니다.
공통적으로, 시스템 인터페이스 ↔ 업무 규칙 두 계층을 포함한다.
이를 통해, 아래와 같은 목표를 달성합니다.
- Framework-Independent Architecture
- Testability
- UI-Independent , DB, And Anything Else
유즈케이스가 프레젠터를 직접 불러서 호출할 수는 없다.
따라서, out 포트를 프레젠터가 구현하게 두고, 의존성 주입해서 굴리는 식으로 코드를 작성하면 됨
(사견)
테스트 하기 어려운 행위는 Humble 객체에게 토스할 수 있다.
Humble 객체는 유의미한 일을 하지 않는, 쓸모 없는 (humble) 객체
클린 아키텍처를 위해 양방향 인터페이스를 두고, DS를 선언하는 것은 비싸고 번거로운 선택지입니다.
저자는 현재와 미래를 적절히 절충할 수 있는 몇가지 대안을 제시합니다,,
💡 목표를 달성하려면, 빈틈없이 지켜봐야 한다.
요구사항이 발전함에 따라, 아키텍처의 경계가 필요해지는 시점이 언제인지 적절하게 파악하는 것은 아주 중요합니다. 저자는 이를 위해 항상 프로젝트를 면밀히 지켜보는 것이 중요하다고 강조합니다.
움퍼스 사냥 게임
- YAGNI vs
나중에 후회하기
- 보통 오버 엔지니어링이 더 눈물 나온다.
- 그러나 반대라고 값싼 건 아니다 ..
메인 컴포넌트는 필요한 모든 것을 준비한 후, 고수준의 시스템에게 제어권을 넘깁니다.
메인 컴포넌트는 궁극적인 세부사항 (가장 저수준의 정책)이자, 궂은 일을 담당하는 오브젝트입니다.
Object To Rescue
, 적절한 OCP를 지키는 설계로 문제 해결 가능아래 이미지는 4가지의 코드 아키텍처를 보여주고 있습니다.
왼쪽부터 간단히 설명해보면, 아래와 같습니다.
컨테이너
, 컴포넌트
, 클래스
세 가지 계층으로 나누고, 컴포넌트와 관련된 모든 내용을 추상화합니다. (C4 아키텍처 참고)제어자
를 신중하게 사용해야 합니다.public
하다면, 위 네 가지 아키텍처는 본질적으로 차이가 없게 됩니다.gradle
에서 프로젝트를 분리해버리기)