| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- js
- 게임제작
- 2D
- PointableCanvasModule
- VR플랫폼
- PHOTON
- OculusInteractionSamplesRayCanvas
- 게임개발
- handtracking
- 게임
- 온라인
- 메모리
- linux
- 바이브코딩
- C++
- Unity
- DAIVerse
- 캐시 메모리 사상
- C언어
- 5G에그
- 클린 아키텍처
- 게임은 문화
- 캐스팅연산자
- MacFilter
- TCP
- 동적 힙
- 스펙주도형개발
- 유니티
- VR
- 파일패킹
- Today
- Total
kunyoungparkk
클린 아키텍처란 - 1 본문
이 글은 <클린 아키텍처 - 소프트웨어 구조와 설계의 원칙> 1~3부의 내용을 정리하고, 약간의 생각을 덧붙여 작성했습니다.
개요
소프트웨어를 제대로 만들기는 어렵습니다.
기술을 향한 열정과 전문가가 되려는 열망을 가진채로, 당장 빨라 보이는 길이 아니라 좋은 길을 찾아 가야하니까요.
저 역시 이러한 점에서 부족함을 많이 느껴왔습니다.
잘 설계된 코드가 중요하다는 사실을 아는 뇌를 깨워야 하는데,
주어진 일을 빨리 끝내버리고 싶으니 눈앞에 보이는 쉬운 해결책으로 문제를 해결해왔다고 생각합니다.
그렇게 여태 경험했던 대부분의 프로젝트들은 서로 강하게 연관돼있고, 복잡하게 결합돼서,
결국엔 쌓이고 쌓여 사소한 변경도 매우 어렵고 큰 위험을 감수해야 했습니다.
어느순간 이전에 작성한 코드를 들여다보기도 싫게 말이죠.
저자는 이러한 상황에서는 프로그래머들이 아무리 열심히 한다해도,
점차 효율이 기하급수적으로 떨어질 수밖에 없다고 말합니다.
빨리 가는 유일한 방법은 결국 제대로 가는 방법밖에 없다고요.
SW 시스템의 가치는 행위(Behavior)와, 구조(Structrue)로 이루어집니다.
여기서 행위란, 요구사항을 만족하고 디버깅을 하는 것을 의미하고
구조란 아키텍처의 설계에 대한 것입니다.
저자는 행위를 긴급함, 구조를 중요함에 빗대 설명합니다.
우선순위는
1. 중요하고 긴급함
2. 중요하고 긴급하지 않음
3. 중요하지 않고 긴급함
4. 중요하지 않고 긴급하지 않음
이어야 하는데, 흔히 3번째의 긴급한 일을 첫번째로 격상시킨다고 합니다.
저 역시 그랬던 적이 너무나 많기 때문에 뜨끔했습니다.
아무튼 아키텍처(구조)가 후순위가 되버리면, 결국 시스템 개발 비용이 증가하고, 변경이 어려워진다고 하네요.
프로그래밍 패러다임 : 프로그래밍을 하는 방법 - 어떤 구조를 언제 사용해야 하는 지를 결정
프로그래밍 패러다임은 구조적, 객체지향, 함수형으로 나눌 수 있고,
각 패러다임은 추가적인 규칙을 부과하고, 프로그래머의 권한을 박탈하는 형태를 띕니다.
구조적 패러다임은 제어 흐름의 직접적 전환에 대한 규칙을 부과했습니다. (goto -> if/else/while)
이 과정에서, 프로그램을 '증명 가능한 세부 기능'의 집합으로 재귀적으로 분해하고, 이후 테스트를 통해 거짓을 증명하는 방식을 도입했습니다.
객체지향 패러다임은 제어 흐름의 간접적 전환에 대한 규칙을 부과했습니다. (클래스, 메서드, 다형성)
저자는 흔히들 객체지향 패러다임에 대한 설명으로 실세계를 표현하는 방법론이니, 캡슐화/상속/다형성의 세가지 주문을 말하는 것을 비판합니다.
저자의 정의는 다음과 같습니다.
"다형성을 이용하여 전체 시스템의 모든 소스코드 의존성에 대한 절대적인 제어 권한을 획득할 수 있는 능력"
객체 지향 패러다임을 활용하면, Dependency Inversion (의존성 역전) 을 활용하여 제어 흐름과 소스코드 의존성을 반대로 만들 수 있습니다. 이렇게, 고수준 모듈이 저수준의 세부사항을 포함하는 모듈에 대해 독립성을 보장하는 것을 저자는 객체지향의 핵심이자 정의 그 자체라고 말합니다.
함수형 패러다임은 할당문에 대해 규칙을 부과했습니다. (람다 계산법, 불변성 바탕)
이러한 불변 변수를 활용하면 동시성 문제를 유발하지 않을 수 있습니다.
SOLID - 좋은 벽돌로 좋은 아키텍처를 정의하는 원칙
SOLID 원칙들의 목표는, 변경에 유연하고 이해하기 쉽게끔 클래스를 설계하는 것입니다.
1) SRP (Single Responsibility Principle)
각 모듈(클래스)의 변경 이유를 하나로 제한하는 원칙입니다.
정확히는 하나의 일만 한다는 것이 아니라, 변경되는 이유가 하나여야 한다는 뜻입니다.
변경되는 이유가 하나라는 것은, 서로 다른 Actor(변경을 유발하는 무언가?)에서 사용(의존)되면 안된다는 것입니다.
여러 일을 하는 클래스를 분리하면, SRP는 달성하지만 클래스가 많아져 오히려 관리가 힘들어질 수 있습니다.
저자는 이럴 때 Facade 패턴을 활용하여 이런 클래스 객체들을 Facade 객체에 위임시키는 방법을 추천합니다.
2) OCP (Open Closed Principle)
확장(추가)에 대해 열려 있고, 변경에 대해 닫혀 있어야 한다는 원칙입니다.
3) LSP (Liscov Substitution Principle)
Super Type 객체를 Sub type 객체로 치환한다해도 기존 Super 객체에서의 행위를 지원해야 한다는 원칙입니다.
대표적인 예로 직사각형은 정사각형의 Super Type이 되면 안됩니다.
4) ISP (Interface Segregation Principle)
고수준 모듈에서 의존하는 인터페이스를 목적과 상황에 따라 최대한 분리해야한다는 원칙입니다.
필요도 없는 것을 굳이 보고 접근하게 할 이유가 없으니까요.
5) DIP (Dependency Inversion Principle)
앞서 말했듯이, 객체지향 패러다임에서는 제어 흐름과 소스코드 의존성을 반대로 만들 수 있습니다.
이를 통해 구현체의 변경에 대해 유연한 소프트웨어를 제작할 수 있겠죠.
제어 흐름 속의 의존은 고수준 모듈(클래스)끼리 하게 되니까요.
이러한 추상 Interface 참조는 객체 생성 방식을 강하게 제약하는데, 이 때 Abstract Factory 패턴을 사용하면 됩니다.
또한 저자는,
변동성이 큰 구체 클래스로부터 파생하지 말고, 구체 함수를 override 하지 말라고 합니다.
구체 함수를 override 하면 의존성을 상속하는 꼴이.. 맞긴하죠.
별 생각 없이 상속했던 기억들이 떠오르네요.
어쨋든 결국, 이런 식으로 계속 가면 몇년 전 읽었던 책인 <오브젝트>와 유사하게
사실상 상속보단 인터페이스 구현 위주로 클래스를 설계하게 될 것 같습니다.