설계2 (소프트웨어 공학)

소프트웨어 공학에서 구조적 설계는 소프트웨어 시스템을 개발할 때 전체 시스템의 구조와 구성 요소 간의 상호작용을 계획하고 설계하는 단계이다. 이는 소프트웨어 개발 프로세스의 중요한 부분으로 시스템의 복잡성을 다루며 유지보수할 수 있고 확장할 수 있는 소프트웨어 시스템을 만들기 위한 첫 번째 큰 단계 중 하나다. 이러한 구조적 설계는 앞서와 같이 모듈화와 추상화, 모듈 간 인터페이스 설계, 데이터 구조 설계, 제어구조 설계, 자료구조 설계 및 모듈의 응집도와 결합도 등으로 구성되어 있다. 특히 구조적 프로그래밍을 통해 GOTO 문을 최소화하고 대신 조건문과 반복문 등을 사용하여 프로그램 흐름을 명확하게 구조화한다. 이는 코드의 가독성과 디버깅 효율을 높이는 좋은 방법이다. 또한 일반적인 소프트웨어 설계 문제에 대한 해결책으로 사용되는 디자인 패턴을 활용하여 설계의 일관성과 재사용성을 향상한다.

구조적 설계는 소프트웨어의 크기와 복잡성을 관리하고 효율적인 개발 및 유지보수를 가능하게 해주는 중요 단계다. 이를 통해 개발자는 요구사항을 충족하면서도 확장 가능하고 품질 좋은 소프트웨어 시스템을 개발할 수 있다.

 




구조적 설계

구조적 분석은 설계 결과를 구조도로 나타낸다. 구조도는 시스템의 모든 모듈을 사각형의 박스로 표현하며 모듈의 호출 관계를 화살표로 나타내는 계층적 트리 형식의 표이다. 결국 소프트웨어 구조도는 시스템을 어떤 모듈들로 나누었는가를 명시적으로 나타내며 모듈들의 계층구조와 모듈 간 입출력, 인터페이스, 기능을 나타내는 이름들을 보여준다. 이러한 시스템 구조는 전체적으로 치우침 없는 균형을 이뤄야 한다. 그렇지 못한 경우 처리에 병목현상이 발생한다. 균형은 구조도의 폭과 깊이, 입출력 또는 처리 편중, 영향 범위 등에 의해서 결정된다. 시스템 구조도가 불균형인 경우 모듈의 역할이나 규모를 다시 나누어 균형을 이루도록 해야 한다.

구조의 깊이는 시스템 구조도의 레벨 수다. 깊이는 모듈 사이에서 호출하고 반환하는 과정이 길게 이어진다는 뜻이다. 따라서 깊이가 깊어지면 최하위 모듈까지 통신에 대한 부하가 늘어난다. 또한 구조의 넓이는 상위 모듈이 호출하는 모듈의 개수이다. 이를 보통 Fan-out이라 하며 한 모듈에서 호출하는 하위 모듈이 너무 많으면 호출하는 모듈이 제어할 때 병목 현상이 생길 수 있다. 제어의 범위를 너무 크지 않게 하는 것이 운용의 묘를 살리는 것이며 이 또한 매직넘버 7 내외를 유지하는 것이 좋고 이것이 Fan-out 수다. 여기서 호출하는 모듈수를 줄이려면 중간층의 모듈을 몇 개 두어 중간 모듈이 하위 모듈을 호출하도록 한다. 또한 하위 모듈 중 그룹화할 수 있는 모듈이 있거나 다른 모듈 등이 있다면 이를 분리한다.

구조적 설계는 분석 단계에서 생성된 자료 흐름도를 활용하여 소프트웨어 구조를 추출한다. 이에 두 가지 방법으로서 변환분석과 처리분석으로 구조를 추출한다. 

  • 변환분석(Transformation Analysis): 소프트웨어 시스템 내 데이터 변환과 이동을 분석하는 과정으로 시스템 모듈 간 어떻게 데이터가 전달되고 가공되는지를 파악하여 시스템의 데이터 흐름을 이해한다. 이를 통해 시스템의 기능과 동작을 정확하게 파악하고 데이터 변환 과정에서 발생할 수 있는 오류나 문제를 사전에 예측하고 방지할 수 있다. 
    • 데이터 흐름 다이어그램(DFD) 작성: 시스템 내의 데이터 흐름을 그래픽으로 표현하는 도구인 데이터 흐름 다이어그램을 작성한다. 다이어그램은 프로세스, 데이터 저장소, 데이터 흐름 등을 나타내어 시스템의 데이터 흐름을 시각화한다.
    • 프로세스 식별과 정의: 시스템 내의 각각의 프로세스(기능 또는 서비스)를 식별하고, 그들이 어떤 데이터를 입력으로 받고 어떤 데이터를 출력으로 생성하는지를 정의합니다.
    • 데이터 변환 및 가공 분석: 각 프로세스 내에서 데이터가 어떻게 변환되고 가공되는지를 분석합니다. 데이터의 가공 단계에서 발생할 수 있는 오류나 일관성 문제를 식별하고 이에 대한 대비책을 마련합니다.
    • 데이터 저장소 식별과 정의: 데이터 흐름 중간에 위치하는 데이터 저장소(파일, 데이터베이스 등)를 식별하고, 이들이 어떤 데이터를 저장하고 어떤 프로세스에 의해 엑세스되는지를 정의합니다.
  • 처리분석(Transaction Analysis): 소프트웨어 시스템 내에서 발생하는 트랜잭션을 분석하는 과정으로 시스템 사용자와 시스템 간의 상호작용을 이해하고 각각의 트랜잭션이 어떤 데이터와 기능을 요구하며 어떤 결과를 반환하는지를 파악한다. 이로써 시스템의 기능 요구사항을 정확히 파악하고 시스템의 응답 시간과 성능을 평가할 수 있다.
    • 트랜잭션 식별과 정의: 시스템 사용자의 작업 또는 요청을 트랜잭션으로 식별하고 각 트랜잭션이 어떤 데이터와 기능을 요구하는지 정의
    • 트랜잭션의 처리 흐름 분석: 각 트랜잭션이 어떤 프로세스나 모듈을 통해 처리되는지를 분석하며 트랜잭션의 처리 흐름을 파악하여 성능 병목 현상이나 오류 발생 가능성을 예측하고 대비책을 마련
    • 트랜잭션의 성능 분석: 각 트랜잭션의 응답 시간, 처리량 등의 성능을 분석하여 사용자 요구사항을 충족시킬 수 있는지 평가

 




소프트웨어 구조

흔히 스타일이라고 하면 설계와 시공을 위한 큰 밑그림을 그리는데 여기서 일관된 모양과 조화를 뜻한다. 이 스타일을 소프트웨어 구조에서도 사용할 수 있다. 소프트웨어 시스템이 복잡해지면서 시스템의 구조 문제는 더욱 중요해졌는데 왜냐면 일단 개발이 시작되면 이후 잘못된 부분을 바로잡기가 쉬지 않기 때문이다. 이런 문제의 중요성을 인식하여 소프트웨어 구조 개념이 나타났다. 소프트웨어 구조는 시스템 분할, 전체 제어 흐름, 오류처리 방침, 서브 시스템 간 통신 프로토콜을 포함한다. 이에 몇 가지 주요 스타일이 무엇이 있는지 살펴볼 필요가 있다.

  • 저장소 (Repository) 구조: 소스 코드, 문서, 데이터 등의 자원을 중앙 집중화하여 관리하는 구조로 팀원들은 동시에 작업하거나 버전을 관리하며 변경 사항을 추적할 수 있다. 버전 관리 시스템(VCS)인 Git과 같은 도구가 이 구조의 핵심 역할을 수행하며 이를 통해 협업과 코드 품질을 개선하며 변경 내역을 추적하여 문제를 해결하는 데 도움을 준다.
  • MVC (Model-View-Controller) 구조: 소프트웨어를 세 가지 주요 컴포넌트로 분리하여 구조화하는 패턴으로 코드의 재사용성, 유지보수성 및 확장성을 향상하며 시스템의 구성 요소 간의 결합 도를 낮추는 데 도움을 준다. 
    • Model: 데이터와 비즈니스 로직을 관리한다. 데이터의 상태와 조작 방법을 정의하고 시스템의 핵심 기능을 담당한다.
    • View: 데이터를 시각적으로 표현하는 역할을 한다. 데이터를 사용자에게 표시하거나 입력을 받을 수 있는 인터페이스를 제공한다.
    • Controller: 사용자의 입력을 처리하고 Model과 View 사이의 상호 작용을 조정한다. 사용자 요청을 해석하고 적절한 Model 업데이트 및 View 업데이트를 수행한다.
  • Client/Server 구조: 소프트웨어 시스템을 두 부분으로 분할하는 구조로 클라이언트는 사용자 인터페이스와 관련된 작업을 수행하고 서버는 데이터 처리 및 비즈니스 로직과 같은 작업을 수행한다. 클라이언트는 서버에 요청을 보내고 서버는 해당 요청을 처리하여 결과를 반환한다. 이 구조는 분산 처리 및 확장성을 가능하게 하며 다양한 플랫폼과 기기 간의 상호작용을 지원하는 가장 기초적인 구조이다.
  • 계층 (Layered) 구조: 시스템을 여러 계층으로 나누어 각 계층이 특정 기능을 수행하도록 하는 구조로 각 계층은 자신 바로 아래 계층과 상호작용하며 상위 계층은 하위 계층을 직접적으로 몰라도 된다. 이 구조는 코드의 모듈화와 재사용을 촉진하며 시스템의 유지보수 및 확장성을 개선하는 데 도움을 준다.
  • 파이프-필터 (Pipe-Filter) 구조: 데이터 처리를 여러 단계로 나누어 각 단계를 필터로 생각하고 각 단계 간 데이터를 파이프로 연결하여 처리하는 구조로 각 필터는 특정 작업을 수행하며 데이터를 받아 처리한 후 결과를 다음 필터로 전달한다. 이 구조는 시스템의 기능을 모듈화하고 데이터 처리를 효율적으로 관리하는 데 도움을 준다.

설계1 (소프트웨어 공학)

무엇을 개발할 것인가에 대한 요구분석이 끝나면 다음 단계로서 소프트웨어 내부 구조와 자료구조를 설계해야 하는 일이 있다. 소프트웨어는 그 품질을 인정받기 위해서는 요구분석 산출물에 기술된 기능을 잘 수행하도록 설계되어야 하는 중요성을 가지고 있다. 소프트웨어 구조는 소프트웨어 시스템의 골격과 조직이며 이를 설계하는 것은 모든 구성요소인 모듈들을 정의하고 모듈 사이 인터페이스를 정하는 것이다. 이것들이 제대로 설계되지 않는다면 이후 개발, 테스트 및 유지보수 등에 있어서 큰 어려움이 뒤따른다고 봐야 한다. 소프트웨어 설계 작업의 결과는 여러 원리를 토대로 다양한 결과물을 만들어낸다. 구조와 알고리즘, 프로그래밍, 데이터베이스 등 요구분석서와 설계서 사이 설계단계는 구현으로 가기 위한 징검다리이다.

이러한 설계는 창조적인 작업이다. 하지만 원리적 접근이나 체계적 개념이 없는 것은 아니다. 다른 창작 작업도 일정한 틀과 원리가 적용된다. 소프트웨어를 설계하는 과정에서도 설계 기법과 많은 도구가 쓰인다. 특정한 설계기법의 선택이 품질 좋은 소프트웨어를 만드는 데 필수 요건은 아니다. 다만 설계 기법을 잘 적용하면 소프트웨어 설계는 더 개선된다. 

 




설계

앞선 요구분석서는 시스템 문제를 해결하기 위한 기술서이다. 설계는 분석서에 기술된 문제를 해결 방안으로 변경하는 작업이다. 분석과 설계. 완성된 설계는 요구를 만족시키는 특정한 솔루션이다. 문제를 정의하기 위해서 요구명세서를 작성하고 문제에 대한 솔루션을 만드는 설계 방안은 이에 제한받지 않는다. 즉 설계는 여러 가지 대안 중 여러 가지 제약 조건을 잘 만족시킬만한 하나의 솔루션을 선택하는 것이다. 이런 설계는 사용자와 개발자를 동시에 만족시켜야 하는 책임과 의무가 있다. 사용자는 설계를 보고 시스템이 어떤 기능을 하는지 이해하고 개발자는 시스템이 어떻게 동작하는지 이해하여야 한다. 

일정 규모 이상의 소프트웨어 설계는 다음과 같은 분류를 해볼 수 있다.

  • 소프트웨어 구조설계: 전체 시스템을 구성하는 서브 시스템과 관계를 파악
  • 인터페이스 설계: 서브 시스템 사이 인터페이스 설계 및 정의
  • 자료저장소 설계: 파일, 데이터베이스 설계
  • 프로그램 설계: 시스템 컴포넌트인 모듈, 알고리즘 설계
  • 사용자 인터페이스 설계: 메뉴, 화면, 입출력 양식 등 설계

 




설계 원리

소프트웨어 개발 방법의 근간인 개념과 원리로서 방법은 상황에 맞는 것을 적절하게 적용해야 한다. 구조적 설계는 구조적 분석의 ‘형식은 기능을 따른다’는 원리를 일관되게 적용한다. 자료흐름도에서 프로세스들의 패턴은 시스템의 구조를 결정한다. 여기서 분할과 정복이 사용되고 반복한다. 이러한 설계의 중심이 되는 원리들이 몇 가지가 있다.

✔️ 추상화(Abstraction)

추상화는 자세한 사항보다는 근본적 본질에 집중하는 것을 말한다. 다시 말해 자세한 사항은 처음부터 다루지 않고 전체적이고 포괄적인 개념부터 차례대로 세세하게 세분화함으로써 구체화해 나가는 것이다. 이러한 추상화는 어떤 결과가 얻어져야 하느냐는 명세화 관점을 어떻게 달성할 것인가라는 구현관점과는 별개로 하여 설계 작업에 좀 더 집중할 수 있게 만든다. 소프트웨어 설계에서의 대표적 추상화는 기능 추상화, 자료 추상화 및 제어 추상화가 있다.

  • 기능 추상화: 입력자료를 출력자료로 변환하는 과정의 추상화
  • 자료 추상화: 자료와 자료에 적용할 수 있는 기능을 함께 정의하여 자료 객체를 구성
  • 제어추상화: 프로그램의 제어 흐름을 추상화하여 코드의 모듈화와 구조화를 촉진 

 

✔️ 정보은닉(Information Hiding)

설계된 각 모듈은 자세한 처리 내용이 시스템의 다른 부분으로부터 감춰져야 한다는 것이 정보은닉이다. 즉 모듈 안의 내용을 보여주지 않고 잘 정의된 인터페이스를 통해 메시지를 전달하는 개념인데 설계상 모든 결정 사항들이 모듈 안에 감춰져서 다른 모듈이 접근하거나 변경하지 못하도록 하는 것이다. 이는 모듈화의 기준으로도 사용이 가능하며 이에 따른 모듈의 구현은 독립적이며 설계 과정에서 하나의 모듈이 변경되어도 다른 모듈 설계에는 영향을 주지 않는 장점이 있으며 모듈의 이해도도 높일 수 있다.

 

✔️ 단계적 분해

문제를 상위 개념부터 더 구체적인 단계로 하향식 분할하는 개념으로 보통 4가지 과정을 거친다. 먼저 문제를 기본 단위로 나누고 이를 독립된 문제로 구별한다. 이후 구분된 문제의 자세한 내용은 가능한 뒤로 미뤄두고 구체화 작업이 계속 점증적으로 일어난다는 것을 보여준다. 이러한 단계적 분해는 소프트웨어를 이루는 모듈에 대한 구체적 설계를 수행할 때 주로 사용된다. 단계적 분해는 문제를 해결할 만한 작은 문제로 나누고 구체화의 정도를 작게 하여 점증적으로 문제를 다루어 나가는 것이다. 구체적인 설계 결정을 뒤로 미룰 수 있고 조금씩 변경되므로 일관성을 유지 할 수 있는 장점이 있다.

 

✔️ 모듈화

소프트웨어 구조를 이루는 기본적인 블록이 모듈이다. 이 모듈이 구체적으로 어떤 단위를 말하는 것인가에 대해서는 일치되는 정의는 딱히 없다. 그러나 여러 개의 독립적인 프로그램으로 구성된 복합 시스템의 경우에는 시스템 설계 단계에서 모듈은 기능적으로 관련된 여러 개의 프로그램, 함수, 부프로그램의 묶음이기도 하고 추상화된 자료나 병행 처리 프로세스도 모듈로 불린다. 이 모듈의 공통 개념은 수행 가능 명령어, 자료 구조 또는 다른 모듈을 포함하고 있는 독립 단위이다. 모듈은 이름을 가지고 있으며 독립적으로 컴파일이 되고 다른 모듈을 사용할 수도 있으며 다른 프로그램에서 사용될 수도 있다. 

모듈의 크기는 다양하다. 일반적으로 쉽게 이해될 수 있을 정도로 작아야 한다는 데 동의한다. 이런 모듈은 모듈을 이루는 각 요소가 공통의 목적을 달성하기 위해 얼마나 관련이 있는가를 나타내는 응집력을 갖는다. 즉, 응집력은 모듈 안의 요소들이 서로 관련된 정도를 말하며 모듈 설계의 목표는 강력한 응집력을 갖는 모듈을 만드는 것이다. 이러한 모듈 안에서 응집력의 정도를 마이어스는 일곱 가지 단계로 구분했는데 응집력이 강한 것부터 약한 것까지 ‘기능적 – 순차적 – 교환적 – 절차적 – 시간적 – 논리적 – 우연적’ 으로 나열할 수 있다.

모듈의 응집도와 같이 이야기되는 것이 결합도 이다. 결합도는 모듈 간 상호 의존하는 정도를 말하는데 모듈은 하나의 블랙박스로 다른 모듈과의 독립성이 높아야 한다. 이런 독립적 모듈이 되기 위해서는 다른 모듈과의 결합도가 약해야 하며 의존하는 모듈 또한 적어야 한다. 모듈 사이 의존하는 정도는 결합도가 강한 것부터 약한 것까지 ‘내용 – 공통 – 제어 – 스탬프 – 자료’ 결합으로 나열할 수 있다.