책을 읽게 된 계기
스쿼드에서 혼자 백엔드 개발을 맡아 예약 관련 에픽을 진행한 적이 있다. 큰 설계 방향은 같은 백엔드 챕터의 엔지니어들에게 의견을 구하며 정했다. 구체적인 클래스 설계까지 리뷰를 기다리면 진행이 지연될 수 있다고 판단해, 그 부분은 스스로 결정하고 구현부터 배포까지 진행했다.
지금도 당시 상황에서는 최선의 선택이었다고 생각한다. 다만 클래스 설계를 더 잘할 수 있었다면 어떤 선택을 했을지에 대한 아쉬움은 남았다. 내가 선택한 설계가 왜 이 상황에서 적절한지 스스로 설명할 수 있을 정도의 판단 기준과 자신감을 갖고 싶었다.
이런 고민을 이야기하다가 『객체지향의 사실과 오해』를 알게 됐다. 사내 도서 구매 지원으로 책을 구입해두었고, 같은 스쿼드의 프론트엔드 엔지니어가 함께 스터디하자고 제안해 읽기 시작했다. 스터디 목표에는 “책을 다 읽었을 때, 나만의 주관이 있었으면 좋겠다”고 적었다.
읽기 전 누군가 추상화가 무엇인지 물었다면, 현실 세계의 무언가를 클래스로 잘 구현하는 일이라고 답했을 것 같다. 책에서는 객체가 어떤 목적을 위해 협력하고, 그 안에서 무슨 책임을 맡는지부터 생각하게 했다. 특히 추상화를 설명하는 부분을 내 설계와 일하는 방식에 연결해볼 수 있었다.
지하철 노선도와 추상화
3장에서 추상화를 설명하는 지하철 노선도가 좋은 비유라고 느꼈다. 승객이 노선도를 보는 이유는 목적지까지 어떤 노선을 타고 어디서 갈아탈지 알기 위해서다. 역의 정확한 위치와 실제 거리를 모두 표현하면 오히려 필요한 연결 관계를 알아보기 어려워질 수 있다.
역 사이를 걸어가려는 사람에게는 실제 거리가 중요하다. 환승 경로를 찾는 사람은 노선의 연결 관계부터 살핀다. 무엇을 표현할지는 그 표현을 사용하는 목적에 따라 달라진다.
책은 추상화를 공통점을 기준으로 대상을 묶는 과정과 불필요한 세부 사항을 생략하는 과정으로 설명한다. 앨리스가 여러 등장인물을 트럼프라는 개념으로 바라보는 장면도 그렇다. 개별 인물의 차이를 모두 기억하는 대신 공통된 특징으로 묶어 생각하면, 한 번에 이해해야 할 대상이 줄어든다.
어떤 공통점으로 묶을지도 목적에 따라 정해야 한다. 여러 대상을 더 넓은 개념으로 묶더라도, 지금 해결하려는 문제에 도움이 되는지 살펴봐야겠다.
나도 지나치게 많은 것을 하나로 묶으려 했던 적이 있다. 공통점을 찾는 데 집중하다 보면 각 경우에 필요한 차이를 충분히 보지 못하기 쉽다. 앞으로 공통 메서드나 인터페이스를 만들 때는 다음을 확인해보려 한다.
- 사용하는 쪽이 하려는 일과 기대하는 결과가 같은가?
- 함께 묶었을 때도 각 경우에 필요한 동작을 설명할 수 있는가?
- 내부에서 처리할 차이와 사용하는 쪽이 알아야 할 차이를 구분했는가?
예를 들어 같은 요청을 처리하더라도 한 경우에는 확정되고 다른 경우에는 대기 상태가 된다면, 그 결과를 안내해야 하는 쪽에는 차이가 드러나야 한다. 처리 과정의 세부 사항은 감출 수 있어도, 다음 행동을 결정하는 데 필요한 결과까지 감추면 사용하기 어려워진다.
설계 문서의 범위
이 설명을 읽으며 설계 문서를 작성했던 경험이 떠올랐다. 처음에는 내용을 많이 제공할수록 리뷰어가 판단할 재료도 많아지고, 그만큼 더 좋은 리뷰를 받을 수 있다고 생각했다. 내가 검토한 내용과 궁금해서 살펴본 내용까지 문서에 담았다.
그런데 리뷰어가 모든 내용을 같은 깊이로 읽을 수는 없었다. 배경을 폭넓게 적어놓아도 정작 내가 어느 부분에 대해 의견을 구하는지 드러나지 않으면, 무엇부터 살펴봐야 하는지 판단하는 데 시간이 든다.
문서를 다시 정리하면서 내가 리뷰받고 싶은 부분을 먼저 정했다. 백엔드 관점에서 검토할 핵심 흐름과 판단에 필요한 맥락을 남기고, 그 범위를 벗어나는 구현 설명은 덜어냈다. 토론할 범위가 좁아지니 해당 설계에 대해 더 깊이 있는 리뷰를 받을 수 있었다.
지하철 노선도에서 환승에 필요한 정보를 남기듯, 문서에도 리뷰어가 설계를 이해하고 판단하는 데 필요한 정보를 남겼다. 충분한 맥락은 제공하되, 내가 고민한 모든 과정을 똑같이 따라오게 할 필요는 없었다.
이후에는 문서의 목적과 읽는 사람의 관점을 함께 생각하게 됐다. 상대가 어떤 역할을 맡고 있고 무엇을 판단해야 하는지 살펴보면, 문서에 넣을 정보도 고르기 수월하다. 앞으로도 리뷰를 요청할 때는 의견을 구하는 지점을 먼저 적고, 그 지점을 검토하는 데 도움이 되는 내용을 중심으로 문서를 구성하려 한다.
행동과 상태
2장의 행동과 상태, 3장의 타입에 관한 설명도 설계 순서를 돌아보게 했다. 책은 객체가 어떤 행동을 해야 하는지 먼저 살피고, 그 행동에 필요한 상태를 찾도록 설명한다. 타입을 구분할 때도 내부에 어떤 데이터를 가지고 있는지보다 어떤 행동을 제공할 수 있는지에 주목한다.
나는 이 설명을 객체 사이에서 무엇을 알아야 하고 무엇은 몰라도 되는지 구분하는 데 연결해봤다. 다른 객체에 어떤 일을 요청할 수 있는지 알면 협력할 수 있어야 한다. 그 요청을 처리하기 위해 내부에서 어떤 데이터를 어떻게 관리하는지는 책임을 맡은 객체가 결정할 수 있다.
취업 전 사이드 프로젝트에서는 항상 데이터와 그 관계를 나타내는 ERD(Entity Relationship Diagram)를 그리며 데이터베이스 설계부터 시작했다. 어떤 테이블과 필드가 필요한지 정한 다음 기능을 구현하는 방식이었다. 지금 다시 그 프로젝트를 시작한다면, 필요한 기능과 사용자 시나리오를 먼저 정하고 그 과정에서 어떤 요청과 책임이 필요한지 살펴볼 것 같다. 그다음 클래스와 데이터 구조를 정하고 ERD를 구체화해보고 싶다.
책의 설명이 상당히 추상적으로 느껴질 때도 있었다. 행동에 따라 타입을 구분한다는 내용을 내 설계에 대입해보니 이미 그렇게 하고 있던 부분도 있었다. 익숙하게 하던 설계를 책의 개념으로 설명해볼 수 있었다. 앞으로는 어떤 필드를 넣을지 고민하기 전에 이 객체가 누구의 요청을 받고 어떤 행동을 해야 하는지부터 적어보려 한다.
3장에서 타입을 정적 모델과 연결하는 설명도 같은 맥락으로 이해했다. 앨리스의 키가 달라지는 순간들을 하나씩 따라갈 수도 있지만, 타입을 통해 어떤 상태와 행동이 가능한지 생각할 수도 있다. 실행 중인 객체의 구체적인 값과 그 객체를 설명하는 모델을 구분하면, 매번 모든 실행 과정을 떠올리지 않고도 설계를 이야기할 수 있다.
인터페이스와 구현
4장의 재판 이야기에서는 등장인물들을 판사와 증인이라는 역할로 바라본다. 누가 증인인지는 달라져도 증언할 책임을 수행할 수 있다면 그 역할을 맡을 수 있다. 협력하는 쪽에서는 특정 인물의 모든 특징보다 요청한 책임을 수행할 수 있는지가 중요하다.
5장의 책임과 메시지에서는 이 관점이 객체의 자율성으로 이어진다. 증언이라는 일을 요청하면서 구체적으로 어떻게 기억을 떠올리고 설명할지까지 모두 지시하지는 않는다. 요청받은 객체가 수행 방법을 선택할 수 있어야 한다. 반대로 책임을 너무 모호하게 표현하면 무엇을 해야 하는지 알 수 없으므로, 협력에 필요한 의도는 분명해야 한다.
메시지와 메서드를 구분하는 것도 이 경계를 이해하는 데 도움이 된다. 메시지는 상대에게 보내는 요청이고, 메서드는 그 요청을 수행하는 구현이다. 사용하는 쪽이 기대하는 행동과 그 행동을 수행하는 방법을 나눠서 생각할 수 있다.
내 경험에서는 데이터 저장과 조회 기능을 정의한 Repository 인터페이스와 이를 구현한 RepositoryImpl이 떠올랐다. 인터페이스를 보고 사용하는 데서 끝나지 않고 구현체까지 확인해야 했던 경우가 있었다.
이 둘을 나누는 데에는 모듈이나 계층 사이의 의존성을 분리하려는 목적이 있다. 호출하는 쪽이 구체적인 구현체에 직접 의존하지 않도록 하는 분리는 그 자체로 의미가 있다. 여기에 더해, 사용하는 쪽이 알아야 할 동작이 인터페이스나 명세에 충분히 드러나 있는지도 돌아보고 싶다. 파일과 클래스를 나누고 나서 호출하는 쪽이 알아야 할 내용도 줄었는지 살펴보려 한다.
반대로 자바의 자료구조는 내부 구현을 매번 확인하지 않고도 사용했다. ArrayList와 LinkedList는 서로 다른 방식으로 데이터를 저장하지만, List가 제공하는 동작을 이해하면 원소를 추가하고 조회하는 코드를 작성할 수 있다. 물론 구현체를 선택하거나 성능을 검토할 때는 차이를 알아야 한다. 사용하는 모든 순간에 내부 동작 전체를 따라갈 필요가 줄어든다는 점이 내가 생각하는 추상화의 이점이다.
앞으로 인터페이스를 설계할 때는 어떤 입력을 받아 무엇을 돌려주는지, 실패할 수 있는 조건은 무엇인지 설명해보려 한다. 그리고 사용하는 쪽이 그 설명만으로 자신의 일을 할 수 있는지 확인하고 싶다. 내부 구현을 알아야만 판단할 수 있는 부분이 남아 있다면, 외부에 드러낼 약속이 빠진 것은 없는지 살펴볼 수 있을 것이다.
프론트엔드에서의 추상화
프론트엔드 엔지니어와 함께 읽으면서 이 책을 백엔드의 클래스 설계에만 한정해서 볼 필요가 없다는 생각도 했다. 책이 강조하는 역할, 책임, 협력은 클래스를 어떤 문법으로 작성할지에 앞서 생각할 수 있는 내용이다.
프론트엔드에는 화면을 어떻게 보여줄지, 사용자 입력과 데이터를 어떻게 관리할지, API를 언제 호출하고 응답을 어떻게 처리할지 등 여러 관심사가 함께 있다. 하나의 기능에서도 화면 표현, 데이터 처리, 외부 통신에 각각 어떤 책임을 맡길지 정해야 한다. 각 부분이 필요한 정보를 주고받되, 서로의 내부 처리까지 알아야 하는 범위는 줄이고 싶다.
React에서 상태를 다루는 로직을 분리해 재사용하는 커스텀 훅을 예로 들어볼 수 있다. 예약 목록을 조회하는 useReservations 훅을 만든다면, 컴포넌트에는 목록 데이터와 로딩 여부, 오류, 다시 조회하는 함수를 제공할 수 있다. 컴포넌트는 이 정보로 어떤 화면을 보여줄지 결정하고, 훅은 별도의 API 호출 함수를 이용해 조회 과정과 상태를 관리하도록 구성할 수 있다. 화면에서는 요청을 어떤 방식으로 보내고 응답을 어떻게 변환하는지 매번 알 필요가 줄어든다.
다만 관련 코드를 전부 훅 하나로 옮기기 전에 그 훅이 맡을 일을 정해야겠다. 데이터를 조회하는 책임에 화면 배치나 사용자 입력 처리까지 계속 더해진다면, 사용하는 쪽에서도 알아야 할 내용이 다시 많아질 수 있다. 컴포넌트와 훅을 나눌 때도 클래스 설계에서처럼 어떤 일을 맡기고 무엇을 외부에 드러낼지 생각해야 한다.
읽기 전에는 추상화를 현실의 무언가를 클래스로 구현하는 일로 좁게 생각했다. 프론트엔드의 화면과 데이터 흐름에도 같은 질문을 적용해보니, 클래스를 만드는 일보다 넓게 이해할 수 있었다.
책을 읽으며 느낀 점
이 책에서 좋았던 점은 추상화나 책임처럼 말로만 들으면 막연한 개념을 구체적인 장면으로 설명한다는 것이다. 지하철 노선도에서는 목적에 따라 정보를 생략하는 이유를, 재판 이야기에서는 역할을 맡은 대상이 달라져도 협력이 이어질 수 있다는 점을 이해할 수 있었다. 개념의 정의를 읽은 뒤에도 그 장면을 떠올리며 의미를 되짚어볼 수 있었다.
다만 비유를 이해하고 나서 내 코드에 적용하는 데에는 생각이 더 필요했다. 증인에게 증언을 맡긴다는 설명에는 쉽게 수긍해도, 내가 만든 클래스에 어디까지 책임을 맡길지는 별도로 판단해야 했다. 책의 설명이 추상적으로 느껴졌던 것도 이 지점과 연결된다. 읽으면서 내가 작성했던 코드나 설계를 함께 떠올려보는 과정이 필요했다.
행동에 따라 타입을 구분하는 부분에서는 이미 하고 있던 설계도 발견했다. 그동안의 선택을 책의 개념으로 설명해보면서, 내가 왜 그렇게 나눴는지 돌아볼 수 있었다. 클래스 설계를 더 잘하고 싶어 읽기 시작한 내게는 이 점이 도움이 됐다. 설계를 검토할 때 무엇을 질문하고 어떤 이유로 판단했는지 이야기할 수 있게 됐기 때문이다.
나에게 추상화란
지금 좋은 객체지향 설계를 설명해보라고 하면, 우선 객체의 책임을 잘 설정해야 한다는 말이 떠오른다. 그 책임이 왜 적절한지도 협력의 목적과 필요한 행동을 바탕으로 설명할 수 있다. 어떤 일을 맡겼는데 호출하는 쪽에서 처리 순서나 내부 상태까지 챙겨야 한다면, 책임을 충분히 맡긴 것인지 다시 살펴볼 것 같다.
처음 책을 읽으며 원했던 ‘나만의 주관’도 이런 판단 기준에 가깝다. 왜 이 객체에 일을 맡겼는지, 사용하는 쪽은 무엇까지 알아야 하는지 설명할 수 있으면 내 설계를 다른 사람과 함께 검토하기도 수월하다.
지금은 추상화를 생각할 때 무엇을 위해 쓰는지, 쓰는 사람이 무엇을 알아야 하는지부터 떠올린다. 객체를 사용하는 쪽에는 필요한 행동과 그 결과가 드러나야 하고, 설계 문서를 읽는 사람에게는 판단에 필요한 맥락이 있어야 한다. 문서에서 리뷰받을 범위를 좁혔던 경험을 돌아보니, 추상화는 코드를 설계할 때뿐만 아니라 일을 하고 다른 사람과 이야기할 때도 필요한 능력이라고 느낀다.
정보를 덜어낼 때도 상대가 판단하는 데 필요한 내용까지 빠뜨리지 않았는지 살펴봐야겠다. 같은 코드나 문서라도 쓰는 목적이 달라지면 드러내야 할 내용도 달라질 수 있다.
앞으로 객체를 설계할 때는 “이 객체가 할 수 있는 행동은 무엇인가?”를 먼저 물어보려 한다. 문서를 작성할 때는 이 문서를 읽는 사람이 무엇을 이해하고 판단해야 하는지 적어보려 한다. 코드와 문서에서 이 질문들을 반복하며 지금의 판단 기준을 더 다듬어가고 싶다.
