설계란 무엇인가?
지난 주 기고에 이어서 모듈 구현을 위한 (중국) 동료 개발자와의 코드 리뷰 이야기를 계속한다. M은 자신이 만드는 모듈인 Tenancy와 다른 두명(X와 C)이 개발하는 모듈인 Shipping 사이의 연관성을 충분히 이해했다. 그래서 다른 둘에게 자신이 이해한 것을 설명해서 코드를 수정하게 하라고 요청했다. 다음 날, 코드 수정이 완료되어 리뷰 요청을 받았다. 마침, M이 UML 작성에 재미를 붙인 상황이라 내가 그려준 아래 그림을 참고로 해서 기능을 완료한 상태를 UML 순차도(Sequence diagram)로 그려서 코드 리뷰할 때 보여 달라고 했다. X와 C에게 UML 지식 공유를 하자는 시도였다.
그런데, 코드 리뷰 몇 시간 전에 화장실에서 M을 만났다. 다 그렸냐고 물었더니 그렇다고 했다. 그래서, 내가 한 마디 더 했다.
'두 사람 코드는 보고 그린거지? 니가 상상해서 그리면 안되.'
몇 시간 후에 함께 모여 M이 그린 그림을 봤다. 두 장을 그렸다. 한 장은 화장실에서 나를 만나기 전에 그린 그림이고, 다른 한장은 그 후에 그렸다고 한다. 전자는 짐작으로 그리고, 후자는 두 사람이 짠 코드를 확인하고 그린 그림이었다. 나중에 M은 회고를 하며 그들이 자기 생각과 비슷하게 구현했을 것으로 짐작했다고 한다. 화장실에서 내 말을 듣고, 코드를 확인하기 전까지는. 이 경험을 통해 소통의 중요성에 대해 다시 한번 느꼈다고 고백했다.
기능에 적합한 모양을 만드는 일
한편, C와 X는 내가 기대한 모양도 아니고 M이 예상한 형태도 아닌 코드를 작성했다. 위 그림을 기준으로 차이를 간단히 기술하면, Shipping으로 그려진 객체(혹은 직사각형)는 shipping-demo와 shipping 으로 나뉘어져 구현되었다. 배송 요청에 대한 기록은 shipping-demo가 맡았고, shipping은 택배사 시스템과 연동만을 담고 있었다. 나중에 알게 된 사실이지만, 마이크로 서비스 아키텍처(Micro Services Architecture)에 대한 모호한 인지가 그들에게 웹 서비스를 아주 작은 단위로 만들어야 한다는 강박을 갖게 했다고 한다.
나는 칠판에 이렇게 썼다.
설계란 프로그램이 구현해야 하는 기능(Function)을 어떤 사물(Object) 형태로 모양(Form)을 정하는 일이라고 즉흥적 정의로 설명했다. 그리고, 이 친구들이 기존 경력에서 익숙한 방식을 이렇게 설명했다.
마스터 데이터를 보관하는 통합 데이터베이스를 기준으로 자신의 프로그램과 데이터베이스를 분리시켜 사고하는 대한민국에서 1990년대에 흔히 쓰였던 낙후된 프로그래밍 방식[1]
그렇다. 이 친구들은 비록 신기술[2]을 쓰긴 했지만 코드나 사고는 Client-Server 시절 행태를 크게 벗어나지 못하는 식으로 길게는 8~9년을 개발해왔다. 거기에서 마이크로 서비스 형태로 바로 도약하려니 쉽지 않다. 내가 이 친구들 일에 개입하는 이유가 바로 현대적인 방법을 알려주는 것임을 다시 한번 상기키셨다.
그리고 칠판에 쓴 사물(Object)에 대응하는 우리의 기준을 모듈(Module)[3]이라고 설명했다. 자, 그렇다면 바람직한 모양(Form)은 어때야 하는가?
오직 코드만 바라보면 유용한 프로그램 설계 불가
매장에서 배송하는 목적에 따라 '회전', '창고철수' 등의 표현을 쓰는데 그런 내용을 DB에 기록하는 코드는 shippping에서 할 일이 아니라고 그들은 생각했다. 나는 모양이 옳고 그름을 이해하려면 컴퓨터 앞에 앉은 여러분들 생각과 컴퓨터에만 관심 있는 습관을 버리고 사용자를 고려할 줄 알아야 한다고 단호하게 말했다. 매장에서 일하는 사람들의 행동을 조금만 알면 쉽게 기능에 어울리는 모양을 찾을 수 있는데, 프로그래밍 하는 여러분 관심사나 습관에만 머물어서 불편한 프로그램을 만들지 말라고 말했다. 초보 개발자 시절에는 업무와 기술을 분리해서 인식하고 기술만 중요시하는 경향이 있다.[4] 이런 경향을 경계하라는 의미에서 그리고 말하고 나서 또 칠판에 그림을 그렸다.
가운데 테두리를 그려넣고, 이것이 shipping 모듈이라고 설명했다. 그리고, shipping 모듈 사용자는 두 가지 유형이 있다고 설명했다. 하나는 화면(UI)을 통해 기능을 사용하는 일반 사용자이고, 다른 하나는 API로 여러분 프로그램을 쓰는 동료 개발자라고...
내장이 빠져 있는 객체
먼저 DB를 의례히 (자신들이 짜는) 프로그램에서 분리해 사고하는 일종의 적폐[1]를 심각하게 생각하길 바라며 강한 표현을 사용했다. 여러분이 배송 기록을 shpping 모듈에서 빼버리면, '내장을 밖에 빼둔 모양'이나 다름 없다고 말했다. 일단, 사용자는 불편하기 짝이 없다. 만일, 여러분이 매장 주인이고, 바쁜 하루 중에 택배를 다양한 목적으로 100건을 보냈다고 하자. 여러분이 택배사와 연동하여 성공하거나 실패한 정보만 줄테니, 매장 주인에게 어떤 택배인지는 장부에 따로 기록하라고 하면 어떤 일이 벌어질것인가? 여러분이 매장 주인이라면 그 프로그램을 쓸 것이냐고 물었다. 장사에서 장부가 차지하는 중요성을 안다면, 여러분은 이렇게 설계하지 않았을 것이라고 비유를 이용한 설명을 마무리했다. (다른 사용자인 클라이언트 프로그램 개발자 입장으로 설명해도 비슷할 결론에 이를 수 있을 것이다.) 그러니 shpping 모듈에서 배송 기록은 핵심 기능이며, 이를 위해 데이터베이스를 내장하는 것이 필수적이라고 설명했다. 그리고, 박스 아래쪽에 DB를 그리며 배송 기록을 의미하는 약자 SR(shipping_records)를 썼다.
뒤이어 모듈 안쪽 모양을 살펴보기로 했다. 좌측 상단에 동료 프로그래머가 만든 client 프로그램을 쓰고, 여기서 접근할 수 있게 하는 부분이 API임을 다시 한번 확인했다. 여러분이 만든 API 를 보여달라고 요청했다. 그들은 Go 프로그램[5] 가운데에 URL 선언한 내용을 보여주었다. API 모양은 적절한지 물었다. 동료들은 무슨 질문인지 어리둥절해 했다. 이런 식으로 생각해본 일이 없을 것이니 자연스러운 일이다. 나는 M에게 잘 만들어져 있는 Swagger API[5] 웹 문서 하나를 띄워 달라고 했다. 여러분이 만든 API나 Swagger로 생성한 문서가 있는 모듈 API나 기능은 똑같이 동작할지 몰라도 여러분 API 모양보다는 화면에 띄워진 이 형태가 사용자인 동료 프로그래머에게는 편리하다라고 말했다. 기능이 같아도 편리하게 모양을 바꾸는 일도 설계 활동이라는 설명이다.
빌딩 블록...
API와 연결된 부분은 요청 처리 코드(request handler)이며, 내부적으로 우리는 이를 controller[6]라고 부른다. 요청 처리를 위해서 shipping내에는 두 개의 큰 덩어리가 필요한데, 여러분(C와 X)이 구분한 shipping-demo가 해줘야 할 일인 배송 기록이 하나이고, 배송 업체 시스템과 연동하는 기능이 다른 하나라고 설명했다. 이 부분은 이미 앞에서 충분히 설명했기 때문에 이들을 사각형으로 표기하며 Entity와 Adapter라고 사족[7]을 붙인 후에 부연했다.
Entity는 여러분들이 ORM을 쓰니 알 것이고, Adapter는 shipping 모듈에서 여러 개 택배사와 다수의 계정을 다뤄야 하기 떼문에 전기 코드에 쓰이는 어댑터처럼 상황에 따라 바꿔 사용할 수 있게 구현해야 한다는 의미라고 은유로만 설명했다.
코드 리뷰 과정에서 즉흥적으로 발언한 것이라 빌딩 블록을 기준 없이 소개한 것이 찜찜했다. 리뷰가 끝나고 DDD 빌딩 블록을 적극 사용할까 해서 찾아보니 Adapter에 대응하는 것은 없었다. 굳이 따지면 Service가 이에 대응하는데 명쾌하진 않다. 일단, 빌딩 블록에 대한 생각은 이 정도에서 접었다. 여지를 남기면서...
회고
다행스럽게 동료들의 표정은 고무되어 있었다. 몇 차례 계속된 코드 리뷰에서 배우고 느낀 점을 나눴다. 그러다가 놀라운 말을 들었다. Go 프로그래밍이나 멀티 태넌트 같은 개념들을 동료들보다 늦게 익히느라 말하길 망설여왔던 C가 이렇게 말했다.
마이크로 서비스(Micro Services)를 구현할 때는 막연히 작게 잘라야 한다고 생각해서, 스스로 shipping-demo를 나누면서도 왜 이렇게 해야 하는지 의문이 들었다.
이 말을 듣는 순간은 꽤 오랜 기간 이어진 협업 과정에 대해 개인적으로는 충부한 보상을 받은 순간이었다. 평소에 이 친구들 코드를 보아온 것도 아니고, Go 개발자도 아닌 탓에 문법도 생소한터라 코드 리뷰 시간에는 단기간에 높은 집중력을 요했다. 2, 3일 단위로 확인을 해오는 과정에 피로감과 다른 일과 병행에 따른 상황 전환에서 오는 피로감이 쌓여 있었는데... 어쩐 일인지 C가 이런 깨달음을 스스로 얻었다는 사실에 피로는 사라졌다.
그리고 나서, 나는 (실력이 뛰어난 동료는 물론이지만) 실력이 뛰어나지 않은 동료들과 함께 하는 과정도 충분히 즐기고 있음을 깨달았다.[8] 그리고, 그 과정에서 과거부터 띄엄띄엄 익혀온 설계 역량이 동료들에게 용기를 주고 있다. 이제 조금 긴 호흡으로 설계가 무엇인지 정리하고 공유하는 인생을 살아야 할 듯하다.
주석
[1] 지난 글에서 하나의 통합 데이터베이스와 기다란 프로시져로 만들어진 프로그램인 탓에 수정이 어려운 전형적인 레거시 시스템으로 표현한 바 있다.
[2] 이 친구들은 입사이후 .NET 기반 웹 개발을 해왔고, 심지어 최근에는 react.js와 go 프로그래밍 언어를 쓰지만, 클라이언트-서버 구조에 가깝게 사고한다.
[3] 우리는 REST API를 제공하는 웹 서비스를 모듈이라 부른다. 서비스라는 말이 기획단계에서와 개발자들 사이에서 각각 다른 의미로 혼용되는 탓에 가급적 사용자 인식상의 서비스가 아닌 개발자의 마이크로 서비스에 대해서는 모듈이란 표현을 쓰기로 했다.
[4] 나도 이런 경향을 겪었는데 깊이 생각해 본 일조차 없어, 현상이나 원인은 구체적으로 쓸 수가 없네
[5] shipping 모듈은 Go 프로그래밍 언어를 사용해서 개발 중이며, REST API 문서 생성을 위해 Swagger를 사용 중이다.
[6] MVC 패턴을 채용하는 경우 흔히 쓰는 이름이기도 하지만, 우리 조직(domain)에서는 로그 표준에서 특정 비즈니스 행위와 연결시키기 위해 같은 의미로 이해하는 프로그램 요소이기도 하다.
[7] Entity는 동료들이 ORM을 사용하기 때문에 의미가 분명한데, Adapter는 즉흥적으로 붙여놓은 말이라 (아직은) 사족이다.
[8] 몰랐던 내 모습을 요즘 발견하는 중이다.