이번 챕터에서는 본문에 언급한대로 실질적인 데이터 주도 설계에서 책임 주도 설계의 과정이 나옵니다.
이번 챕터에 나온 이론적인 내용은 사실 1, 2, 3장에서 강조해서 소개했던 내용을 중심으로 풀어서 나왔기 때문에 중복되는 구절들이 많았습니다.
그렇기 때문에 이번 챕터의 핵심은 변화하는 소스코드에 있었고, 4장까지는 제가 이미 과거에 선행학습을 했었기 때문에 4장까지만 읽고도 이렇게 변화된 저를 경험할 수 있었던 만큼, 사전에 읽어오는 것이 중요했다고 생각해서 꼭 독서회 전에 읽어올 것을 독서회 리더로서 강조했던 챕터이기도 했습니다.
구성원 분들 중에 4장이 그저 1, 2, 3장 내용과 겹친다라고만 생각하고 소스코드를 가치를 낮게 보고 넘어가신 분이 혹시 계시다면, 꼭 소스코드의 변화를 오랫동안 들여다보시면서 그 과정을 지켜보고 이해했으면 좋겠습니다.
제가 이번에 가장 중요하게 생각했던 문장과 느낀점은 아래와 같습니다.
106P
ReservationAgency는 데이터 클래스들을 조합해서 영화 예매 절차를 구현하는 클래스다.
이 문장은 저자분께서 데이터 주도 설계를 예시로 보여주시면서 언급되는 문장입니다.
위 문장에서 데이터 클래스들을 조합 이라는 것에 포인트를 두고 싶은데요. 4장에서 예제 코드를 통해 소개하고 있듯, 데이터 주도 설계는 객체의 책임을 고려하지 않고, 어떤 프로세스를 처리할 때 어떤 데이터가 있으면 좋을까? 라고 먼저 초점을 맞추는데서 시작하기 때문입니다.
데이터에 초점을 맞추기 때문에 여러 데이터 클래스들을 미리미리 만들어두고 그것들을 하나의 클래스에 의존을 걸어 처리하기 때문입니다. 그러면 C#을 예제로 들 경우 많은 namespace와 class type이 하나의 클래스 파일에 의존이 걸리게 됩니다.
바로 이 부분에서 하나를 바꾸면 다 바꿔야하는 것이 안좋은 포인트라는 것입니다.
물론 Class 마다 역할이 다 있기 때문에 최종적으로는 Aggregation해서 Type들이 집중된 클래스가 생길 수는 있습니다. 책임 주도 설계를 할 때도 이 부분은 트레이드오프를 통해 자연스럽게 나오는 부분입니다.
다만 처음부터 그래서는 안 된다는 것입니다. 처음에 어떤 Domain을 머리 속으로 프로세스를 그려보며 소스코드로 Boilerplate를 구성해 갈 때는 어떤 행위가 필요할까 생각해보고, 어떤 책임들이 필요하며, 그 책임들을 담당할 객체들이 필요할까를 고민하고, 여기까지 비로소 고민이 마쳐졌을 때 class로 코딩하여 추상화를 하는 것입니다.
class를 만든다는 그 자체가 이미 개발자가 추상화하는 행위입니다. 아무 고민도 없이 막 만들어서는 안됩니다. 그렇기에 class의 이름에는 그 객체의 책임이 담겨있는 것이기에 네이밍이 중요하고 가장 어려운 부분일 것입니다. 그렇기에 함축적인 것보다는 할 수 있다면 길게 늘여서 쓰는 것입니다.
도메인의 성숙도와 디테일에 따라 다르고, class의 많다 적다의 기준을 명확히 할 수는 없겠지만, 생각을 충분하게 하지 않은 Boilerplate는 class의 개수가 적을 것이고, 그만큼 객체들이 1개당 1개의 책임이 아니라 여러개의 책임을 지니고 있다는 것일 것이고 이것은 SRP를 위반하는 것입니다.
따라서 최대한 원시타입으로 작성은 하되, OCP를 위반하지는 않도록 적절하게 추상화해야할 것입니다.
다른 하나의 문장은 이것입니다.
114P
앨런 홀럽(Allen Holub)은 이처럼 접근자와 수정자에 과도하게 의존하는 설계 방식을 추측에 의한 설계 전략(design-by-guessing strategy) [Holub04]이라고 부른다. 이 전략은 객체가 사용될 협력을 고려하지 않고 객체가 다양한 상황에서 사용될 수 있을 것이라는 막연한 추측을 기반으로 설계를 진행한다. 따라서 프로그래머는 내부 상태를 드러내는 메서드를 최대한 많이 추가해야 한다는 압박에 시달릴 수 밖에 없으며 결과적으로 대부분의 내부 구현이 퍼블릭 인터페이스에 그대로 노출될 수밖에 없는 것이다.
이 문장에서 저격하고 싶은 포인트는 객체가 사용될 협력을 고려하지 않고 객체가 다양한 상황에서 사용될 수 있을 것이라는 막연한 추측을 기반으로 설계라는 부분입니다.
이 문장을 언급할 때 저는 독서회 구성원들에게 Helper 접미사를 지닌 클래스에 대해서 언급했습니다. 혹은 Common이라는 단어가 들어간 클래스가 있는지도요.
저도 경력을 쌓으면서 선배들의 코드들을 많이 봤지만, Helper라는 접미사가 붙어서 이곳저곳에서 쓰이고 있는 클래스를 봤습니다.
Helper, Common 이라는 class가 있다면 장점이 있습니다.
- 사용이 편리하다.
- 클래스 이름을 고려할 필요가 없다. (메서드 이름만 고려)
저는 이 2번의 이유가 책임 주도 설계의 적이라고 생각합니다. 물론 개발 편리를 위해 어느 정도는 사용할 수 있습니다. BCL에도 Helper 클래스를 이용해서 구현된 static 객체들도 존재하고 BCL뿐 아니라 제가 알지 못하는 수많은 여러 선배들의 Open Source에도 존재할 것입니다.
Helper를 사용하지 말자는 뜻보다는, 매우 엄격하게 Helper의 기준을 정의하고 사용하는 것이 좋다라는 의견을 주장하고 싶었습니다.
많은 회사들에서 최초에 프로젝트를 개발했던 개발자가 그 프로젝트를 회사 근속동안 계속해서 유지보수하는 경우는 솔직히 거의 없습니다. 솔루션 업계든 SI 회사든 마찬가지죠. Domain이 지속적으로 성장하다가 안정기에 접어들면 그 고급인력은 다른 프로젝트에 투입되어 Domain을 성숙시키는데 사용하는 것이 회사에서 개발자 비용을 효율적으로 사용하는 것이기 때문입니다.
그렇기 때문에 최대한 스파게티 코드가 될 여지를 제거하면서 개발하는 것이 내 다음 사람, 내 후배, 내 동료가 유지보수할 수 있는 시간을 줄일 수 있을 것이라는 뜻입니다. 같은 맥락에서 저는 Singleton 패턴도 지양하는 이유가 이것입니다. 아래는 과거에 제가 작성했던 Singleton 패턴에 관한 의견입니다.
static 객체는 CLR이 유일한 인스턴스를 보장 해주는 인스턴스 형태입니다. 싱글톤 역시 인스턴스는 static형태로 가지고 있습니다. 프로세스 내에서 아무데서나 막 접근해도 유일한 인스턴스이기 때문에 개발 시 간편하게 사용할 수 있으나, static 객체에 여러 맴버를 추가해놓고 쉽게 프로그래밍을 한다면, 스파게티 코드 및 static 객체에 대한 의존성이 엄청나게 생겨서 프로그램 유지보수에 좋지 않은 영향 을 끼칩니다. 싱글톤은 나아가, 하나의 클래스기 때문에 static 객체에 대하여 자신만의 추가 기능을 구현하거나, 클래스기 때문에 상속 을 할 수 있다는 장점이 있습니다. 하지만 이렇게 추가 기능을 여러가지를 구현하면 역시 의존도가 높아지기 때문에 프로그래밍 구조를 깨뜨릴 수 있게 될 겁니다.
위 의견과 현재도 크게 변하지 않았습니다.
물론 나쁜 방법은 없고, 현재에 적합한 선택만 있다고 평소에 주장하지만, 그 현재에 적합했던 선택이 시간이 점점 누적되면서 돌이켜 봤을 때 안 좋은 선택만으로 쌓이면 그것은 하나의 방향을 만들어내게 됩니다. 그것을 기술부채라고 부르는 것일 것입니다.
따라서 기술부채를 줄이기 위한 노력은 나로부터 시작되어 팀 모두가 함께 참여해야하는 것이고, 그것을 회사에서 선배 개발자들이 문화로서 주도해야한다는 점입니다.
따라서 잘하든 못하든 지금부터라도 이런 인지를 가지고서, 아무리 이 책이 이상적인 내용을 주장하고는 있다지만 포기하지 말고 이상을 지향하는, 훈련하는 자세가 필요하다고 생각합니다.
그냥 돈 버는 회사에서 왜 그렇게까지 고민하고 어렵게 살려고 하느냐 라고 물어본다면, 문화의 힘이 강력하다고 생각하기 때문입니다. 혼자서는 바꾸기 어려워도 여러 사람이 모이면 바꿀 수 있습니다. 그리고 문화는 좋은 문화던 안좋은 문화던 전파되기 마련이므로, 기왕이면 좋은 문화를 전파하고 싶다는 생각을 갖고 있습니다.
다음 챕터에도 도움이 되는 내용들이 많을 것이라 기대가 되는 독서회 였습니다.