Featured image of post 4. 구조적 프로그래밍

4. 구조적 프로그래밍

2부 - 벽돌부터 시작하기: 프로그래밍 패러다임

구조적 프로그래밍(Structured Programming)은 프로그래밍의 한 접근 방법으로, 프로그램을 작성할 때 제어 흐름을 구조화하여 코드를 보다 이해하기 쉽고 유지 보수하기 쉽게 만들려는 것을 중점으로한다.

구조적 프로그래밍은 프로그래밍 분야에서 중요한 발전 중 하나로, 초기 프로그래밍 방법론이 가진 한계(복잡성, 프로그래밍 실수, 소프트웨어 신뢰성 등)를 극복하고자 탄생하였다.

goto문의 해로움

구조적 프로그래밍을 발견한 데이크스트라는 프로그래밍은 어렵고, 프로그래머는 프로그래밍을 잘하지 못한다고 생각하였는제 아래와 같은 이유가 원인이라고 생각했다.

  • 모든 프로그램은 단순할지라도 너무 많은 세부사항을 담고 있었다.
  • 아주 작은 세부사항이라도 간과하면 예상 외의 방식으로 실패하곤 했다.

데이크스트라는 증명이라는 수학적인 원리를 적용하여 이러한 문제를 해결하려고자 했고, 수학자가 유클리드 계층구조를 사용하여 증명하는 방식을 프로그래머도 사용할 수 있다고 믿었다.

유클리드 계층구조
공리(axiom)는 증명 없이 참으로 받아들이는 명제를 뜻한다.

정리(theorem)는 증명이라는 과정을 통해 참이라는 것이 밝혀진 명제다.

이러한 정리를 증명하는 데 필요한 정리를 보조정리(lemma)로 부르며, 정리를 통해 자연스럽게 도출되는 정리를 따름정리(corollary)라고 부른다.

이를 위한 연구를 진행하면서 합리적인 증명을 위한 분할 정복 접근법을 적용하려 했는데 goto 문장을 이용한 제어 흐름 전환이 모듈을 더 작은 단위로 재귀적으로 분해하는 과정에 방해가 된다는 것을 발견하였다.

반면 goto 문장을 사용하더라도 모듈을 분해할 때 문제가 되지 않는 경우는 if/then/elsedo/while 같은 분기와 반복이라는 단순한 제어 구조에 해당한다는 사실을 발견했다.

다익스트라는 제어 구조가 순차 실행(Sequential execution)과 결합해야 한다고 깨닳았다.

Structured program theorem

Böhm–Jacopini 정리라고도 불리는 구조적 프로그래밍 정리는 프로그램을 작성하는 데 사용되는 모든 제어 구조를 표한할 수 있는 최소한의 구조가 있음을 보여준다.

  • 순차(Sequence)
    • 프로그램의 각 문장들은 순차적으로 실행
    • 어떤 작업을 차례대로 수행하는 것을 의미함
  • 분기(Selection)
    • 조건문을 사용하여 프로그램의 흐름을 분기
    • 조건에 따라 다른 명령문 또는 블록을 실행
    • 일반적으로 if-else문이나 switch-case문을 사용
  • 반복(Iteration)
    • 루프를 사용하여 특정 조건이 충족될 때까지 특정한 명령문 또는 블록을 반복하여 실행
    • 대표적으로는 for루프나 while루프

이러한 제어 구조들이 모든 프로그램을 작성하는 데 필요한 최소한의 구조라는 것을 보여주며, 즉 어떤 프로그램이라도 세 가지 구조로 구현할 수 있다는 것을 의미한다.

기능적 분해

구조적 프로그래밍을 통해 모듈을 증명 가능한 더 작은 단위로 분해할 수 있게 되었다.

  • 이는 결국 모듈을 기능적으로 분해할 수 있음을 뜻하며, 거대한 문제 기술서를 받더라도 문제를 고수준의 기능들로 분해할 수 있다.
  • 분해한 기능들은 구조적 프로그래밍의 제한된 제어구조를 이용하여 표현할 수 있다.

이를 토대로 구조적 분석이나 구조적 설계와 같은 기법이 인기를 끌었으며, 개선되어 널리 알려졌다.

  • 대규모 시스템을 모듈과 컴포넌트로 나눌 수 있다.
  • 모듈과 컴포넌트는 입증할 수 있는 작은 기능들로 세분화할 수 있다.

과학적 방법(Scientific Method)

테스트는 버그가 있음을 보여줄 뿐, 버그가 없음을 보여줄수는 없다.

소프트웨어 개발이 수학적인 구조를 다루는 듯 보이더라도, 소프트웨어 개발은 수학적인 시도가 아니다.

오히려 올바르지 않음을 증명하는 데 실패함으로 올바름을 보여주는 과학과 같다.

이러한 부정확함에 대한 증명은 입증 가능한 프로그램에만 적용할 수 있으며, 구조적 프로그래밍은 프로그램을 증명 가능한 세부 기능 집합으로 재귀적으로 분해할 것을 강요한다.

그러고 나서 테스트를 통해 증명 가능한 세부 기능들이 거짓인지를 증명하려고 시도하며, 거짓임을 증명하는 테스트가 실패한다면, 충분히 참이라고 여기게 된다.

현대적 가치

구조적 프로그래밍에 대한 논의는 많은 새로운 언어를 낳았으며, 기존의 언어에 구조적인 면이 추가되는 등 언어의 발전에 도움이 되었다. 그리고 이후에 나온 프로그래밍 패러다임들에도 영향을 끼쳤다.

구조적 프로그래밍은 프로그래머의 습관을 바꾸었다.

프로그램의 정확성을 증명하는 문제를 떠나서 데이크스트라가 그의 논문에서 말한 대로 시간에 따라 변하는 동적인 과정을 시각화하는 것은 인간에게 매우 어려운 일이다.

GOTO문만의 문제가 아니라 구조화된 흐름 제어문을 사용한다고 할지라도 너무 복잡하게 중첩되어 있거나 스코프의 길이가 너무 긴 코드를 작성한다거나 너무 긴 길이의 하위프로그램을 작성하는 일을 가급적 피하게 경향이 생겼다.

그리고 이런 습관은 다른 사람이 작성한 프로그래밍 코드를 쉽게 이해하는 데 도움을 준다.

결론

  • 구조적 프로그래밍이 가치있는 이유는 프로그래밍에서 반증 가능한 단위를 만들어 낼 수 있는 능력 때문이다.
  • 가작 작은 기능에서 부터 가장 큰 컴포넌트에 이르기까지 모든 수준에서 소프트웨어는 과학과 같고, 반증 가능성에 의해 주도된다.
  • 스프트웨어 아키텍트는 모듈, 컴포넌트, 서비스가 쉽게 반증 가능하도록(테스트하기 쉽도록) 만들기위해 노력해야 한다.
  • 구조적 프로그래밍과 유사한 제한적인 규칙들을 통해 쉽게 반증 가능한 구조를 만들 수 있다.