[Software Engineering] 설계
소프트웨어 설계 과정에 대해서
✏️ 설계 기본 개념
- 요구 분석: 무엇을 만들 것인가에 대한 분석
- 설계: 그 요구 사항을 어떻게 실현할 것인지에 대한 구체적인 방안을 설계
■ 설계 레벨
소프트웨어 설계는 여러 레벨로 나뉜다:
- 소프트웨어 시스템: 시스템 전체의 구조를 설계
- 서브 시스템 또는 패키지 수준: 아키텍처 스타일이 적용되는 수준
- 패키지 내부의 클래스 수준: 설계 패턴이 적용되는 수준
- 클래스 내부 데이터와 메소드 수준: 데이터 구조와 메소드의 설계
- 메소드 내부 설계: 메소드 내에서의 로직과 흐름 설계
■ 전통적 설계 방법
소프트웨어 설계에서 널리 사용되는 방법론들:
- 분할 정복: 복잡한 문제를 더 작은 문제들로 나누어 해결
- 추상화: 세부 사항을 숨기고, 중요한 개념만을 남김
- 합성: 더 작은 구성 요소를 모아서 전체 시스템을 구성하는 방법
■ 아키텍처
시스템의 구조와 동작 방식을 정의하는 기본 설계 원칙
소프트웨어 아키텍처는 시스템의 구성 요소들이 어떻게 조직되고 서로 소통하는지를 설명한다. 주요 요소는 다음과 같다:
- 모듈화: 시스템을 여러 모듈로 나누는 것
- 계층 구조: 각 모듈의 계층적 관계를 정의
- 데이터 흐름: 데이터가 시스템 내에서 어떻게 전달되는지 설명
- 통신 프로토콜: 시스템 내에서 모듈 간의 통신 방법을 정의
■ Package
Class를 포함하는 컨테이너
- 시스템 아키텍처는 패키지 수준에서 표현된다.
- 패키지는 클래스들의 그룹으로, 유사한 기능을 가진 클래스들을 모아서 하나의 패키지로 구성한다.
- 패키지 사이의 관계는 의존성(Dependency)으로 표현된다.
✏️ 설계 작업 과정
설계는 의사 결정 과정이면서 동시에 시스템을 깊이 이해해가는 과정이다. 설계는 단순히 기술적인 과정뿐만 아니라, 소프트웨어의 비기능적 요구 사항도 고려해야 한다.
■ 품질 목표
설계의 목표 중 하나는 비기능적 요구 사항을 만족시키는 것이다. 이는 시스템의 성능, 보안성, 유지보수성, 확장성 등을 포함하며, 구체적인 설계 목표로 명시된다.
- ISO 25000 표준에 따라 비기능적 요구 사항이 정의된다. 이 표준은 소프트웨어의 품질 목표를 세우는 데 중요한 기준이 된다.
✏️ 전통적인 설계 원리
■ 추상화, 캡슐화, 모듈화
- 추상화: 특정 목적에 관련된 정보에 집중하고, 나머지 불필요한 정보를 무시하는 개념.
- 복잡성을 줄이고, 필요한 정보만 제공하는 설계 원칙.
- 복잡성을 줄이고, 필요한 정보만 제공하는 설계 원칙.
- 캡슐화: 추상화된 대상이 제공하는 서비스를 쉽게 접근할 수 있게 하며, 객체 내부의 데이터와 메소드에 대한 정보 은닉을 제공.
- 데이터 무결성을 유지하고 객체의 상태를 보호하는 역할.
- 데이터 무결성을 유지하고 객체의 상태를 보호하는 역할.
- 모듈화: 소프트웨어를 작은 구성 요소로 나누어, 각 모듈이 독립적으로 작업을 수행하도록 하는 설계 원칙.
- 패키지 또는 클래스로 나누어 구조화한다.
추상화, 캡슐화, 모듈화의 관계
추상화는 설계의 복잡성을 줄이기 위해 불필요한 세부 사항을 숨기는 것이고, 캡슐화는 데이터와 그 데이터에 접근하는 방법을 보호하며, 모듈화는 소프트웨어를 작은 부분으로 나누어 독립성을 확보하는 것이다.
■ 결합 (Coupling)
모듈 간에 서로 의존하는 정도를 의미하며, 결합이 낮을수록 모듈 간의 독립성이 높아지고 유지보수성이 향상된다.
| Coupling 종류 | 설명 |
|---|---|
| Data Coupling | 두 모듈이 기본 데이터 타입(정수형, 문자형 등)을 사용하여 상호작용하는 경우. 모든 함수는 기본적으로 데이터 결합 관계에 있기 때문에 이를 완전히 피하기는 어렵다. |
| Stamp Coupling | 복합 데이터 구조의 일부만 사용하는 경우에도 전체 구조를 전달할 때 발생. 이 경우 복합 데이터 구조를 두 개 이상의 구조체로 분리하는 것이 바람직하다. |
| Control Coupling | 한 모듈이 제어 흐름을 다른 모듈에 전달하는 경우 발생한다. |
| Common Coupling | 두 모듈이 전역 변수를 공유하고 있다면 공통 결합 관계에 있다. 이 경우 두 모듈이 동일한 전역 변수에 접근하여 데이터를 읽거나 수정할 수 있다. |
| Content Coupling | 한 모듈이 다른 모듈의 내부를 직접 참조할 때 발생. 절차적 언어에서 goto 명령어가 이에 해당한다. |
결합을 낮추는 방법
- 모듈 간의 인터페이스 수를 줄이고, 각 인터페이스의 복잡도를 낮추어 결합도를 줄일 수 있다.
■ 응집 (Cohesion)
하나의 모듈 안에서 수행되는 작업들이 서로 얼마나 관련된 정도를 의미하며, 응집도가 높을수록 모듈의 독립성과 유지보수성이 향상된다.
| 응집의 종류 | 설명 |
|---|---|
| Informational Cohesion | 모듈이 여러 작업을 수행하지만, 각 작업은 고유한 시작점과 독립적인 코드를 가진다. |
| Functional Cohesion | 모듈의 모든 요소가 단일한 하나의 기능을 수행할 때 기능 응집도가 높다고 한다. |
| Communicational Cohesion | 모듈의 모든 요소가 동일한 입력 또는 출력을 사용하는 경우 교환 응집력이 있다고 한다. |
| Procedural Cohesion | 모듈 내에서 순서가 정해진 구성 요소들로 이루어진 경우 발생한다. |
| Temporal Cohesion | 모듈의 요소들이 단순히 같은 시간대에 실행된다는 이유로 하나의 모듈로 묶인 경우. 각 요소의 기능이 다르며 상호 의존성이 없다. |
| Logical Cohesion | 구성 요소들이 관련된 임무 또는 비슷한 기능을 수행하기 때문에 하나의 모듈로 묶인 경우. |
| Coincidental Cohesion | 모듈의 구성 요소들이 우연히 모여 응집된 경우로, 가장 낮은 응집도를 의미한다. |
응집력과 결합력의 관계
- 결합력: 서로 다른 모듈, 클래스, 또는 컴포넌트 간에 얼마나 의존하는지를 나타내는 정도.
- 응집력: 모듈, 클래스, 또는 컴포넌트 내의 모든 요소가 하나의 기능 단위로 협력하는 정도.
- 결합력은 낮을수록 좋고, 응집력은 높을수록 좋다.
✏️ 객체지향 설계 원리
__S__RP(Single Responsibility Principle): 단일 책임 원칙 __O__CP(Open/Closed Principle): 개방-폐쇄 원칙 __L__SP(Liskov substitution Principle): 리스코프 치환 원칙 __I__SP(Interface segregation Principle): 인터페이스 분리 원칙 __D__IP(Dependency Inversion Principle): 의존관계 역전 원칙
■ SRP(Single Responsibility Principle)
- 단일 책임 원칙
- 한 클래스는 하나의 책임만 가져야 한다.
■ OCP(Open/Closed Principle)
- 개방-폐쇄 원칙
- 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
- 클라이언트의 코드를 변경하지 않고, 서버의 코드를 확장하여 문제를 해결
■ LSP(Liskov substitution Principle)
- 리스코프 치환 원칙
- 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
- ex) 액셀 인터페이스의 기능은 “앞으로 전진”이다. 후진하면 프로그램의 정확성을 깨뜨리므로 LSP 위반이다.
■ ISP(Interface segregation Principle)
- 인터페이스 분리 원칙
- 하나의 범용 인터페이스를 설계하기 보다는, 특정 클라이언트를 위한 인터페이스 여러개를 설계해야 한다.
■ DIP(Dependency Inversion Principle)
- 의존관계 역전 원칙
- 구체화에 의존하지 말고, 추상화에 의존하라
- 의존한다 == 코드에서 사용된다