객체 지향과 데이터 지향 프로그래밍
이야기에 앞서서, "데이터 중심 설계"와 "데이터 지향 프로그래밍"은 "설계"와 "프로그래밍"(구현)의 관점에서 둘은 서로 다를 수 있지만, 어느정도 결은 같이한다고 생각하고 작성하였다. 혹시라도 내가 가정한 부분이 크나큰 오류를 범하고 있다면 언제든지 바로잡아주길 바란다.
"오브젝트"를 읽으면서 "데이터 중심 설계"의 문제점에 대해 이야기하는 부분이 있었는데, 최근 읽은 Brian Goetz의 Data Oriented Programming in Java 라는 아티클의 내용과 같이 이야기해보려고 한다.
"오브젝트" 책의 4장부터 5장에 걸쳐서 "데이터 중심 설계"의 문제점에 대해서 이야기하고 "데이터 중심 설계"에서 "책임 중심 설계"로 리팩토링하는 전반적인 과정에 대해서 다루고있다.
책에서 언급하는 "데이터 중심 설계"의 가장 큰 문제점은 변경에 취약하는 것이다.
- 데이터 중심 설계는 본질적으로 너무 이른 시기에 데이터에 관해 결정하도록 강요한다.
- 데이터 중심 설계에서는 협력이라는 문맥을 고려하지 않고 객체를 고립시킨채 오퍼레이션을 결정한다.
이 두가지 문제점에 대해서 좀 더 풀어서 설명하자면 다음과 같다.
객체의 행동보다는 상태에 초점을 맞춘다
- "데이터 중심 설계"를 시작할 때 던졌던 첫번째 질문은 "이 객체가 포함해야하는 데이터가 무엇인가?"이다.
- "데이터"는 구현의 일부로, 시작하는 처음부터 "데이터"에 관해 결정하도록 강요하기 때문에 너무 이른 시기에 내부 구현에 초점이 맞춰진다.
- 데이터 중심 관점에서는 객체는 그저 단순한 데이터의 집합체일 뿐이며 이는 과도한 접근자와 수정자를 추가하게만들고, 데이터 객체를 사용하는 절차를 분리된 별도의 객체 안에서 구현하게 된다.
- 접근자와 수정자는
public
속성과 큰 차이가 없기 때문에 캡슐화가 무너진다.
- 데이터를 먼저 결정하고 나중에 데이터를 처리하는데 필요한 오퍼레이션을 나중에 결정하는 방식은 데이터에 대한 지식이 객체의 인터페이스에 고스란히 드러난다.
- 결국 객체의 인터페이스는 구현을 캡슐화하는데 실패하고 코드는 변경에 취약해진다.
객체를 고립시킨채 오퍼레이션을 정의하도록 만든다
- 올바른 객체지향 설계의 무게중심은 항상 외부에 맞춰져있어야한다.
- 객체가 내부에 어떤 상태를 가지고 관리하는지는 부가적인 문제고 중요한 것은 다른 객체와 어떻게 협력하는가이다.
- 데이터 중심 설계의 초점은 객체 내부로 향한다.
- 실행 컨텍스트에 대한 깊이있는 고민 없이 객체가 관리할 데이터의 세부 정보를 먼저 결정한다.
- 객체의 구현이 이미 결정된 상태에서 다른 객체와의 협력 방법을 고민하기 때문에 이미 구현된 객체의 인터페이스를 억지로 끼워 맞출 수밖에 없다.
극단적인 치우침과 데이터 중심 설계
"데이터 중심 설계"의 문제점에 대해서는 깊이 공감하고있다.
- 소프트웨어 세계는 끊임없이 변한다. 그렇기 때문에 확장가능하고 변경에 유연하게 대처할 수 있는 설계의 중요성에 대해서는 공부하면서 귀에 못이 박히도록 들은 것 같다.
- 뿐만 아니라 회사 프로젝트의 설계 및 코드 베이스가 데이터 중심이고 이로 인해서 책에서 언급한 여러가지 고충들을 실제로 몸소 경험하고 있기 때문에 공감가는 부분이 많았다.
그렇기 때문에 나 스스로도 "데이터 중심 설계"를 안티 패턴이나 구시대의 유물쯤으로 생각하고 있었는데, Brian Goetz 의 글을 읽고는 한번 더 지식의 극단적인 치우침이 얼마나 위험한지 생각하게 되었다.
Brian Goetz 가 말하는 "데이터 중심 프로그래밍"이란 무엇일까? 이야기에 앞서서 결론부터 말하자면 프레드 브룩스의 논문의 구문처럼 "소프트웨어 개발의 복잡성을 한번에 해소할 마법같은 솔루션(은탄환)은 없다"이다.
Java에서의 데이터 중심 프로그래밍
Brian Goetz가 작성한 "Java에서의 데이터 중심 프로그래밍"은 "데이터 중심 설계"와 비교했을때, "설계"에서 "프로그래밍"으로 이어지는 흐름상 같은 기조를 가지고 있다고 판단했다. (이로인한 논리적 오류나 잘못된 판단을하고 있다면 정정해주길 바란다)
아티클 전반적인 내용에 대해 설명하지는 않고 Brian Goetz가 작성한 아티클의 내용을 일부 요약하려고한다.
- OOP는 복잡하고 거대한 애플리케이션을 작은 경계를 가진 모듈들로 나누어 복잡성을 효과적으로 제어할 수 있지만, 잘못 수행된 OOP는 끔찍하며, 많은 사람들이 극단적인 OOP의 원칙에 노출되어있다.
- 즉, OOP가 어떠한 상황에서도 만능이라고 생각하는 사람들에 의해 잘못 사용되고있다.
- 현대의 프로그래밍 패러다임은 거대한 모놀리식 프로그램에서 점차 많은 소규모 서비스를 통해 거대한 애플리케이션을 구성하는 마이크로 서비스 형태로 변화하고있다.
- 소규모 서비스에서는 내부 경계가 덜 필요하다. (충분히 작은 서비스는 단일 팀 또는 개발자에 의해서 유지보수 될 수 있음)
- 복잡한 엔티티를 모델링하거나 Java의 Stream API같이 풍부한 라이브러리를 작성할 때 객체 지향은 많은 것을 제공한다.
- 그러나 단순하고, 임시의 데이터를 처리하는 간단한 서비스를 구현할 떄는 데이터 지향 프로그래밍이 좀 더 나은 방법이 될 수 있다.
- 객체 지향과 데이터 지향은 결코 상충되는 개념이 아니다 우리는 좀 더 적절하다고 생각하는 곳에 적합한 도구를 사용하면 된다.
- Java에서는 이러한 데이터 지향 프로그래밍을 지원하기 위해서
Record
, Sealed
, 패턴 매칭 등의 다양한 기능을 제공한다.
- 이러한 독립적인 개념들은 상호 보조적으로 동작하며 이러한 도구들을 조합해서 사용하면 좀 더 쉽게 유효하지 않은 상태를 표현하는 것을 방지할 수 있다.
- 이를 통해 코드의 안전성과 유지보수성을 향상시킬 수 있을 것이다.
사실상 "데이터 지향 프로그래밍"을 옹호하는 입장이라기 보다는 객체 지향이든 데이터 지향이든 적합하다고 생각하는 곳에 적절한 도구를 사용해야한다는 말을 하면서, 데이터 지향 프로그래밍을 할 때 좀 더 효과적으로 프로그래밍 할 수 있는 Java의 여러가지 도구들을 소개하는글에 가까운 것 같다.
결론을 지어보자면 "데이터 지향 프로그래밍(설계)"이 틀렸다, "객체 지향 프로그래밍(설계)"이 항상 옳다는 잘못된 말인 것 같다. 특정 사상이나 기술에 매몰되는 것은 위험하다. 상황이나 가치 판단의 기준을 흐리게하고 편협한 사고, 좁은 시야에 개발자를 가두고만다.
개발자에게 있어서 좀 더 중요한 것은 Brian Goetz 의 말대로 적재적소에 좀 더 적절한 도구를 선택해서 주어진 "문제"를 해결하는 것이 아닐까 생각이든다. (그렇다고해서 기술이나 사상이 덜 중요하다는 이야기는 아니다)
프레드 브룩스의 논문의 구문을 한번더 언급하면서 끝맺으려고한다. "소프트웨어 개발의 복잡성을 한번에 해소할 마법같은 솔루션(은탄환)은 없다"