오브젝트 독서회 5회차 - 5장 책임 할당하기

Intro

이번 5회차 스터디는 강남역 7번 출구 부근 강남311에서 진행했습니다.

오피스텔 안을 사용하는 것인데, 역시 전문 스터디 룸이 아니라서 그런지 커스텀하게 이쁘게 잘 꾸며져있었고 안락했고 저렴했습니다.

다만 원룸 오피스텔이라서 일반 주민들이 거주하기 때문에 복도에서 소음을 주의해야 한다는 점은 주의했어야 했고, 오피스텔이라서 찾아가기 편했습니다.

잘 꾸며져 있어서 퇴실할 때 방을 사진 찍고 나오려고 했는데 깜빡하고 그냥 나왔네요. 링크에서 사진 확인하시면 될 것 같습니다.

이번 독서회는 1회차 이후 오랜만에 5인 전원이 참석한 독서회였어서 시간을 알차게 사용했습니다.


독후감 문장

133P

데이터 중심 설계로 인해 발생하는 문제점을 해결할 수 있는 가장 기본적인 방법은 데이터가 아닌 책임에 초점을 맞추는 것이다.

@은딩

책임에 초점을 맞춰서 설계할 때 직면하는 가장 큰 어려움은 어떤 객체에게 어떤 책임을 할당할지를 결정하기가 쉽지 않다는 것이다. 책임 할당 과정은 일종의 트레이드오프 활동이다. 동일한 문제를 해결할 수 있는 다양한 책임 할당 방법이 존재하며, 어떤 방법이 최선인지는 상황과 문맥에 따라 달라진다. 따라서 올바른 책임을 할당하기 위해서는 다양한 관점에서 설계를 평가할 수 있어야 한다.

@Vincent @freebear @은딩

134P

객체에게 중요한 것은 데이터가 아니라 외부에 제공하는 행동이다. 클라이언트의 관점에서 객체가 수행하는 행동이란 곧 객체의 책임을 의미한다. 객체는 협력에 참여하기 위해 존재하며 협력 안에서 수행하는 책임이 객체의 존재가치를 증명한다.

@Vincent @아메리카노 @은딩

데이터는 객체가 책임을 수행하는 데 필요한 재료를 제공할 뿐이다.

@은딩

너무 이른 시기에 데이터에 초점을 맞추면 객체의 캡슐화가 약화되기 때문에 낮은 응집도와 높은 결합도를 가진 객체들로 넘쳐나게 된다. 그 결과로 얻게 되는 것은 변경에 취약한 설계다.

@Vincent @은딩

가장 기본적인 해결 방법은 객체를 설계하기 위한 질문의 순서를 바꾸는 것이다.

@Vincent

반면 책임 중심의 설계에서는 "이 객체가 수행해야 하는 책임은 무엇인가"를 겨정한 후에 "이 책임을 수행하는 데 필요한 데이터는 무엇인가"를 결정한다.

@muki4742

135P

협력을 시작하는 주체는 메시지 전송자이기 때문에 적합한 책임이란 메시지 수신자가 아니라 메시지 전송자에게 적합한 책임을 의미한다.

@Vincent

다시 말해서 메시지를 전송하는 클라이언트의 의도에 적합한 책임을 할당해야 한다는 것이다.

@은딩

객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 한다[Metz12].

@아메리카노

"이 클래스가 필요하다는 점은 알겠는데 이 클래스는 무엇을 해야 하지?"라고 질문하지 않고 "메시지를 전송해야 하는데 누구에게 전송해야 하지?"라고 질문하는 것.

@Vincent

136P

메시지를 먼저 결정하기 때문에 메시지 송신자는 메시지 수신자에 대한 어떠한 가정도 할 수 없다. 메시지 전송자의 관점에서 메시지 수신자가 깔끔하게 캡슐화되는 것이다.

@Vincent

책임 주도 설계의 핵심은 책임을 결정한 후에 책임을 수행할 객체를 결정하는 것이다.

@freebear

137P

GRASP 패턴은 이런 지식을 서로 공유하고 쉽게 토의할 수 있도록 이름을 붙여 놓은 것이다.

@은딩

설계를 시작하기 전에 도메인에 대한 개략적인 모습을 그려 보는 것이 유용하다.

@은딩

따라서 어떤 책임을 할당해야 할 때 가장 먼저 고민해야 하는 유력한 후보는 바로 도메인 개념이다.

@muki4742 @freebear

138P

설계를 시작하는 단계에서는 개념들의 의미와 관계가 정확하거나 완벽할 필요가 없다. 단지 우리에게는 출발점이 필요할 뿐이다.

@Vincent @은딩

중요한 것은 설계를 시작하는 것이지 도메인 개념들을 완벽하게 정리하는 것이 아니다.

@muki4742

필요한 것은 도메인을 그대로 투영한 모델이 아니라 구현에 도움이 되는 모델이다. 다시 말해서 실용적이면서도 유용한 모델이 답이다.

@Vincent @은딩

139P

따라서 객체에게 책임을 할당하는 첫 번째 원칙은 책임을 수행할 정보를 알고 있는 객체에게 책임을 할당하는 것이다.

@은딩

책임을 수행하는 객체가 정보를 ‘알고’ 있다고 해서 그 정보를 '저장’하고 있을 필요는 없다.

@은딩

어떤 방식이건 정보 전문가가 데이터를 반드시 저장하고 있을 필요는 없다는 사실을 이해하는 것이 중요하다.

@Vincent

140P

만약 스스로 처리할 수 없는 작업이 있다면 외부에 도움을 요청해야 한다. 이 요청이 외부로 전송해야 하는 새로운 메시지가 되고, 최종적으로 이 메시지가 새로운 객체의 책임으로 할당된다. 이 같은 연쇄적인 메시지 전송과 수신을 통해 협력 공동체가 구성되는 것이다.

@은딩

143P

다시 말해 두 협력 패턴 중에서 높은 응집도와 낮은 결합도를 얻을 수 있는 설계가 있다면 그 설계를 선택해야 한다는 것이다.

@freebear

144P

LOW COUPLING 패턴과 HIGH COHESION 패턴은 설계를 진행하면서 책임과 협력의 품질을 검토하는 데 사용할 수 있는 중요한 평가 기준이다.

@은딩

145P

이미 결합돼 있는 객체에게 생성 책임을 할당하는 것은 설계의 전체적인 결합도에 영향을 미치지 않는다.

@아메리카노

그리고 협력과 책임이 제대로 동작하는지 확인할 수 있는 유일한 방법은 코드를 작성하고 실행해 보는 것뿐이다.

@은딩

올바르게 설계하고 있는지 궁금한가? 코드를 작성하라.

@freebear

147P

여기서 중요한 것은 Screening이 Movie의 내부 구현에 대한 어떤 지식도 없이 전송할 메시지를 결정했다는 것이다. 이처럼 Movie의 구현을 구려하지 않고 필요한 메시지를 결정하면 Movie의 내부 구현을 깔끔하게 캡슐화할 수 있다.

@Vincent @아메리카노

Screening은 Movie와 협력하기 위해 calculateMovieFee 메시지를 전송한다. Movie는 이 메시지에 응답하기 위해 calculateMovieFee 메서드를 구현해야 한다.

@은딩

151P

가장 큰 문제점은 변경에 취약한 클래스를 포함하고 있다는 것이다.

@은딩

152P

따라서 낮은 응집도가 초래하는 문제를 해결하기 위해서는 변경의 이유에 따라 클래스를 분리해야 한다

@muki4742 @은딩

지금까지 살펴본 것처럼 일반적으로 설계를 개선하는 작업은 변경의 이유가 하나 이상인 클래스를 찾는 것으로부터 시작하는 것이 좋다.

@muki4742 @은딩

코드를 통해 변경의 이유를 파악할 수 있는 첫 번째 방법은 인스턴스 변수가 초기화되는 시점을 살펴보는 것이다.

@muki4742

클래스의 속성이 서로 다른 시점에 초기화되거나 일부만 초기화된다는 것은 응집도가 낮다는 증거다. 따라서 함께 초기화되는 속성을 기준으로 코드를 분리해야 한다

@Vincent @muki4742 @아메리카노 @은딩

모든 메서드가 객체의 모든 속성을 사용한다면 클래스의 응집도는 높다고 볼 수 있다.

@Vincent @은딩

154P

클래스를 분리하면 앞에서 언급했던 문제점들이 모두 해결된다.

@Vincent

하지만 안타깝게도 클래스를 분리한 후에 새로운 문제가 나타났다.

@Vincent

155P

클래스를 분리한 후에 설계의 관점에서 전체적인 결합도가 높아진 것이다.

@아메리카노

156P

두 클래스가 할인 여부를 판단하기 위해 사용하는 방법이 서로 다르다는 사실은 Movie 입장에서는 그다지 중요하지 않다.

@Vincent

역할을 대체할 클래스들 사이에서 구현을 공유해야 할 필요가 있다면 추상 클래스를 사용하면 된다. 구현을 공유할 필요 없이 역할을 대체하는 객체들의 책임만 정의하고 싶다면 인터페이스를 사용하면 된다.

@Vincent

158P

프로그램을 if ~ else 또는 switch ~ case 등의 조건 논리를 사용해서 설계한다면 새로운 변화가 일어난 경우 조건 논리를 수정해야 한다. 이것은 프로그램을 수정하기 어렵고 변경에 취약하게 만든다. POLYMORPHISM 패턴은 객체의 타입을 검사해서 타입에 따라 여러 대안들을 수행하는 조건적인 논리를 사용하지 말라고 경고한다. 대신 다형성을 이용해 새로운 변화를 다루기 쉽게 확장하라고 권고한다.

@Vincent @은딩

159P

"설계에서 변하는 것이 무엇인지 고려하고 변하는 개념을 캡슐화하라[GOF94]"라는 객체지향의 오랜 격언은 PROTECTED VARIATIONS 패턴의 본질을 잘 설명해준다. 우리가 캡슐화해야 하는 것은 변경이다. 변경이 될 가능성이 높은가? 그렇다면 캡슐화하라.

@Vincent @muki4742

하나의 클래스가 여러 타입의 행동을 구현하고 있는 것처럼 보인다면 클래스를 분해하고 POLYMORPHISM 패턴에 따라 책임을 분산시켜라. 예측 가능한 변경으로 인해 여러 클래스들이 불안정해진다면 PROTECTED VARIATIONS 패턴에 따라 안정적인 인터페이스 뒤로 변경을 캡슐화하라.

@Vincent @아메리카노

162P

클래스는 작고 오직 한 가지 일만 수행한다. 책임은 적절하게 분배돼 있다. 이것이 책임을 중심으로 협력을 설계할 때 얻을 수 있는 혜택이다.

@아메리카노 @은딩

163P

설계를 주도하는 것은 변경이다.

@은딩

대부분의 경우에 전자가 더 좋은 방법이지만 유사한 변경이 반복적으로 발생하고 있다면 복잡성이 상승하더라도 유연성을 추가하는 두 번째 방법이 더 좋다.

@muki4742

165P

이 도메인 모델은 도메인에 포함된 개념과 관계뿐만 아니라 도메인이 요구하는 유연성도 정확하게 반영한다.

@은딩

166P

아무것도 없는 상태에서 책임과 협력에 관해 고민하기 보다는 일단 실행되는 코드를 얻고 난 후에 코드 상에 명확하게 드러나는 책임들을 올바른 위치로 이동시키는 것이다.

@아메리카노

주로 객체지향 설계에 대한 경험이 부족한 개발자들과 페어 프로그래밍을 할 때나 설계의 실마리가 풀리지 않을 때 이런 방법을 사용하는데 생각보다 훌륭한 설계를 얻게 되는 경우가 종종 있다.

@은딩

이처럼 이해하기 쉽고 수정하기 쉬운 소프트웨어로 개선하기 위해 겉으로 보이는 동작은 바꾸지 않은 채 내부 구조를 변경하는 것을 리팩터링(Refactoring) 이라고 부른다.[Fowler99a]

@muki4742

168P

긴 메서드는 다양한 측면에서 코드의 유지 보수에 부정적인 영향을 미친다.

@은딩

한마디로 말해서 긴 메서드는 응집도가 낮기 때문에 이해하기도 어렵고 재사용하기도 어려우며 변경하기도 어렵다. 마이클 페더스(Michael Feathers)는 이런 메서드를 몬스터 메서드(monster method)[Feathers04]라고 부른다.

@Vincent

주석을 추가하는 대신 메서드를 작게 분해해서 각 메서드의 응집도를 높여라.

@Vincent

169P

뽑아내는 것이 코드를 더욱 명확하게 하면 새로 만든 메서드의 이름이 원래 코드의 길이보다 길어져도 뽑아낸다.[Fowler99a]

@Vincent @아메리카노

171P

이렇게 조그마한 부분에서 개선된 명확성이 모여 변경하기 쉬운 코드가 만들어진다.

@은딩

174P

여기서 하고 싶은 말은 책임 주도 설계 방법에 익숙하지 않다면 일단 데이터 중심으로 구현한 후 이를 리팩터링하더라도 유사한 결과를 얻을 수 있다는 것이다. 처음부터 책임 주도 설계 방법을 따르는 것보다 동작하는 코드를 작성한 후에 리팩터링하는 것이 더 훌륭한 결과물을 낳을 수도 있다. 캡슐화, 결합도, 응집도를 이해하고 훌륭한 객체지향 원칙을 적용하기 위해 노력한다면 책임 주도 설계 방법을 단계적으로 따르지 않더라도 유연하고 깔끔한 코드를 얻을 수 있을 것이다.

@Vincent @freebear @muki4742 @아메리카노 @은딩


이번 챕터에서는 책임 주도 설계에 대해서 이론적으로 자세한 기준을 공부할 수 있었습니다.

모두가 객체지향에 대해 조금 더 이해할 수 있었고, 객체지향의 책임 주도 설계라는 것이 일상 생활에서도 비유될 수 있는 개념으로 확장도 가능했습니다.

기업에서 JD를 만든다던지, 기술의 민주화를 구성한다 같은 개념들이었습니다.

GRASP(General Responsibility Assignment Software Pattern) 이라는 새로운 단어도 등장했지만 다들 단어에 집중하고 현혹되지 않고 그 맥락을 잘 파악하기 위해서, 결국 그 패턴이 어떤 것을 의미하는지 구성원 모두가 이해할 수 있었던 것 같아서 뜻 깊었습니다.

이번 챕터는 다들 이론이 매우 중요하다고 생각해서 문장을 요약하지 못하고 문단 자체를 통째로 지정했기 때문에 타이핑하고 정리하는데 애먹었지만 의미있었다고 생각합니다.

6장에는 드디어 책임 그 자체라고 할 수 있는 인터페이스가 등장합니다. 다같이 다음 챕터를 기대하며 독서회를 마쳤습니다.

4 Likes

독서회를 하고 제 차례 때 3가지를 말을 했던 것 같은데, 그게 벌써 지난 수요일이라 5일이 지난 지금 잘 기억이 나지 않네요.

기억나는 것이 1가지 있습니다.

138P

반대로 코드의 구조가 도메인을 바라보는 관점을 바꾸기도 한다.

필요한 것은 도메인을 그대로 투영한 모델이 아니라 구현에 도움이 되는 모델이다. 다시 말해서 실용적이면서도 유용한 모델이 답이다.

입니다.

얼마 전 JD에 관해 코멘트를 달면서 ‘도메인 성숙도’ 라는 표현을 썼었습니다.

저는 이 도메인 성숙도가 위 문장을 관통하는 문장이라고 생각합니다.

예를 들어…저는 사업을 해본 적은 없지만, 커리어를 쌓아가면서 크게 2가지의 사업형태를 경험했습니다.

  1. 이미 성숙한 도메인과 유사한 비지니스 모델을 구현하여 가격을 낮춰 경쟁력을 갖추는 사업
  2. 아예 존재하지 않는 새로운 시장을 개척하여 프론티어가 되는 사업

여기서 1번을 예시로 들면,


사업 아이템이 있는 개발자 출신 대표가, 창업 비지니스 플랜을 세웁니다. 사업을 하기 위해 그동안 모은 돈이있던, 어디서 투자를 받던, 본인이 혼자 전부 할 수 없으므로 해당 도메인의 경력을 가진 기술자 채용을 고려합니다.

그 기술자를 고용할 때의 기준은 아마도 2가지 중 하나일 것입니다.

  1. 성숙한 도메인에서 특정 파트만을 담당했던 사람
  2. 비교적 덜 성숙한 도메인이지만 여러 파트를 두루 담당했던 사람

둘 중에 하나 이상을 만족하는 인재를 채용하려고 할 것입니다. (인건비는 고려하지 않겠습니다.)
여기서 대표가 자신의 경험과 사업아이템과 소프트웨어 개발 경력으로 고용한 개발자와 함께 2명이서 비지니스 구축을 시작합니다.

만약에 대표가 고용한 개발자에 비해서 덜 성숙한 도메인 경험자였다면, 고용한 개발자보다 미래에 비지니스가 성숙했을 때의 상황을 자세히 알지 못할 것입니다. 더욱 성숙한 도메인에서 직접 경험한 것은 없고, 멀리서 봤을 때 그랬을 것이다~ 라는 것만 있었겠지요.

그렇기 때문에 본인 나름의 경험을 투영해서 추상화한 코드와 고용한 개발자와 의견 충돌이 발생합니다. 서로 경험한 도메인 성숙도가 다르기 때문에 관점에 차이가 발생한 것입니다.

고용한 개발자 관점은 이렇습니다. 고용한 개발자는 비지니스가 성장하다보면 어느 시점에 특정 서비스에 트래픽이 몰릴 것이라는 것을 본인 경험을 통해 알고 있고, 나중에 이 부분은 Auto Scale 기술을 이용해서 컨테이너 오케스트레이션을 해야할 것을 미리 알고 있습니다. 따라서 당장은 아니더라도 언제라도 떼어서 컨테이너화 시킬 수 있도록 모듈화를 해 놓은 모델일 것입니다.

아주 없는 말은 아니기 때문에 개발자 출신 대표는 회사가 더 잘 되길 바라는 미래를 그리며, 개발자 출신 대표가 고용한 개발자의 추상화 모델에 설득당하게 됩니다.


위의 스토리 텔링에는 태클을 걸려면 당연히 걸 수 있을 헛점이 있습니다.

하지만 있을 수 있는 상황을 예시로 든 것이며, 바로 이 예시가 코드의 구조가 도메인을 바라보는 관점을 바꿀 수 있다 라는 예시를 든 것입니다.

결국 서비스는 코드로 표현이 되어 실제적으로 돌아가는 서비스여야하며, 고객대응이 유용해야하며 그것은 곧 품질로 연결되기 때문에 유지보수가 매우 중요합니다.

현재 서비스를 유지보수하는 사람들의 지식수준도 맞아야하며, 방향도 같아야하고, 현재 인원 수에 최적화된 인프라 & 보일러플레이트 방식일 때 유지보수 시간이 가장 효율적으로 비용이 발생할 것입니다.

그래서 동종업계의 비교적 작은 회사들이 상대적으로 큰 회사들의 서비스를 밴치마킹하는 것이고, 그 전략 중 하나가 Job Description 을 파악하여 어떤 책임을 가진 직무들이 있는지 확인해서 미리 서비스의 성장을 대비하는 것이라고 생각합니다. 어쩌면 그런 직무를 고용하는 자체가 도메인을 성장시킬 수 있을 것입니다.

다시 돌아와서,
결국 유지보수가 쉬운 서비스가, 책임 주도 설계로 작성한 추상화 모델이
도메인을 확장시킬 수도 있고
새로운 비지니스를 발견할 수도 있는 계기가 될 수도 있고
개발자들의 시간도 아낄 수 있다는 것입니다.
그렇게 발생한 여유는 다방면으로 서비스의 품질을 향상시킬 것입니다.
1,2,3,4,5 장은 계속 이런 유지보수에 관해 설명하고 있습니다.

6장도 상당히 기대가 됩니다.

7 Likes

주옥같은 후기 너무 감사드립니다.
놀랍게도, 정말 놀랍게도. 아직 제 실력이 많이 미흡함에도 불구하고,
설계에 대해 막연하게 생각해오던 것들이
틀리지 않았다, 난 올바르게 자라고 있구나(?!) 라는 느낌을 주어서 더 힘이 나네요.

3 Likes

책 다 읽으셨나요? 6장이 안 올라 오네요.

2 Likes

독서회를 개인의 시간을 모두 맞춰서 최다 참여로 구성하다보니 중간에 딜레이가 많이 되었고, 그래서 매주 진행하지 못했습니다.

현재 6, 7, 8 회차 독서회가 완료되어서 기록으로 가지고 있는데 제가 최종적으로 정리를 해서 올려야하다보니, 제 개인 시간이 부족하여 올리지 못하고 있습니다.

3 Likes

바쁘시다면 어쩔 수 없죠. 차근 차근 올려주세요.

2 Likes