더북(TheBook)

일화

독립적인 컨설턴트로 일하는 동안, 저자 중 한 명은 스타트업에서 어떤 작업을 요청받았다. 그것은 핸드헬드 장비의 그래픽 출력과 관련된 프로젝트에서 리팩토링 후보를 파악하는 작업이었다. 저자는 객체 지향 척도, 설계 악취 감지, 코드 기반에서 동작하는 복제 감지 도구를 포함하여 여러 도구를 돌려 리팩토링 후보를 파악했다. 리팩토링 대상은 Image 클래스였다. 이 클래스에는 공개 메서드가 120개 있었고, 5만 행이 넘는 코드에서 여러 책임이 구현되어 있었다. 설계 악취 감지 도구를 돌리는 동안 이 도구는 Image 클래스를 ‘정신 분열증 클래스’ 악취로 평가했다(이 도구에는 클래스에 엄청나게 많은 공개 인터페이스가 있으며, 클라이언트가 각자 해당 클래스 인터페이스 중에서 완전히 분리된 메서드 그룹만 사용하는 경우를 ‘정신 분열증 클래스’ 악취로 정의한다).

프로젝트 팀에 Image 클래스에 대한 리팩토링을 처음 제안했을 때, Image 클래스는 SRP를 따랐으므로 리팩토링할 필요가 없다고 거절당했다. 그들은 분명히 클래스에는 이미지를 메모리에 올리고, 렌더링하고, 조작하고, 저장하는 작업과 관련된 메서드의 포괄적인 집합이 있으며, 이 모든 메서드는 논리적으로 이미지와 연관성이 있다고 주장했다.

논쟁 과정에서 프로젝트 팀은 책임의 ‘조밀도’ 개념을 완전히 놓쳤다. 이미지 처리 애플리케이션을 살펴보면, 거의 모든 것이 이미지와 관련되어 있다. 모든 것을 Image라는 단일 클래스 내에 넣어야 할까? 절대 그렇지 않다. 추상화마다 고차원 책임 대신 구체적이고, 명확하고, 정확한 책임이 주어진다.

논의 중인 내용으로 다시 되돌아와서 추가로 조사한 결과, 팀 리더는 결함 수정과 기능 향상 과정에서 Image 클래스가 가장 자주 변경된다는 점을 인정했다. 팀 리더는 또한 다양한 이유에서 Image 클래스를 변경했다고 덧붙였다. 저자와 팀은 이 문제를 회피할 수 있는 방안을 여러 차례 토론했다. 결국 팀은 Image 클래스를 리팩토링할 필요성이 있음을 받아들였다. 결과적으로 이미지를 메모리에 올리고, 렌더링하고, 조작하고, 영속적으로 저장하는 기능과 관련이 있는 ImageLoader, ImageRenderer, ImageProcessor, ImageWriter 등 모듈 넷(또는 이름 공간)을 원본 Image 클래스에서 추출했다. 이 리팩토링으로 얻은 이점은 변경을 지역화했다는 것이다. 예를 들어, 이미지 렌더링과 관련된 변경이 필요하면 ImageRenderer 모듈에 있는 코드만 변경하면 될 것이다.

이런 경험에서 파악할 수 있는 핵심 교훈은 ‘책임’(Single Responsibility Principle에서 ‘Responsibility’)이 개념과 관련된 모든 기능의 논리적인 그룹화를 의미하지 않는다는 사실이다. 그 대신 ‘책임’은 변경 이유가 구체적이고, 명확하고, 정확한 책임을 의미한다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.