강호형(hohyeong kang)
강호형(hohyeong kang)

Categories

Tags

놀라운 경험

지난 한달동안 부스트캠프 멤버십을 참여하면서 두 개의 작은 프로젝트를 만들었습니다. 첫번째 프로젝트를 할 때에는 기능 구현을 하는 것에 급급하여 일단 작동하는 코드를 짜는데 집중하였는데요, 그러다보니 결국 완성은 하였지만 추후에 이 프로젝트를 유지보수하라고 하면 차라리 다시 처음부터 짜는 것을 선택할 것 같은 엉망진창 스파게티 코드를 만들게 되었습니다. 때문에 두번째 프로젝트를 할 때에는 프로젝트 구조 설계와 여러 코드(모듈) 간의 역할을 나누는 것에 집중하였습니다. 그 결과 각 모듈들의 역할과 위치가 명확해져서 기능을 구현하기 위해 코드를 추가해야할 곳, 수정이 필요할 때 건드려야할 곳이 즉각적으로 파악되었습니다. 거의 대부분의 시간을 설계에 투자하여 실제 기능 개발에는 3일 밖에 쓰지 못했는데도 불구하고 요구사항의 대부분을 구현할 수 있었고, 미구현 기능도 시간이 더 주어진다면 얼마든지 쉽게 추가할 수 있다는 확신이 들었습니다.

적절한 구조를 짜고 각 모듈에게 적절한 역할을 부여하여 알맞은 장소에 위치시키는 것이 얼마나 개발에 편의성을 부여하고, 유지보수를 쉽게 만들어주는지 정말 생생하게 느끼는 경험이었고 개인적으로는 새롭게 눈을 뜨는 것 같은 놀라운 경험이었습니다. 복잡하고 어렵고 중복적으로 구현되었던 기능들이 단지 역할을 나누는 것 만으로 술술 풀리는 것이 정말 놀라웠지요. 거기에 더불에 지난번에 자바스크립트의 프로토타입에 대해 정리하면서 클래스가 객체 지향을 구현하는 방법 중 하나일 뿐이라는 사실을 알게 되면서 더 객체지향에 대해 관심을 가지게 되었습니다. 이토록 강력한 객체지향의 본질은 무엇이고 어떻게 활용해야 더 효과적일지 알고 싶어졌습니다. 때문에 프로젝트가 끝나고 추석 연휴동안 부스트캠프 내에서 정말 많이 추천되었던 객체지향의 사실과 오해 라는 책을 읽게 되었습니다.

객체지향의 사실과 오해

이 책을 읽는 독자들은 대부분 객체지향의 사실과 오해라는 제목을 보고 ‘어디 한 번 내가 알고 있는 객체지향을 부정해보시지’ 하는 도전적인 마음을 가지고 첫 장을 펴게 될 것입니다. 그리고 적어도 저는 첫 장을 통해 완전히 객체지향을 다르게 보게 되었습니다. 저자는 첫 장을 통해 내 머릿속에 들어왔나 싶을 정도로 저를 포함한 많은 사람들이 가지고 있는 객체지향에 대한 개념을 언급한 후 아주 간단하게 그러한 관점은 틀렸다는 것을 설득시킵니다.

기존에 생각하던 객체지향은 시스템을 우리가 사는 실세계의 사물에 비유(추상)하여 적절하게 분할하자는 것이라고 생각했습니다. 이름부터가 ‘객체’지향이니 어떻게 ‘객체’라는 단위로 나눌지에 대해 집중하는 것은 자연스럽습니다. 다행히도 우리는 실세계에서 모든 사물을 아주 자연스럽게 일반화하여 인식하기 때문에 객체지향을 실현하기 위해 우리가 실세계에서 일반화 시킨 사물을 시스템 상에 객체로 추상화하는 것은 그리 어렵지 않습니다. 결국 [객체지향 = 실세계의 모방]이 되는 것이죠. 그러나 이러한 관점은 우리가 시스템을 지나치게 실세계스럽게 바라보게 하면서 객체지향이 가진 거대한 장점들을 잘 활용하지 못하게 합니다.

먼저, 기존의 관점은 객체를 그저 하나의 틀로 보게 만듭니다. 실세계에서 우리가 ‘커피’를 식별하고자 할 때에는 ‘커피’만이 가지는 특징에 집중하게 됩니다. 진한 갈색의, 특유의 향기가 나는 음료를 ‘커피’로 분류하고 인식하게 되죠. 마찬가지로 바삭거리고, 밀가루로 구성된, 달콤한 과자는 ‘쿠키’로 인식합니다. ‘커피’와 ‘쿠키’를 만들어서 손님에게 제공하는 사람을 ‘점원’으로 인식하죠. 이처럼 그 객체가 가지는 핵심적인 특징을 추출해서 클래스를 만들고 그를 통해 객체를 인스턴스화 하는 것이 객체지향이라고 생각하게 됩니다. 그러나 저자는 객체지향에서 정말 중요한 것은 적절한 책임을 수행하는 역할 간의 유연하고 견고한 협력 관계 라고 말합니다. 객체지향적으로 설계하기 위해서는 각 객체에게 어떤 책임을 부여할 것이며 서로 다른 역할을 가진 객체들이 어떻게 협력하게 할 것인가를 우선적으로 고려해야 한다는 것이죠. 우리는 실세계를 완벽하게 똑같이 모방하기 위해 객체지향 페러다임을 도입하는 것이 아닙니다. 카페 시뮬레이션을 구현한다고 가정한다면 앞서 말한 ‘커피’의 진한 갈색과 특유의 향기라는 특징은 실세계에선 가장 핵심적인 특징이지만 시스템을 구현한다는 관점에서는 전혀 필요 없을지도 모릅니다. 정말 중요한 것은 누가 커피를 만들고 누구에게 전달되는가 하는 관계입니다. 이렇게 관계를 생각한다면 이 시스템에서 정말 중요하게 커피가 가져야 하는 상태를 알게 됩니다. 바로 ‘가격’과 ‘용량’이죠. 마찬가지로 ‘점원’ 이라는 객체를 시뮬레이션에서 구현한다고 생각한다면 ‘점원’의 성별, 키, 나이, 심지어 사람이라는 특징도 전혀 중요하지 않을 수 있습니다. 오히려 ‘손님’으로부터 주문을 받아 ‘커피’와 ‘쿠키’를 만든다는 것이 중요하죠. 이처럼 시스템 설계 관점에서 중요한 것은 그 객체가 어떤 상태를 가지냐 보다, 그 객체가 수행하는 역할과 협력 관계가 우선적으로 고려되어야 합니다. 이것은 우리가 실세계에서 어떤 사물(객체)를 바라볼 때와는 다르죠. 실세계에서는 해당 사물의 고유한 상태에 집중하지만 시스템 설계 관점에서 상태는 시스템이 원할하게 작동할만큼만 추상화 되어있으면 충분합니다. 오히려 시스템을 구현하다보면 실세계에는 존재하지않는 상태를 부여하게 될 수도 있죠. 따라서 우리가 객체지향적으로 설계를 할 때에는 역할과 협력 관계를 먼저 구상한 후에 그에 따라 필요한 상태를 할당하는 순서로 진행되어야 합니다. 객체를 단순한 틀이 아닌 시스템 내에서 역할을 가진 협력자로 바라보자는 것입니다.

기존의 관점에서 객체의 메소드가 단순히 해당 객체의 행동에 대한 추상이었다면, 이제는 객체 간에 주고받는 메시지라는 것을 알게 됩니다. 실세계에서 행위는 누가 그 행위를 수행하는지가 중요합니다. ‘손님’이 주문하고 ‘점원’은 주문에 따라 무언가를 제작합니다. 이를 모방하여 행위의 주체를 기준으로 메소드를 정의하는 것은 위에서 상태를 먼저 고려하는 것처럼 시스템 관점에서는 그다지 효과적이지 않습니다. 메서드에서 행위라는 개념을 지워봅시다. 객체지향에서 객체의 메서드는 다른 객체로부터 수신할 수 있는 메시지입니다. 예를 들어 ‘주문받아라’는 ‘점원’이 ‘손님’으로부터 수신하는 메시지 입니다. 프로그래밍에서는 점원.주문받아라() 와 같은 형식으로 표현되죠. ‘손님’ 이라는 객체가 점원.주문받아라() 라는 메서드를 호출함으로써 ‘점원’ 객체에게 ‘주문’이라는 메시지를 송신하는 것입니다. 즉 메서드(메시지)는 특정 객체가 수행할 수 있는 책임이 되는 것입니다. 만약 ‘점원’이 아니라 다른 ‘손님’에게 ‘주문받아라’이라는 메시지를 보내면(손님.주문받아라()) 손님은 ‘주문받아라’ 라는 메시지를 처리할 책임이 없기 때문에 정상적으로 메서드가 수행되지 않겠죠. 시스템 관점에서 메서드는 객체들의 협력을 위한 메시지이고 따라서 메서드를 누가 수행하느냐 보다 어떤 행위가 의사소통 관점에서 왜 필요한지가 더 중요하게 됩니다. ‘손님’이 주문을 한다는 것보다 ‘손님’과 ‘점원’ 간의 시스템적 연결을 위해서 ‘점원’에게 주문을 처리하는 책임을 부여하고 ‘점원’이 ‘손님’과 협력할 수 있도록 ‘점원’이 ‘주문받아라’라는 메서드(메시지)를 이해하도록 하는 것이 객체지향에서의 메서드입니다. 마찬가지로 ‘손님’이 완성된 커피를 가지러 간다는 것보다 ‘점원’이 ‘손님’에게 ‘가져가라’라는 메시지를 보낸다는 관점이 객체지향 설계에서는 적절한 것이죠. 그러면 ‘손님’은 완성된 커피를 가져가야 한다는 책임이 주어지는 것이구요. 만약 ‘서빙’이 존재하는 시스템이라면 ‘서빙’에게 ‘가져가라’라는 메시지를 처리할 수 있도록 하고 ‘점원’이 ‘서빙’에게 ‘가져가라’라는 메시지를 보내면 그만입니다. 이처럼 메서드는 메시지라는 관점은 객체지향의 마법중 하나인 다형성과도 이어지게 됩니다. 결론적으로 객체지향 설계에서는 어떤 행위가 필요한지를 먼저 결정하고 그 행위를 수행할 객체를 결정하는 what/who 사이클 을 따르는 것이 효과적입니다. 메서드는 더이상 어떻게 하느냐에 대한 기술이 아니라 무엇을 해야하는지에 대한, 즉 책임에 대한 기술이라는 것을 기억해야 합니다.

마지막으로, 객체지향이 실세계의 모방이라는 관점은 모든 객체는 자율적이고 능동적으로 행동할 수 있다라는 사실을 잊어버리게 됩니다. 실세계에서는 능동적인 존재와 수동적인 존재가 명확합니다. 예를들어 실세계에서 ‘커피’는 수동적인 존재이죠. ‘점원’에 의해 만들어지고, ‘손님’에 의해 마셔지게 됩니다. 하지만 객체지향의 세계에서는 ‘커피’ 도 능동적으로 행동할 수 있습니다. ‘커피’가 ‘손님’에게 마셔지는 것이 아니라 ‘커피’ 스스로 자신의 양을 줄일 수 있다는 것이죠. 이것은 캡슐화의 관점에서 상당히 중요합니다. 만약 ‘커피’라는 객체가 ‘양(量)’ 이라는 상태를 가지고 있다면 ‘손님’이라는 객체가 ‘커피’의 ‘양’ 에 접근하는 것보다 ‘커피’가 직접 자신의 ‘양’을 줄이는 것이 의존성을 줄일 수 있습니다. 캡슐화를 통해 상태를 숨기고 메서드(메시지)를 통해서만 소통하는 것이 객체간의 협력을 더 단순하고 유연하게 합니다. ‘커피’는 현실에서 처럼 멍청할 필요가 없습니다. 시스템을 더 견고하게 만든다면 ‘커피’ 역시 똑똑하게 행동할 수 있습니다. 다시 한번 말하지만 실세계에 대한 추상은 도메인 모델을 개발자와 사용자가 충분히 인지할 수 있을 정도면 충분합니다. 캡슐화를 통해 모든 객체를 능동적으로 행동할 수 있도록 하고, 적절한 책임을 부여하면, 유연하고 단순한 협력을 낳을 수 있습니다.

소감

이 책을 통해 객체지향 프로그래밍에서 정말 중요한 것은 ‘객체’ 그 자체가 아니라 ‘객체’ 간의 협력이라는 것을 알게 되었습니다. 덕분에 객체지향을 왜 하려고 하는지 그 본질에 더 가까워진 느낌입니다. 왜 객체 단위로 나누려는 것인가. 어떤 단위로 객체를 나눠야 하는가. 왜 객체 단위로 나누면 유지보수성이 좋아지는가. 캡슐화, 추상화, 다형성이 의미하는 바가 무엇인가. 실제 설계에서 객체지향을 어떻게 활용해야 하는가. 이 많은 질문에 놀랍도록 명쾌하게 답을 해주는 책이었습니다. 멀게만 느껴졌던 디자인 패턴도 객체간의 책임과 협력의 방법론이라는 것을 알고 나니 굉장한 흥미가 생겼습니다. 빨리 이 새로운 관점을 가지고 프로젝트를 설계해보고 싶네요!