프로덕트 매니저(PM)를 위한 도메인 주도 설계(Domain Driven Design, DDD)
설계의 영역
소프트웨어를 개발하는데 있어서 누군가는 설계를 합니다. 옛날에는 설계자(Architect, 아키텍트)가 따로 존재했지만, 애자일 방법론이 유행함에 따라서 무거운 작업이었던 설계가 점차 변화에 빠르게 대응하는 방향으로 진행되었습니다. 이에 따라 자연스레 개발자가 개발을 하면서 직접 설계를 하는 것이 일반화 되었습니다.
개발을 하면서 자연스럽게 설계를 하다 보니, 설계라는 행동에 대해 좀 더 고민하지 않고 눈에 보이는 대로 설계를 하는 형태도 많이 생겼습니다. 생각보다 많은 시니어 개발자들이 좋은 설계에 대한 깊은 고민 없이 코드를 작성하고 기능을 만들어냅니다. 좋은 조직에서 성장했다면 설계에 대한 깊은 고민 없이도 좋은 설계를 만들어내기도 합니다. 설계의 정확한 근거는 자세히 모르더라도 좋은 설계가 몸에 밴 것이죠. 하지만 보통의 경우는 고민 없는 설계는 부정적인 결과를 만들어냅니다.
설계를 중요하게 생각하지 않는 개발자들이 의외로 좋은 평가를 받는 경우들이 있습니다. 이들에게 기능 개발을 요청하면 빠른 시간 안에 해당 기능을 만들어 내죠. (이 부류의 개발자들은 보통 코드 리뷰도 싫어합니다.) 협업을 하는 입장에서 만들고자 하는 기능을 빠르게 만들어 주니 단기적인 평가가 좋을 수 밖에 없습니다. 문제는 프로젝트가 성장하고 팀이 커지면서 발생합니다.
좋은 설계가 아닌 경우 첫 번째로 다가오는 가장 큰 문제는 변화에 대응하기 어렵다는 점입니다. 설계가 잘못되면 무언가를 하나 고치려고 할 때 부작용이 너무 많아 집니다. 시간이 지나면서 특정 기능 개발을 할 수 없거나 오래 걸리는 변명이 늘어나죠. 애써서 무언가를 하나 만들면 여러 곳에서 문제가 터집니다. 이 부류의 개발자는 보통 테스트 코드도 작성하기 싫어하고 무시합니다. 초기 개발 속도를 늦추거든요. 변경이 일어났을 때 변경이 부작용(side-effect)를 만들지 않는다는 보장을 하기가 어려워 집니다.
다음 문제는 협업이 어려워 진다는 점입니다. 시스템의 컨텍스트를 알지 못하면 개발을 하기가 어려워 집니다. 해당 개발자가 아닌 다른 개발자가 기능을 개발할 때 개발 전에 알아야 할 것들이 너무 많아 지죠. “알아야 할 것은 알아야지. 공부를 안 하는게 문제인 것 아냐?” 라고 생각할 수도 있습니다. 공부도 공부 나름이죠. 해당 시스템에서만 필요한 아주 사소한 문맥이나 예외 사항들을 모두 머리 속에 넣어야만 개발할 수 있다면 개발 효율이 떨어지는 것은 당연할 것입니다. 애써 개발하면 문제들이 터지고 그 책임을 본인이 져야 하는데 업무 효율이 잘 나올 수가 없습니다. 점차 일을 맡기 싫어하고, 개발할 수 없는 이유를 찾다가, 마침내 회사를 떠나게 될 것입니다.
익스퍼트 비기너(Expert Beginner)는 전사적으로 견제해야 할 대상입니다. 본인만 성장을 못하는 것이 아니라 조직 전체를 흔들 수 있습니다. 정작 훌륭한 사람들이 이들 때문에 이탈할 가능성도 높습니다. 팀의 멋진 여정을 함께 하는 입장에서, 동료 개발자의 KPI를 기능 개발 속도로 잡지 마세요. 특정 기능을 빠르게 개발했다는 사실 만으로 그 사람을 높게 평가하지 마세요. 물론 속도는 중요합니다만, 장기적인 속도를 생각해야 합니다. 마구잡이 설계를 하는 개발자가 팀 내에서 스포트라이트를 독차지하면, 실상을 아는 다른 개발자들은 팀에 대한 애착이 사라지게 될 것입니다. (대부분의 동료 개발자는 실상을 알고 있습니다.)
알아두면 좋은 개념들
좋은 설계를 판단하기 위해 자주 사용되는 개념들이 있습니다. 낮은 결합도(Loosely coupled)와 높은 응집도(Highly Cohesive)를 가진 설계를 통해 유연하고 재사용성이 높은 프로그램을 구성할 수 있습니다.
결합도 Coupling
소프트웨어의 한 부분이 다른 부분에 얼마나 강력하게 연결되었는지 의미합니다. 결합도가 높은 경우 특정 변화가 만들어졌을 때 부작용(Side-effect)가 발생할 가능성이 높습니다.
응집도 Cohesion
소프트웨어의 특정 기능을 수행하기 위해 관련되어 있는 책임과 역할이 얼마나 잘 모여 있는가를 나타냅니다. 응집성이 낮을 경우 하나의 변경을 위해서 여러 곳을 수정해야 하는 문제가 발생합니다.
유연성 Flexibility
요구 사항 변화에 쉽게 적응할 수 있는가를 나타냅니다.
재사용성 Reusability
특정 기능이나 컴포넌트를 쉽게 재사용 할 수 있는가를 나타냅니다.
단단함 Robustness
시스템이 얼마나 잘 버틸 수 있는가를 나타냅니다.
회복력 Resilience
문제 발생 시 잘 회복할 수 있는가를 나타냅니다.
도메인 주도 설계 (Domain Driven Design)
소프트웨어의 본질은 해당 소프트웨어의 사용자를 위해 도메인에 관련된 문제를 해결하는 능력에 있다.
- 도메인 주도 설계, 에릭 에반스
도메인은 소프트웨어가 아닌 실제 비지니스에 대한 영역을 지칭합니다. 항공기 운항을 관리하는 소프트웨어의 경우 도메인 지식은 항공기 운항에 관련된 전문 지식을 의미합니다.
도메인 주도 설계는 소프트웨어 자체로부터 시작하는 설계가 아니라 도메인 영역으로부터 시작하는 설계를 의미합니다. 생각보다 많은 개발자들이 소프트웨어 자체에 매몰되어서 비지니스에 대해 관심을 잃게 됩니다. 하지만 소프트웨어가 궁극적으로 해결하고자 하는 문제는 소프트웨어 자체가 아니라 실제로 존재하는 “어떤 문제”입니다. 이 문제 자체를 이해하지 못한 상태에서 설계와 개발을 진행하면 프로덕트가 산으로 가게 됩니다.
에릭 에반스는 도메인 주도 설계 책을 통해서 도메인을 이해하고 이를 바탕으로 설계하는 것이 왜 중요하며, 어떻게 해야 도메인 주도 설계를 할 수 있는지 설명합니다. 모든 도메인에 적용할 수 있는 궁극의 설계라는 것은 존재하지 않습니다. 따라서 해당 도메인에 맞는 올바른 설계를 하기 위해 반드시 수행되어야 하는 도메인 전문가와의 커뮤니케이션 방식과 이를 통한 설계 방법을 설명합니다.
아래에서는 도메인 주도 설계에 관련되어 있는 몇 가지 개념에 대해 간략하게 소개합니다.
제한된 컨텍스트(Bounded Context)
제한된 컨텍스트는 도메인 주도 설계에서 매우 중요한 개념 중 하나입니다.
규모가 큰 프로젝트에서는 다수의 모델이 사용되기 마련이다. 그러나 개별적인 모델을 기반으로 작성된 코드가 한데 섞이면 많은 버그가 발생하고 신뢰성이 떨어지며 이해하기 힘든 소프트웨어가 만들어진다. 아울러 팀 구성원 간의 의사소통이 혼란스러워진다. 종종 어떤 컨텍스트에서 어떤 모델을 사용해서는 안 되는지 불분명한 경우도 있다.
- 도메인 주도 설계, 에릭 에반스
도메인 주도 설계는 컨텍스트가 많아지면서 공통으로 사용하는 모델이 점차 복잡해지는 것을 견제합니다. 예를 들어 광고라는 모델을 생각해 볼까요? 사용자의 관점에서 광고는 특정 제품으로 연결되는 이미지 입니다. 이 광고는 이미지(혹은 동영상 등의 크리에이티브) 정보와 광고로 연결되는 URL을 들고 있습니다. 한편 광고 운영자의 관점에서 광고는 광고의 기간과 광고 대상 사용자, 광고 비용 등의 정보가 중요합니다. 광고주의 입장에서 광고는 광고의 효과와 효율이 중요하겠죠. 이 모든 정보를 하나의 ‘광고’ 라는 모델에 다 넣게 되면 위에 인용한 문제가 발생합니다.
이를 해결하기 위해서 제한된 컨텍스트에서 사용하는 모델은 제한된 컨텍스트 내부로 제한합니다. 같은 ‘광고’ 라는 개념을 대할 때에도 제한된 컨텍스트가 다르다면 다른 모델로 구성합니다. 경계 내에서는 모델을 엄격하게 일관된 상태로 유지하고, 경계 바깥의 이슈 때문에 초점이 흐려지거나 혼란스러워지지 않도록 주의합니다.
제한된 컨텍스트는 팀 조직, 어플리케이션 특정 부분, 코드 기반 혹은 데이터베이스 스키마 등 물리적인 형태의 관점에서 명시적으로 설정해야 합니다. 제한된 컨텍스트의 기준은 도메인마다 다를 수 있습니다. 이벤트 스토밍(Event Stroming) 방법론을 활용해서 제한된 컨텍스트를 정의하는데 도움을 받을 수 있습니다.
유비쿼터스 언어(Ubiquitous Language)
위의 예시에서 제한된 컨텍스트 마다 모델은 다를 수 있지만, 광고의 개념 자체는 동일합니다. 도메인 전문가들이 사용하는 언어는 생각보다 더 많은 도메인 지식을 담고 있습니다. 시스템과 개발자, 도메인 전문가가 모두 통일된 언어를 사용함으로써 도메인을 더욱 명확하게 이해할 수 있고, 이를 통해 더 좋은 설계를 할 수 있습니다.
리팩토링(Refactoring)
표면적인 기능의 변화 없이 내부 구조를 변경하는 것을 리팩토링이라고 부릅니다. 리팩토링에도 수준(Level)이 있습니다. 대부분 리팩토링을 얘기하면 코드의 가독성을 높이거나 쉽게 개선할 수 있는 기계적인 변경으로 생각합니다. 한편 도메인 주도 설계에서는 심층 모델을 통한 설계 변화에 해당하는 리팩토링을 언급합니다.
프로젝트 초기 모델은 단편적인 도메인 지식을 담고 있을 가능성이 높으며, 프로젝트가 성장함에 따라서 더욱 복잡하고 전문적인 도메인 지식을 담아야 하는 경우들이 발생합니다. 이 때 심층적인 리팩토링을 통해 도메인에 대한 새로운 통찰을 얻으며 프로젝트의 도약을 경험할 수 있습니다.
리팩토링의 효과는 선형적으로 증가하지 않습니다. 리팩토링 과정에서 얻은 통찰을 통해 발생한 충격은 특정 시점에 갑자기 프로젝트 전체로 퍼져 나갑니다. 책에서는 이런 도약을 ‘기법’이 아니라 ‘사건’이라고 표현합니다.
프로덕트 매니저로서 설계를 대하는 자세
업무 간의 경계가 점차 흐려지고 있습니다. 기획은 기획자, 설계는 설계자, 디자인은 디자이너, 개발은 개발자만 하는 시대는 이미 흘러 갔어요. 모든 팀원이 서로의 분야에 대해 이해하고 소통하고 협력해야 좋은 결과를 만들어낼 수 있습니다. 서로의 영역에 대한 이해도가 높아지고, 그에 따라 다른 영역의 업무 까지도 영향을 미치게 됩니다. 모두가 알고 있는 사실이고 긍정적인 지향점입니다.
이는 매우 좋은 방향이지만, 경계가 흐려지다 보니 가끔 부작용도 생겨납니다. 대표적인 예시 중 하나는 의욕이 넘치는 PM과 매우 수동적인 개발자가 만났을 때 발생합니다. 적극적인 PM은 서비스 기획을 더 충실히 하기 위해 서비스 구성과 더불어 시스템 로직까지 구성합니다. SQL을 공부하면서 DB도 이해하고 필요에 따라 간단한 프로그래밍도 직접 공부해서 하다보니, 기능 기획을 더 충실하게 하기 위해 DB 스키마도 직접 설계하고 시스템 구조에 대해서도 문서화해서 전달하죠. 소극적인 개발자는 주어진 요구 사항에 충실합니다. 팀의 비전과 미션, 해당 기능의 목적과 목표가 무엇인지 고민하지 않습니다. 게다가 DB 구조에 시스템 로직 구성까지 주어졌으니 더욱 고민할 필요가 없어졌습니다. 설계에 대한 고민 없이 주어진 그대로 구현하게 됩니다. 전체 시스템과 이후 방향성으로 봤을 때 더 좋은 설계를 생각해 낼 수 있었음에도 말이죠.
절대 영역을 침범하지 말라는 밥 그릇 챙기기 용 얘기가 아닙니다. 저는 경계는 무너질수록 좋다고 생각합니다. 다만, 할 수 있는 모든 수단을 동원해서 생각을 충분히 전달하고 기여하되 동시에 개발자 스스로 설계를 해볼 수 있도록 적극적으로 독려해 주시기를 바랍니다. 각 분야에 대한 경험과 지식이 서로 다르니 나오는 결과물 역시 당연히 다를 수 있기 때문입니다.
열린 마음으로 귀를 열고, 당장 결과를 볼 때까지의 시간이 아닌 더 높은 목표를 위한 좋은 결과물을 기준으로 생각하고 협업한다면 반드시 좋은 제품을 만드는 훌륭한 팀이 될 수 있을 것이라고 생각합니다.