이 글은 객체지향의 사실과 오해를 기반으로 작성했다. 자세한 내용은 책을 직접 읽어보는 것을 추천한다.
지난 시간에 작성한 객체 지향의 사실과 오해 - Part1과는 또 다른 내용이지만, 한 번 읽어보는 것을 추천한다.
오늘날 우리는 지하철이라는 대중교통을 통해 손쉽게 다른 지역이로 이동할 수 있다. 신도림에서 합정까지 가기 위해서는 먼저 지하철 노선도 를 봐야한다. 물론 이 커다란 노선도에서 해당 역이 어디에 있는지 찾기 위한 시간은 걸릴 수 있지만, 알아보기 위한 행동은 명확하다. '신도림' 과 '합정'이라는 역을 찾은 후 최단 경로를 찾으면 되는 것이다.
하지만 과거에는 지하철 노선도는 이렇게 간단한 형태가 아니였다. 실제와 유사한 물리적인 지형 위에 구불부굴한 운행 노선과 불규칙적인 역 간의 거리를 사실적으로 표현했었다. 물론 사실적이기는 하다. 하지만 고객이 보기에 정말 좋은 방법일까? 현대의 지하철 노선도를 보면, 전혀 사실적이지 않다.
지하철 노선도에서 '신풍'에서 '보라매', '보라매'에서 '신대방 삼거리'는 간격이 일치하고 있다. 하지만 실제 지도상으로 저 간격이 같을까? 당연히 아니다. 그럼에도 불구하고 우리는 사실적인 과거의 지하철 노선도보다 현대의 지하철 노선도를 더 명확하고 사용하기 좋다고 생각한다.
가장 중요한 것은 얼마나 사실적으로 지형을 묘사했느냐가 아니라 역과 역 사이의 연결성을 얼마나 직관적으로 표현했느냐이다. 본래의 목적과는 무관한 사실적인 지형 정보를 혼합함으로써 역 사이의 연결성이라는 중요한 정보를 파악하기 힘들게 하는 것은 현명한 방법이 아닌 것이다. 우리는 지하철 노선도에서 대동여지도를 원하는 것이 아니다. 항상 목적을 잊지 말자.
그래서 추상화란 무엇인걸까? 이미 느낌적으로는 다들 감이 왔다고 생각한다. 추상화는 불필요한 부분을 도려내가면서 사물의 본질을 드러나게 하는 과정이다. 과거의 지하철 노선도의 문제점을 파악하고, 사실적인 것들을 도려내며 '역과 역의 연결성'만 생각하여 간단한 형태로 만든 것 처럼 말이다.
회사에 입사를 했다고 상상해보자. 수 많은 사람들이 일하고 있는 모습을 볼 수 있을 것이다. 커피를 마시며 열심히 일하고 있는 이 사람이라는 객체들은 우리는 팀별 혹은 직무별로 특정지어 분류할 수 있다. 예를 들어, 프론트엔드 개발자와 백엔드 개발자 팀과 같이 말이다. 이렇게 공통점을 기반으로 객체들을 묶기 위한 그릇을 개념(concept) 이라고 한다. 그리고 나는 방금 그 개념을 이용하여 객체를 여러 그룹으로 분류(classification)한 것이다.
결국 각 객체는 특정한 개념을 표현하는 그룹의 일원으로 포함된다. 이렇게 객체에 어떤 개념을 적용하는 것이 가능해서 개념 그룹의 일원이 될 때, 객체를 그 개념의 인스턴스(instance)라고 한다. 즉, 나는(프론트엔드 개발자임)은 프론트엔드 개발자라는 개념 그룹의 인스턴스인 것이다.
그리고 개념에는 심볼(Symbol), 내연(intension), 외연(extension) 이라는 세 가지 관점을 함께 언급한다. 심볼은 개념을 가리키는 간략한 이름이나 명칭으로, 이 예제에서는 프론트엔드 개발자 또는 백엔드 개발자가 된다. 내연은 개념의 완전한 정의를 나타내며 내연의 의미를 이용해 객체가 개념에 속하는지 여부를 확인할 수 있다.
쿼리나 api를 짜고 있으면 백엔드일 것이고, 뷰단에서 UI/UX와 JSON 상하차하고 있으면 프론트일 것이다. 백엔드가 React를 사용하고 있진 않으므로 내연을 만족시키지 못해 프론트엔드가 될 수 없는 것이다. 마지막으로 외연은 개념에 속하는 모든 객체의 집합(set)이다. 백엔드팀의 외연을 구성하는 객체의 집합은 백엔드 팀의 개발자들일 것이다.
이렇게 개념을 통해 공통점을 가진 객체들을 분류해서 하나의 회사를 만들었다. 그런데 갑자기 풀스택 개발자가 회사에 입사했다. 그러면 이 사람은 어떻게 분류를 지어야 할까? 객체를 적절한 개념에 따라 분류하지 않고 많은 상황을 고려하지 않은 경우 이와 같은 문제점이 발생한다. 즉, 객체를 적절하게 분류하는 것은 객체지향의 품질을 결정 하므로 주의하자.
개념을 이용해 공통점을 가진 객체들을 분류할 수 있다는 아이디어는 객체지향 패러다임이 복잡성을 극복하는 데 사용하는 가장 기본적인 인지 수단이라는 사실을 명심하자.
추상화는 두 가지 차원에서 이루어진다. 첫 번째는 구체적인 사물 간의 공통점은 취하고 차이점은 버리는 일반화를 통해 단순화한다. 두 번째는 차원은 중요한 부분을 강조하기 위해 불필요한 세부 사항을 제거해 단순화 한다. 위에서 알아본 개념을 통해 객체를 분류하는 과정은 추상화의 두 가지 차원을 모두 사용한다.
회사의 사람들을 프론트엔드 개발자와 백엔드 개발자라는 개념으로 묶은 것은 개별 객체 간의 차이점은 무시하고 공통점을 취하여 일반화를 적용한 것이다. 그리고 개발을 한다는 것 외에 손목의 건강을 위해 버티컬 마우스를 쓴다던가의 사항들은 중요하지 않다ㅠㅠ. 차상화의 두 번째 차원에 따라 불필요한 세부 사항이 제거되었다고 보자..
이제부터 위에서 개념이라고 언급했던 것을 개발자 답게 타입이라고 하자. 그런데 잠깐.. 타입이라고 하면 가장 먼저 생각나는 것은 데이터 타입이다. age: number
와 같은 것 말이다.
객체도 타입에 따라 분류하고 그 타입에 이름을 붙여 프로그램에서 사용할 새로운 데이터 타입을 선언하는 것과 같다. 그렇다고 객체가 데이터라는 말은 아니다. 객체에서 중요한 것은 객체의 행동이라는 것을 명심하자.
객체가 어떤 행동을 하느냐에 따라 객체의 타입이 결정된다.
객체의 타입은 객체의 내부 표현과 아무런 상관이 없다.
이 특징은 곧 이 말과 같다. 동일한 타입에 속한 객체는 내부의 데이터 표현 방식이 다르더라도 동일한 메세지를 수신하고 이를 처리할 수 있다. 다만 내부의 표현 방식이 다르기 때문에 동일한 메시지를 처리하는 방식은 서로 다를 수 밖에 없다. 이것은 다형성을 의미한다. 데이터의 내부 표현 방식과 무관하게 행동만이 고려 대상이라는 사실은 외부에 데이터를 감춰야 한다는 것을 의미한다. 이것은 캡슐화이다.
행동에 따라 객체를 분류하기 위해서는 객체가 내부적으로 관리해야 하는 데이터가 아니라 객체가 외부에 제공해야 하는 행동을 먼저 생각해야 한다. 이를 위해서는 객체가 외부에 제공해야 하는 책임을 먼저 결정하고 그 책임을 수행하는 데 적합한 데이터를 나중에 결정한 후, 데이터를 책임을 수행하는 데 필요한 외부 인터페이스 뒤로 캡슐화 해야한다. 이것이 책임 주도 설계이다.
망치로 한대 얻어 맞은 것처럼 이 말들이 이해가 안간다면 곱씹어 생각해보자.
세상에는 정말 많은 직무의 개발자가 있다. 이 글을 보는 우리들은 서로 매우 다르다는 것을 알지만, 이 분야를 잘 모르는 사람들은 그냥 "개발자"로 퉁 친다. 프론트엔드 개발자, 백엔드 개발자, 유니티 개발자등 해당 타입에 속한 객체는 개발자 타입의 객체에도 함께 속한다는 것을 의미한다. 이 관점에서 개발자는 프론트엔드 개발자를 포괄하는 좀 더 일반적인 개념이 된다. 이것을 일반화라고 한다. 그리고 여기서 프론트엔드 개발자는 좀 더 특화된 행동을 하는 특수한 개념이라는 의미로 특수화라고 한다. 특수한 타입은 일반적인 타입이 할 수 있는 모든 행동을 동일하게 수행할 수 있어야 한다.
주의해야할 점은 어떤 객체가 다른 객체보다 더 일반적인 상태를 표현하거나 더 특수한 상태를 표현한다고 해서 두 객체가 속하는 타입 간에 일반화/특수화 관계가 성립되는 것은 아니다. 일반화/특수화 관계를 결정하는 것은 객체의 상태를 표현하는 데이터가 아닌 행동 이다.
개발자는 컴퓨터를 키고, 코딩을 할 수 있어야 한다. 특수화 관계인 프론트엔드 개발자는 개발자 도구도 잘 확인할 수 있어야 한다. 이거 말고도 프론트엔드 개발자로써 갖춰야 할 스킬들이 있을 것이다. 여기서 한 가지 알 수 있는 사실은 특수한 타입은 일반적인 타입보다 더 많은 수의 행동을 가진다는 것이다.
일반적인 타입은 특수한 타입보다 외연의 크기는 더 크도 행동의 수는 더 적다.
슈퍼타입과 서브타입이란 것도 있으니 궁금하면 찾아보자.
회사에서 웹 서비스를 하나 만들거나 커다란 기능이 추가된다고 가정해보자. 많은 과정들이 있겠지만, 간략하게 추려보면 기획 > 디자인 > 개발의 플로우를 가지게 된다. 기획과 디자인이 완성되면, 개발자가 이 프로젝트 개발 기간이 얼마나 걸릴지에 대한 산정을 해서 알려준 후에 기획자의 허락하에 개발을 시작한다고 가정하자. 이제 요청과 응답 플로우를 보자.
요구사항이 들어오면, 기획자는 기획을해서 그에 맞는 디자인을 디자이너에게 요청한다. 디자이너는 기획에 맞게 디자인을 하고, 개발자에게 요청한다. 개발자는 기획과 디자인을 꼼꼼하게 살펴보고 스토리포인트를 산정하여 응답한다. 개발자의 응답은 연쇄적으로 디자이너에 대한 기획자의 요청에 대한 응답이기도 하다. 이제 기획자는 개발자에게 개발할 것을 요청한다. 개발자는 웹 서비스를 개발해서 기획자에게 응답한다.
여기서 중요한 것은 여러 서비스를 만들거나 많은 기능들이 추가 되더라도 이 협력 관계를 변하지 않게 된다. 즉, 직무에 맞게 역할을 대체할 수 있는 기획자, 디자이너, 개발자 객체는 동일한 메시지를 이해할 수 있다는 것이다. 이렇게 역할이란 것은 객체지향 설계의 단순성(simplicity), 유연성(flexibility), 재사용성(reusability)을 뒷받침하는 핵심 개념이다.
이 책에서는 역할, 책임, 협력은 견고하고 유연한 객체지향 설계를 낳기 위한 중요한 토양이라고 소개하고 있다. 이렇게 계속 강조하는 이유는 역할, 책임, 협력으로 객체지향을 보는 눈이 정말 중요하고 설계에 영향을 미치기 때문이다. 이 관점에서 애플리케이션을 설계하는 유용한 세 가지 기법이 있다. 책임 주도 설계, 디자인 패턴, 테스트 주도 개발.. 정말 많이 들어본 기법들이다. 이 기법들을 온전히 이해하기 위해서는 역할, 책임, 협력에 대해 알고 있어야 한다. 잊지 말자!
모든 책이 그렇겠지만, 객체지향의 사실과 오해라는 책은 여러 번 읽을수록 더 깨닫는 것이 많다. 처음에는 이해했다고 생각했지만 다시 한 번 보면 또 다른 것을 이해하고 있는 나를 보면.. 이해한 척이 아니었을까? 그래서 내가 경력이 쌓이고 3년차 이상이 되면, 그 때 또 읽어볼 예정이다. 현재와는 또 다르게 많은 것을 공감하고 이해하지 않을까 싶다.
제목 보고 지하철 노선도를 추상화 하는 내용일 줄 알았는데.. 좀 더 객체 추상화에 대한 개념설명 이네요!