Architecture

[SW Design] - Design by Contract

0cool 2023. 2. 8. 00:35

CS 를 오래 접한 사람이라면 한 번쯤 들어봤을 법한 SW Design 패턴 중에 하나인 Design by Contract.

 

 처음 이 이론이 나온 시점은 대략 1986년, EIffel 이라는 프로그래밍 언어가 개발될 때 컨셉이 제안 됐고, 이후 여러 번의 개선이 있고 나서 대략 2004년쯤 널리 널리 쓰이게 된 이론이다.

 

 해당 이론의 요점은 생각보다 단순한다. 단어 그대로 SW 컴포넌트 단위의 호출을 일련의 '계약'으로 정의하여 관리하는 것.

 

우리는 일상생활에서 수많은 계약을 접하게 된다. 근로계약서, 부동산계약서, 납품계약서 기타 등등..

 

 이러한 계약서들을 우리는 왜 작성할까? 아마 계약 당사자간의 계약 내용에 대해서 강제하고 제한하고 명시하기 위해 사용할 것이다.

 

 이 컨셉을 그대로 SW로 가져오면 된다. 컴포넌트들이 계약 당사자가 되는 거고 해당 당사자간의 호출을 계약으로 정의하는 것이다. 그리고 각각의 당사자(컴포넌트) 사이에서 호출을 수행할 때, 일련의 특약 사항들을 강제하고 제한하고 명시하여 호출하는 것 이것이 DbC의 핵심이다.

 

 위 사진은 DbC에 대해 잘 표현한 wikipedia 이미지이다. 잘 보면 input-precondition, output-postcondition 이 있는데 이 2가지를 명시하고 정의하는 것이 곧 강제하고 제한하고자 하는 계약 내용이다.

 

 즉, 명시된 precondition을 충족하지 못한다면 input을 받지 않을 것이며, postcondition을 충족하지 못한다면 output도 생성하지 않을 것이라는 것이다.

 

우리가 알법한 예시로 예를 들어보자

 

 영쿨쇼핑몰이 있다고 가정하자. 해당 쇼핑몰의 백엔드 파트는 RestApi 구조로 이루어져 있고, 쇼핑몰에 들어가면 제일 먼저 금일 땡처리 상품이 주르륵 DP 된다.

 

 수많은 제품이 있을 텐데 금일 땡처리 상품은 어떻게 DP 해야 할까? 단순하다. Api 호출에 오늘날짜를 넣어서 해당 날짜에 땡처리 등록된 제품을 주르륵 가져오면 된다. 자 그렇면 이 프로세스를 어떻게 DbC로 구현해야 할까?

 

 먼저 input-precondition이다. 이 부분을 서버에서 처리하느냐 아니면 클라이언트에서 처리하느냐에 따라 또 방법이 나뉘긴 하지만(일반적으로 서버에서 처리하는 것을 권장한다.) 바쁘신 프론트개발자를 대신해서 서버에서 해당 precondition을 체크한다고 가정하자.

 

 영쿨쇼핑폴은 DbC를 적극활용하고 해당 api (땡처리 GET)의 precondition으로 '클라이언트의 today와 서버 today의 일치' 여부로 정의했다.

 

 클라이언트에선 '/product/luckies/2023-02-07' 같은 방식으로 api를 접근할 것이다.(뒤에 있는 날짜는 js의 Date() 함수를 활용해 찍어주고) 그러면 서버는 일단 URL의 마지막 날짜를 가져와서 서버의 오늘 날짜와 클라이언트의 오늘 날짜가 같은지를 먼저 체크할 것이다.

 

 이때! 메가히트 영쿨쇼핑몰이 호주에도 런칭해서 쓰이고 있다! 만약에 호주에 있는 사람이 금일 기준으로 해당 api 호출하면 2023-02-08이 호출이 될 것이다!

 

 자 한국에 있는 사람의 오늘은 2023-02-07, 호주에 있는 사람의 오늘은 2023-02-08 아직 글로벌 서버를 만들지 못한 영쿨쇼핑몰은 한국 시간을 기준으로 모든 걸 처리한다고 하면, 호주에 있는 사람의 요청은 대한민국 시간 기준 미래의 땡처리 상품을 조회하는 요청이라 결과를 넘겨줄 수가 없다! 우리가 정의한 Precondition에 위배되는 호출이므로 서버는 땡처리 GET 함수를 호출하지 않고 적절한 리다이렉트 페이지로 넘겨줄 수 있다.

 

그러면 Postcondition이다.

 

 메가히트 영쿨쇼핑몰은 구두쇠 사장의 압력에 못 이겨 작디작은 서버에 모든 서비스를 우겨놓고 간신히 서비스를 돌리고 있다. 하지만 메가히트인 나머지 트래픽과 주문량이 너무 많아서 서버의 로드가 넘칠랑 말랑 하면서 상시 작동하고 있다고 가정하자.

 

쇼핑몰에서 가장 민감한 부분은 아마 결제일 것이다.

 

잘못된 결제의 후폭풍이 두려운 백엔드 개발자는 밤을 새워가며 아래와 같이 postcondition을 정의하였다.

 

 '결제 함수 호출 이후 서버의 분당 로드가 90% 이상이면 서버의 emergency_flag를 '1'로 설정하고, 설령 다음 결제 건부터 함수 호출이 성공했다 가정하더라도, emergency_flag값이 1 이면 해당 결제를 따로 관리하도록 한다.'

 

 조금 복잡하지만 결국 '정상적으로 결제가 성공하더라도 'emergency_flag'가 '1'이라면 결제 완료 리턴을 주지 않을 것이다.' 곧 핵심이다.

 

 이런 식으로 필요와 상황에 따라 precondition과 postcondition을 적절히 사용해 가면서 sw의 안전성을 지속적으로 가져갈 수 있다. 또 하나의 이점으로는 precondition을 적절히 잘 활용하면 이슈와 에러의 추적이 정말 쉬워진다는 이점이다. 이는 업무에 있어서 클라이언트 / 서버 사이의 유한책임 분리를? 적절히 도와주는 역할도 수행한다. (뭐.. 결국 모두의 잘못이겠지만..)

 

 이렇게 오늘은 SW design의 고전 중에 하나인 DbC에 대해 간략히 알아보았다. 최근 들어 Clean 관련된 다양한 아키텍처가 유행하고 (SOLID 원칙 역시 파생된 부분 중에 하나이지만..) 있다. 물론 다양한 필드와 분야에서 부족한 부분이 생겼기 때문에 이를 해결하고자 도입된 이론이라 생각한다. 하지만 그저 유행이라고 세상 모든 SW에 클린아키텍처를 적용하는 오류를 범해서는 안된다.

 

 모름지기 edge tech가 힙하다 생각하여 FP와 Clean arc를 온 세상에 적용하고 개발하면 이는 핏에 맞지 않는 기술로 인한또 다른 '혼란'을 야기할 수가 있다.

 

 옛것이 최고다라는 고지식한 얘기를 하고자 하는 것은 아니다. 하지만 에자일을 꿈꾸는 모든 사람이 혐오하는 WarterFall 설계 방식은 전 세계 최고의 대기업들이 아직까지도 사용하는 100년이 넘어가는 설계방식이며, 가장 직관적이고 단순한 프로세스이다.

 

 모름지기 개발자란 최적의 리소스로 최고의 효율을 뽑아내는 사람, 적절한 도구를 상황에 맞춰 활용하고 융통성?을 잘 갖추고 프레임에 갇히지 않으며 오픈 마인드를 가지고 소통해야 한다고 생각한다.

 

 여러 클래식한 SW Design도 다루면서 요즘 최고의 스포트라이트를 받는 Clean과 Solid 도 기회가 되면 꼭 다뤄보도록 하겠다.

 

'Architecture' 카테고리의 다른 글

[SW Design] - Defensive Programming  (0) 2023.02.10