설계 기본 원리 (Design Principles)
설계는 “어떻게 실현할 것인가”를 구체적으로 결정하는 활동으로, 시스템의 논리 구성을 결정한다. 설계 작업은 기본 구조에 대한 설계와 모듈 내부의 설계(상세 설계)로 분류된다.
- `기본 구조 설계`: 아키텍쳐 설계로, 각 모듈의 역할과 인터페이스를 정의한다.
- `상세 설계`: 모듈 내부의 알고리즘, 데이터를 명세화한다.
설계 작업을 위해서는 반드시 기초가 되는 개념을 정확히 이해하고 적용할 수 있어야 한다. 또한 설계 작업은 문제를 해결하며 솔루션을 구축해 나가는 높은 수준의 의사 결정 과정의 연속이다. 이를 위해서는 설계 원리를 숙지해야 하며 커뮤니케이션도 중요하다.
품질 목표 (Quality Attributes)
사용자가 요구하는 기능을 만족하는 요구 분석 모델은 하나이지만, 품질을 만족시키는 설계안은 여러 가지일 수 있다. 비기능 요구 사항이 품질 특성이고 시스템 설계안을 결정한다.
설계 작업 전에 시스템이 요구하는 품질 특성을 충분히 검토해야 하고, 설계 단계에서 품질 특성에 대한 요구를 잘 반영하는 설계안을 만들어야 한다. 여러 품질 요구 사항을 충족시키도록 설계할 때, 다른 속성에 미치는 영향을 잘 고려하여 설계해야 하며, 상반되는 품질 요구 간의 절충안을 찾는 것도 중요하다.
아키텍쳐 설계 원리 (Architecture Design Principles)
아키텍쳐를 고려한 설계는 복잡한 문제를 다룰 수 있고, 특별히 변경에 잘 대처할 수 있기에 아키텍쳐에 관한 개념을 이해하는 것이 중요하다,
컴포넌트, 서브시스템, 모듈
소프트웨어의 아키텍쳐를 구성하는 요소가 많아질수록 당연히 시스템의 구조는 복잡하지고 유지보수하기 어려워진다. 시스템의 복잡도를 줄이기 위해 분할하는 것이 서브시스템이며, 복잡한 시스템을 서브시스템으로 분할하여 계층화하면 전체적인 개발 과정의 효율이 증가한다.
이때, 컴포넌트, 서브시스템, 모듈은 비슷한 의미로 사용되지만 차이가 존재한다.
- `서브시스템(Sub-System)`: 복잡한 시스템을 쪼개기 위한 아키텍처 수준의 단위이다. (가장 추상화 단계가 높다.)
- `컴포넌트(Component)`: 명확한 인터페이스를 가진 배포 가능/재사용 가능한 기능 단위, 독립 실행 단위이다.
- `모듈(Module)`: 코드를 조직화하기 위한 개발자 중심의 구조 단위로, 세부적인 코드 조각 단위이다.
모듈화 (Modularization)
모듈화는 소프트웨어를 작은 구성 요소(패키지 또는 클래스)로 나누는 것을 말한다. 큰 시스템을 만드는 것보다 모듈 단위로 개발하고 테스트하는 것이 훨씬 쉽고, 또한 변경 사항이 있을 때 수용하는 것도 모듈 단위가 더 쉽기에 자주 사용된다.
모듈로 분할하면 각각의 모듈을 별개로 만들고 수정 가능하기에 좋은 구조가 될 수 있다. 그러나, 모듈성이 너무 크게 증가하면 모듈 간 서로 어떻게 상호작용하는지 이해하는 것이 어려워진다. 따라서 소프트웨어 모듈화 수준을 적절하게 선택하는 것이 중요하다.
결합 (Coupling)
결합은 모듈 간에 서로 의존하는 정도를 의미한다. 좋은 소프트웨어는 낮은 결합력을 가지게 되는데, 이는 모듈 사이에 의존도가 강하면 시스템을 이해하기 어려우며 변경될 때의 파급효과가 커지기 때문이다. 모듈 간의 결합 정도는 다음 두 가지 요소에 의해 결정된다.
- 모듈 간 인터페이스 수
- 각 인터페이스의 복잡성
모듈 사이의 결합은 정도에 따라 다섯 가지로 나누어진다. 위에서 아래로 내려갈수록 결합도가 낮아지고, 더 좋은 설계가 된다.
- `내용 결합(Content Coupling)`: 한 모듈이 다른 모듈의 내부 작업을 직접 참조하거나 수정하는 경우
- `공통 결합(Common Coupling)`: 한 모듈이 다른 모듈이 읽은 전역 변수 값을 쓰거나 변경하는 경우
- `제어 결합(Control Coupling)`: 한 모듈이 다른 모듈의 제어(분기, 조건 등) 흐름 경로를 결정하는 경우
- `스탬프 결합(Stamp Coupling)`: 모듈 간에 필요한 데이터만 전달하지 않고, 구조체나 객체처럼 필요한 것보다 복잡한 데이터 구조를 전달하는 경우
- `데이터 결합(Data Coupling)`: 한 모듈이 다른 모듈에 정확히 필요한 값만 인자로 넘겨주고, 반대로 불필요한 정보는 주지 않는 경우
응집 (Cohesion)
응집은 하나의 모듈 안에서 수행되는 작업들이 서로 관련된 정도를 말한다. 모듈 안의 여러 요소들은 하나의 목적을 위해 유기적으로 관련되어 있는 것이 좋다. 높은 응집성을 가지는 모듈은 재사용하기 좋고, 이해하기도 쉬우며, 수정할 때 받는 영향도 적다.
모듈 사이의 응집은 정도에 따라 다음과 같이 분류된다. 위에서 아래로 내려갈수록 응집도가 강해지고, 더 좋은 설계가 된다.
- `우연적 응집(Coincidental Cohesion)`: 가장 응집이 약한 상태로, 서로 관련 없는 기능들이 그냥 한 곳에 묶여 있는 상태.
- `논리적 응집(Logical Cohesion): 비슷한 종류의 기능들이 하나의 모듈에 논리적으로 묶여 있지만, 호출할 때 구분해서 사용해야 하는 상태.
- `시간적 응집(Temporal Cohesion)`: 특정 시간에 함께 수행되는 작업들이 하나의 모듈에 묶여 있는 상태. (예: 프로그램 초기화 작업)
- `절차적 응집(Procedural Cohesion)`: 수행 순서에 따라 묶여 있지만, 서로 기능적으로는 크게 관련 없는 상태.
- `교환적 응집(Communicational Cohesion)`: 같은 데이터를 사용하거나 같은 데이터를 생성하는 작업들이 묶여 있는 상태.
- `기능적 응집(Functional Cohesion)`: 모듈 안의 모든 기능이 하나의 명확한 기능을 수행하기 위해 밀접하게 연결된 상태. (가장 좋은 응집 형태)
- `정보적 응집(Informational Cohesion)`: 하나의 모듈이 다양한 기능을 제공하지만, 공통된 데이터를 기반으로 서로 다른 작업을 수행하는 상태.
설계 관점
아키텍쳐 기반 설계는 전통적인 설계와 큰 차이가 있다. 전통적 설계 원리는 “Top-Down”, “Bottom-Up , “분할 정복” 등 기능을 실현하는데 초점이 맞추어져 있고, 아키텍쳐 기반 설계는 관점에 근거하는 품질 중심의 설계 작업이다. 이때, 관점이란 소프트웨어 구조(요소와 관계)를 바라보는 일관된 방법이다.
아키텍쳐의 설계 관점은 다음과 같다.
- `모듈 관점`: 일정한 책임을 구현하는 코드 단위인 모듈과 그 관계로 소프트웨어 구조를 설명한다.
- `컴포넌트 관점`: 실행될 때 동작하는 요소와 상호작용으로 구조를 설명한다.
- `할당 관점`: 소프트웨어의 하드웨어 설치, 작업 할당, 구현, 데이터 저장 등으로 설명한다.
설계 작업 과정
설계 과정은 의사 결정 과정이며 동시에 시스템을 알아가는 과정이다. 일반적인 설계 작업 과정은 다음의 순서로 이루어진다.
- `설계 목표 설정`: 전체 시스템에 대한 설계 목표를 파악하고 결정한다.
- `스타일 결정`: 시스템이나 서브시스템의 타입을 결정하기 위해 설계 목표와 유형에 맞는 아키텍쳐 스타일을 결정한다.
- `서브시스템 기능, 인터페이스 명세`: 서브시스템 사이의 인터페이스를 정의하고 서브시스템 사이의 상호작용을 위한 동작을 작성한다.
- `아키텍쳐 설계 검토`: 설계한 아키텍쳐가 요구, 설계 목표, 설계 원리를 잘 만족하는지 검토한다.
설계 메트릭 (Design Metrics)
설계 메트릭은 소프트웨어의 설계 품질을 수치적으로 평가할 수 있게 도와주는 정량적 기준이다. 설계를 마친 후, 그 결과가 원리들을 잘 적용하여 좋은 설계가 되었는지 따진다. 설계 모델에 대한 전통적 측정 방법은 다음과 같다.
- `크기(Size)`: 시스템의 물리적인 규모를 하나의 메트릭으로 측정하는 방법 → 모듈 수, 인터페이스 수, 코드 라인 수(LOC) 등을 센다
- `복잡도(Complexity)`: 시스템이 얼마나 복잡하게 얽혀 있는지를 나타내는 방법 → 구조적 복잡도, 데이터 흐름 복잡도, 제어 흐름 복잡도 등을 수식으로 측정한다
- `결합도(Coupling)`: 모듈 간의 실제 연결 정도를 나타내는 방법 → 외부 모듈 호출 수, 입출력 매개변수의 수, 전역 변수 사용 여부 등을 고려한다.
- `응집도(Cohesion)`: 모듈 내부의 구성 요소들이 하나의 목적을 위해 얼마나 잘 협력하는지를 나타내는 방법 → 내부 함수 간의 관계, 사용하는 데이터 객체 등을 분석한다
- `정보 흐름 (Information Flow)`: 시스템 내에서 얼마나 많은 정보가 처리되고 이동하는지를 나타내는 방법 → 함수 간 전달되는 매개변수 수, 전역 변수 사용, 입출력의 수 등을 통해 측정한다