소프트웨어 개발이 창조의 영역이라면 도로시 세이어즈의 관점에 따라 (1) 개념적 구조의 형성 (2) 실제 표현 수단으로 구현 (3) 실사용자와의 상호작용으로 이뤄질 것이다.

 

이 중 소프트웨어 개발자의 주된 업무는 (1)에 해당하는 현실 세계 문제를 해결하기 위해 소프트웨어 차원의 개념적 구조를 모델링하는 일과 (2)에 해당하는 프로그래밍 언어, 프레임워크, 인프라와 같은 기술적 요소를 사용해 모델링한 개념적 구조를 컴퓨터 시스템으로 구현하는 일이다. 나는 (1)에 해당하는 개념적 구조의 모델링을 소프트웨어 개발의 본질적인 부분으로 (2)에 해당하는 개념적 구조의 구현을 소프트웨어 개발의 부수적인 부분으로 나누고, 소프트웨어 개발이 어려운 이유는 본질적인 부분 때문임을 명확하게 하고자 한다.

 

개념적 구조의 모델링이 본질적인 부분인 이유는 소프트웨어 개발의 목적과 관련이 있다. 우리가 소프트웨어 개발을 하는 이유는 특정 문제를 해결하여 새로운 가치를 고객에게 전달하기 위함이다. 개념적 구조는 이에 부합하지만, 실제 구현은 문제 해결에 직접적으로 도움을 주지 않으며 개념적 구조에 종속적이다. 즉 문제를 해결하고 가치를 전달하는 것은 개념적 구조에 해당하며, 여러 가지 다른 방법으로 구현하더라도 동일한 개념적 구조를 표현한다면 가치 역시 동일하다.

 

그렇다면 소프트웨어 개발은 왜 어려운 것일까? 소프트웨어 개발이 어려운 이유는 부수적인 부분에 해당하는 기술적 요소들과는 무관하게, 본질적 부분에 해당하는 문제 해결을 위한 개념적 구조의 모델링이 이미 어렵기 때문이다. 즉 부수적인 부분이 매우 복잡해져도 이는 추가적인 어려움일 뿐이며, 부수적인 부분이 매우 간편해져도 여전히 소프트웨어 개발은 어려울 것이다.

 

 

본질적 부분

 

개념적 구조

다소 추상적인 개념적 구조란 무엇인가? 개념적 구조를 형성하는 것은 마치 연극 무대를 만드는 것과 같다. 연극 무대를 만들 때 시나리오를 먼저 쓰고 사람이 어떤 행동을 하고 어떠한 걸 느낄지 상상하고 이것을 담을 수 있는 무대를 디자인하는 것처럼, 소프트웨어 세계에서는 문제를 해결하기 위한 유저 시나리오를 쓰고 이를 담을 수 있는 개념적 구조를 모형화 하는 것이다. 즉 문제를 해결한다는 것은 이에 대응하는 유저 시나리오를 쓴다는 것이고, 이 유저 시나리오를 담을 수 있는 효과적인 구조물을 모델링한다는 것이다.

 

본질적 부분의 3가지 특성

앞서 우리는 소프트웨어 개발이 어려운 이유가 본질적 부분에 해당하는 개념적 구조의 모델링이 어렵기 때문이라고 했다. 그렇다면 무엇이 개념적 구조의 모델링을 어렵게 만드는가? 소프트웨어 개발을 어렵게 만드는 본질적 부분의 특성 3가지를 살펴보자.

 

복잡성

소프트웨어 개발은 결코 단순하지 않다. 소프트웨어 개발은 현실 문제를 해결하는 것이기에, 개념적 구조는 문제 해결에 필요한 필연적 복잡성을 가지며, 이 필연적 복잡성 이하로 소프트웨어 개발이 쉬워질 수는 없을 것이다. 즉 풀고자 하는 문제가 어려울수록 소프트웨어 개발은 본질적으로 어려울 것이다.

 

이런 필연적 복잡성 외에도 소프트웨어 개발을 어렵게 하는 우발적 복잡성이란 개념이 존재한다. 우발적 복잡성은 문제 해결에 적합하지 않은 개념적 구조로 접근할 때 증가한다. 필연적 복잡성과 우발적 복잡성의 관계를 이해하기 위해 소프트웨어 차원의 개념적 구조의 복잡성에 대해서 알아볼 필요가 있다.

 

소프트웨어 차원의 개념적 구조는 구조를 이루는 요소들과 이 요소들 간의 상호작용으로 이뤄진다. 이 때 하나의 요소에 갇힌 복잡성을 로컬 복잡성, 이들 요소 간의 상호작용으로 발하는 복잡성을 글로벌 복잡성이라고 한다. 로컬 복잡성과 글로벌 복잡성은 비대칭 관계로 요소 하나가 담당하는 로컬 복잡성이 낮아져 요소들이 많아지면 글로벌 복잡성이 증가하고, 요소의 로컬 복잡성이 높아지면 글로벌 복잡성은 낮아진다* 비대칭 관계를 이루는 로컬 복잡성과 글로벌 복잡성이 균형을 이뤄 복잡성의 총합이 최소를 이루는 지점이 좋은 개념적 구조의 하나의 후보가 되며 이 지점의 복잡성이 필연적 복잡성이 된다. 이 둘의 균형이 깨져 필연적 복잡성보다 증가한 복잡성만큼이 우발적 복잡성이다.

 

물론 이 모델은 매우 단순한 모델이며, 실제 소프트웨어는 훨씬 복잡하기 때문에 더 많은 요소가 고려되어야 할 것이다. 하지만 이 모델을 통해 우리는 소프트웨어에서도 이러한 모델의 역학이 작동함을 유추할 수 있다. 요지는 어떤 문제 해결에 필연적 복잡성을 지닌 개념적 구조가 존재한다는 것이며, 잘못된 개념적 구조는 우발적 복잡성을 증가시킨다는 것이다.

개념적 구조는 필연적 복잡성과 우발적 복잡성을 더한 총 복잡성만큼 어려우며, 이는 우리가 해결해야 할 문제와 우리가 어떤 개념적 구조를 만드느냐에 따라 영향을 받는다. 또한 앞으로 소개할 2가지 특성도 이러한 복잡성을 증가시키는 것으로 기능한다.

 

변경가능성

소프트웨어 세계가 현실 문제를 다루면서 따라온 것은 복잡성만이 아니다. 현실 세계의 개념인 시간도 소프트웨어 세계에서 흐르게 되었다. 개념적 구조가 문제 해결을 위한 유저 시나리오를 담는 것이라면 시간이 흘러 유저 시나리오가 변함에 따라 개념적 구조도 변해야 할 것이다. 이러한 특성은 다른 창조물들과는 다른 소프트웨어만의 독특함을 드러내는데, 소프트웨어와 유사하다고 알려진 건축과 비교하면 더 명확하게 드러난다

 

건축과 소프트웨어의 공통점은 현실 세계의 문제를 해결하기 위한 구조체를 만든다는 것이다. 차이점은 건축은 공간 물리적 제약을 극복하는 분야며 시간 물리적 제약으로부터는 비교적 자유롭다. 건축은 중력으로 인한 공간 물리적 제약을 극복해 가며 구조체를 쌓아 올리며, 우선 한번 완성되면 시간으로 인한 변경으로부터 자유롭다. 반면 소프트웨어는 가상 공간 안에 구조체를 올리기 때문에 공간 물리적 제약으로부터 매우 자유롭다. 우리는 자유롭게 구조체를 이루는 구성요소를 창조하고 결합하고 변경할 수 있지만 소프트웨어는 시간 물리적 제약으로부터 자유롭지 않으며 시간이 흘러 현실 세계가 변하면 이에 소프트웨어는 계속 대응해야 할 책임이 있다.

 

이러한 변경 가능성은 소프트웨어의 필연적 복잡성을 더욱 증폭한다. 시간에 따른 변경 가능성도 개념적 구조에 필히 반영되어야 하며 이는 필연적 복잡성에 해당한다.

 

비가시성

소프트웨어의 특성인 비가시성 또한 건축과 비교하여 설명할 수 있다. 건축의 경우 문제를 해결하기 위한 개념적 구조 전체를 2차원 또는 3차원 공간 안에 투시도 혹은 3D 모델링으로 가시화할 수 있다. 이러한 가시적인 특징은 매우 강력한데, 복잡한 개념적 구조의 논리적 사유를 도와줄 뿐 아니라 구조물을 실제로 구현하지 않아도 개념적 구조가 올바른지 검증할 수 있다. 하지만 소프트웨어는 가상공간에 존재하며 형체가 여러 추상화된 계층으로 이루어져 구조체 전체를 우리 차원으로 가시화하는 것이 불가능하다.

단지 우리가 추상화시킨 여러 계층적 중 하나를 설계 다이어그램으로 표현할 수 있을 뿐이다. 이러한 비가시성은 필연적 복잡성을 가지는 개념적 구조를 구조화하는데 어렵게 하며 우연적 복잡성을 유발한다.

위 3가지 특성으로 알 수 있는 사실은 소프트웨어 개발은 본질적으로 문제 해결의 필연적인 만큼 복잡하며, 변경 가능성과 비가시성으로 복잡함이 가중된다는 것이다. 다시 한번 말하지만 소프트웨어 개발은 어떠한 기술의 발전해도 본질적 복잡성 이하로는 쉬워지지 않을 것이다.

 

 

부수적인 부분

소프트웨어 개발의 부수적인 부분은 개념적 구조 프로그래밍 언어, 프레임워크, 인프라와 같은 기술적 요소로 표현하여 컴퓨터 시스템으로 구현하는 일이다. 여기서 오해하지 말아야 할 것은 부수적이란 단어가 결코 쉽다는 것을 뜻하지 않는다는 것이다. 부수적인 부분도 자체적인 복잡성과 어려움을 가지고 있다. 그렇기에 우리가 그렇게 많은 시간을 기술 공부에 투자하고 있다. 단지 내가 말하고자 하는 것은 본질적 부분에 해당하는 개념적 구조를 형성에 비하면 이를 구현하는 일은 아무리 어렵다고 해도 수고로움에 지나지 않는다는 것이다.

부수적인 부분은 개념적 구조를 더 쉽고 명확하게 표현하기 위해 계속 발전해 왔다. 고급 프로그래밍 언어는 인간의 언어로 거의 모든 개념적 구조를 범용적으로 표현할 수 있으며, 프레임워크와 같은 기술적 요소들은 특정 개념적 구조를 아주 쉽게 표현하도록 설계된 강력한 도구를 제공한다. 또한 비즈니스가 변하면서 개념적 구조가 발전함에 따라 부수적인 부분도 계속 발전해 왔는데, 현대의 대규모 비즈니스에 적합한 하나의 개념적 구조인 마이크로서비스 아키텍처를 구현하는 클라우드 인프라의 발전이 그 예이다.

하지만 부수적 부분은 부수적일 뿐이다. 부수적 부분이 아무리 발전하고, 개발자가 매우 잘 훈련되었다고 하더라도 비가시성으로 인한 우발적 복잡성의 일부만 낮출 수 있을 뿐이며 본질적인 복잡성, 이 이하로 낮아지지 않는다. 부수적 부분은 아무리 발전해도 본질적 부분에 집중할 수 있도록 하는 것 이상으로는 기능하지 못한다. 반대로 구현상의 오류로 개념적 구조가 잘못 구현되면 그 구현이 개념적 구조의 ‘스냅숏’이 되어 우발적 복잡성이 매우 많이 증가할 수 있다. 이는 부수적 부분도 중요함을 보여준다.

 

 

기술주도와 개념주도

요즘 가장 흔히 일어나는 오해는 소프트웨어 개발을 이끌어야 하는 본질과 부수가 거꾸로 되었다는 것이다. 이러한 현상은 소프트웨어 분야가 성숙해져 입문자의 개념적 구조와 현재 유행하는 기술이 다루는 개념적 구조가 많은 차이를 가지기 때문이다. 이 때문에 입문자는 기술이 표현하는 개념적 구조를 보며 개념적 구조를 학습한다. 이러한 상황은 학습에는 매우 비약적으로 유리하나 중요한 것의 순서가 바뀌는 문제를 발생시킨다.

기술주임으로 개념적 구조를 잡는 것이 당연해졌기에 기술적 중심으로 개념적 구조를 잡고 이를 문제에 대입하며 문제를 해결하는 것이다. 이러한 방법은 시스템이 문제의 필연적이지 않기 때문에 우발적 복잡성이 매우 크다.

개발자라면 문제를 보고 개념적 구조를 잡아 기술로 푸는 과정이 정석임을 인지하고 이런 힘을 기르기 위한 노력을 기울여야 한다.