본문 바로가기
Study/객체지향 시스템 디자인 원칙

[객체지향 시스템 디자인 원칙] 내 생각 Q&A - 1장. 모든 게 복잡도 관리다

by Nahwasa 2025. 7. 16.

목차

     

      이 글은 ['객체지향 시스템 디자인 원칙'을 읽으며 정리한 내 생각 - LLM과의 Q&A 기반 사고 확장] 글을 챕터별로 나눈 시리즈 중 하나 입니다. 전체 흐름을 보고 싶거나, 다른 챕터를 확인하고 싶으시다면 위 통합글을 참고해주세요.

     

      이 글은 책 내용을 요약하거나 정리한 글이 아닙니다. 책을 읽고 난 뒤 제 생각을 중심으로 정리한 글 입니다. '객체지향 시스템 디자인 원칙'을 읽으며, 느낀점이나 좋았던 점 및 책에 제시된 연습문제를 LLM에게 알려줬습니다. 이를 바탕으로 제 생각을 기반으로 책의 연습문제를 변형하거나, 새로운 질문을 생성하게 했습니다. 그 후 제가 그 질문에 답하고, LLM과 토론하는 방식을 사용해 사고를 확장해봤습니다. 이 글은 그 질문과 답변을 모아둔 글 입니다. 자세한 작성 배경은 통합글에서 확인하실 수 있습니다.

     

     Q1, Q2와 같은 질문 외의 모든 답변은 LLM을 쓰지 않고 제가 직접 답변한 내용입니다. 이후 LLM과 토론하며 생각이 조금씩 바뀐 부분들도 있으나, 최대한 초안 답변으로 올렸습니다.

     제 생각을 적은 것으로, 일반적인 개발자들의 생각이나 사실과 다소 다를 수 있습니다.

     

     


     

     

    1장. 모든 게 복잡도 관리다

    Q1. '복잡성'의 정체?

    단순한 객체지향 디자인은 '복잡도 관리'라는 렌즈로 보면, 세영이 생각하는 ‘복잡성’의 정체는 뭐야?

    • 시간에 따라 변화하는 요구사항
    • 조직 구조에 따라 바뀌는 책임 분리
    • 혹은 인프라, 프레임워크, 정책 등 외부 요소와의 연결

    ‘복잡성’이 실제 코드에 끼친 가장 강력한 흔적은 어떤 형태로 남았었는지 떠오르는 사례가 있다면?

     

    내 답변

      복잡성에는 내부 절차가 복잡하다, 초기에 너무 과도하게 설계해두었다 등 여러 외부 요인이 있을 수 있을거야. 어쨌든 그런 외부 요인은 빼고 현재 잘 동작하는 시스템을 기준으로, 복잡성은 요구사항의 변화에서 출발한다고 생각해. 요구사항은 외부로 보면 기능 추가에 대한 요청사항, 성능 개선에 대한 요청, 기능 수정, 타겟층 추가, UI 개선 같은게 있을 수 있지. 내부로 보면 프레임워크 버전업을 위한 마이그레이션, 이후 요구사항을 더 유연하게 적용하기 위한 구조 변경, 초기 사용량 예측 실패로 인해 최적화 예상 실패한 부분에 대한 최적화 진행, 보안을 위해 토큰 구조 변경, 테스트 커버리지 높히기, 외부 API가 버전 변경되면서 API 요청, 결과 파라미터가 바뀜 등이 있을 수 있겠지.

     

      물론 조직 구조에 따라 역할과 책임이 변할 순 있겠지만, 근본적으로는 대외적인 요구사항 혹은 팀 내부에서 개선을 위한 요구사항 등 아무튼 요구사항이 발생할 때 복잡성이 발생할 수 있다고 봐. 그리고 이 요구사항에 얼마나 유연하게 대응할 수 있는지가 설계의 핵심이고.

     

      복잡성이 코드에 끼친 사례를 생각해볼께. 나 개발자 극초반에 웹쪽을 처음 해보는 구간이 있었단말야. js, css, 스프링부트, html 전부 처음 해보는 구간이 당연히 있었을꺼잖아. 그 때, js에도 객체지향 설계 개념을 넣고 싶었어. 뭐 지금 생각해도 괜찮은 생각이긴 한데, 너무 과도하게 넣었어 ㅋㅋ. 현재 고객이 보고있는 지도 화면을 캡쳐한 후에 고객이 요청한 전송 방식(MMS, 메일, 팩스 등)으로 보내주는 거였거든. 해당 프로젝트의 js 코드 안에 class로 구성된게 하나도 없음에도 js에 class를 추가했어. 그리고 이후 유지보수는 생각 안하고, 해당 class를 깔끔하게 쓰면 된다는 생각으로 ‘전송 방식’ 부분을 비트마스킹으로 처리했어. 이게 자바이고, MSA 같은거였으면 사실 내부 구현을 캡슐화하는게 문제는 아니지. 근데 js이고, 이후에 전송 방식이 추가되면 유지보수 하는 사람이 수정해야 하는 부분이란 말야. 그 전송하는 부분을 하나의 ‘모듈’로 잡고 모듈의 사용성만 생각해서 비트마스킹으로 처리해버린거지. 애초에 코드의 일관성 없게 class를 쓴 것 자체도 문제였고 ㅋㅋ. 그거에 연결되어 백엔드에서 받는 부분도 굳이 스프링 환경임에도 POJO하게 막 Singletone으로 된 매니저 클래스로 전송 모듈 관리하고 그랬어. 당연히 실제로 이후 유지보수하는 곳에서는 힘들어했지.

     

      사실상 사회생활 첫 프로젝트였고, 당시 파견 느낌의 회사라서 딱히 날 컨트롤해줄 사수도 없었다보니 막을 사람도 없던거지 ㅋㅋㅋ. 그래서 ‘왜 이렇게 코드를 짰을까? 제 코드의 선택과 이유들’ 이라는 세미나를 할 때, 현재와 다른 회사임에도 맨 처음 설명한 코드도 거기서 짠 코드의 레플리카였어. 자기반성용이지. 결국 유지보수를 생각하고 만든게 아니라, 내가 만든 구조의 품질 자체를 우선시한거였어.

     

     

    Q2. ‘단순한 코드 / 좋은 추상화 / 좋은 모듈화’의 차이점?

    여섯 가지 항목 중 ‘단순한 코드 / 좋은 추상화 / 좋은 모듈화’는 겉으로 비슷해 보여.

    이 셋의 차이점을 세영의 언어로 다시 정의해보면 어떻게 구분돼?

    (예: 추상화는 개념 정리, 모듈화는 책임 정리, 단순한 코드는 구현 정리…)

    그 기준으로 봤을 때, 세영이 가장 약하다고 느끼는 부분은?

     

    내 답변

      단순한 코드는 ‘동일한 INPUT에 동일한 OUTPUT을 내면서도, 더 짧고 가독성 높은 코드’ 라고 생각해. 좋은 코드는 책처럼 술술 읽혀야 한다는 얘기가 있지. 그 외에 중복 제거 같은 부분도 필요하겠지만, 사실 if문 같은 분기가 없다면 보통의 코드가 몇백 줄 수준이 아니라면 읽기 어려운건 아니야. 그래서 난 단기 기억력의 부담을 최대한 줄여주는데 초점을 맞추는게 좋다고 봐. 또, 짧다고 무조건 좋은것도 아니겠지. if (a > b) return a; else return b; 이것보다 return a>b ? a:b; 가 짧긴한데 가독성이 꼭 좋다고 볼 순 없거든. 오히려 내가 선호하는건 if (a ≤ b) return b; return a; 이거긴 해. if 등 블록 depth가 늘어나는 부분에서 컨텍스트 스위칭 된 개발자의 단기 기억력을 최대한 빠르게 free 시켜주는게 중요하다고 보거든.

     

      좋은 추상화는 단순히 abstract, interface 관점에서 뭐 SOLID 따져서 ISP를 위해 인터페이스를 만들고.. OCP를 지키기 위해 Template Method 패턴을 쓰려고 abstract 만들고 이런 관점에서도 볼 수 있겠지. 근데 내가 주로 쓰는 추상화라는 말은 어떠한 개념에 대한 추상화 수준 얘기야. 즉 복잡한 문제를 단순화해서 어느 수준에서 묶을꺼냐는 개념적인 얘기말야.

     

      이것도 사람의 단기 기억력에 대한 얘기긴 한데, 이 책에서 얘기한 피플그로우 시스템을 예시로 생각해볼께. 높은 추상화 수준이라면 프론트엔드, 백엔드, 외부시스템 등등으로 나눌 수 있겠지. 낮은 추상화 수준이라면 좀 더 디테일하게 들어가서 뭐 강의 엔터티가 존재하고, 그 강의들을 묶은 커리큘럼이 존재하고.. 이런 수준에서의 생각이겠지. 더 깊게 들어가면 강의 엔터티 내에 어떤 속성이 필요하고.. 그런 수준일테고. 코드에서의 interface, abstract 같은 부분은 결국 이런 개념적 추상화의 구현체라고 생각해. 좋은 추상화를 위해선 개념적인 추상화 수준에서 생각해봐야해.

     

      좋은 모듈화는 결국 결합도와 관련있다고 봐. 나도 항상 결합도를 떨어뜨리는 방향을 선호해. 역시 코드만의 얘기는 아니야. 처음 내가 파견업체로 개발자를 시작할 때, 업무 분담은 항상 수직적이었어. 무슨말이냐면, 어떠한 기능에 대해 A개발자는 1,2번 기능, B개발자는 3,4번 기능 이런식이지. 이 경우 해당 기능에 대해 프론트, 백엔드, DB 모두를 해당하는 사람이 짜야해.

     

      현재는 수평적으로 분리해. 난 프로젝트를 시작할 때 백엔드와 프론트 사이의 인터페이스를 먼저 만들어. 서로 어떻게 구현하는진 오히려 몰랐으면 해. 서로 인터페이스를 기준으로 각자 최선의 구조로 코드를 짜는거지. 좋은 모듈화도 이런 관점이야. 물론 모듈을 어떤 추상화 수준에서 분리할지는 다양한 요소가 고려되어야 해. 뭐 최대 500명 쓰는 내부 관리 시스템을 만드는데, 관리자용 모듈, 대리용 모듈, 과장용 모듈, … 이런식으로 나누는건 오히려 복잡성만 증가시키는거겠지. 적절한 추상화 수준에서 서로의 결합도를 떨어뜨릴 수 있게 모듈화 하는게 좋은 모듈화라 생각해. 또, 해당 서비스가 발전함에 따라 이 모듈화 수준은 물론 달라질 수 있다고 보고.

     

      내가 가장 약하다고 생각하는건 ‘단순한 코드’ 부분이야. 알고리즘 문제를 꽤 풀었다보니, 기본적으로 코드를 짤 때 해당 기능이 어느정도의 시간복잡도가 나올지 자동으로 계산하면서 짜게돼. 그렇다보니 복잡함 추가가 미미한 수준이라면, 소소한 최적화를 하는걸 좋아해. 예를들어 스트림 쓰면 코드가 단순해지는 부분이 있다고 해볼께. 근데 사실 자바 17 미만에서 스트림은 성능이 떨어지거든. 이 해당 부분의 최악 시간복잡도를 생각해보고, 스트림으로 할 시 기대 이하의 성능이 나올 것 같다면 스트림을 쓰지 않아. 어느정도 자제하긴 하는데, 아무튼 단순한 코드도 좋지만 기본적인 최적화도 아는데 안할순 없다보니 좀 약하다고 생각해.

     

     

    Q3. 시스템이 성장해도 디자인을 단순하게 유지할 수 있다고 보는 편이야?

    그걸 가능하게 하는 전제 조건이 있다면 어떤 게 있을까?

    • 팀 문화?
    • 코드리뷰 기준?
    • 아키텍처 설계 철학?

    그리고, 실제로 단순함을 유지하려다 실패한 경험이 있다면 공유해줘.

     

    내 답변

      운영 구조에 따라 좀 다를 것 같아. 사실 현재 내 구조라면 힘든편이야. 내 경우, 플랫폼 서비스를 만든 후에 하위 이용기관 한두군데 붙여서 실 사용에도 문제 없는지 확인해. 그 후 운영팀쪽으로 프로젝트를 넘기는 방식이야. 해당 프로젝트가 크다면 해당 프로젝트 전용 팀이 생기고, 좀 작다면 다른 운영팀 중 한곳으로 가고 그런 방식이야. 물론 커다란 기능 추가 건이 들어온다면 다시 우리팀으로 올 때도 있지만, 대부분의 경우 운영팀에서 처리하고 있어.

     

      그러니 경험상 많은건 아니지만, 중간중간 하위 이용기관이 늦게 붙거나 어디로 이관할지 정해지지 않아 좀 오래 들고 있을때도 있어. 또, 내부에서만 쓰는 프로젝트 같은 경우에도 오래 들고 있는 경우가 있어. 그걸 기준으로 생각해볼께.

     

      이 경우 역시 팀 문화가 가장 중요하다고 봐. 팀 분위기 자체가 다들 축 쳐져서 대충 일정이나 맞추자 분위기면 디자인을 단순하게 유지하려는 노력 자체가 이루어지기 힘들겠지. 반면에 못해도 팀의 20%정도만 이런 것에 관심이 있어도, 오히려 다수파쪽이 묻힌다고 생각해. 실제로도 팀원이 누구였냐에 따라서 분위기 차이가 많이 났던 것 같아. 즉, 단순하게 유지하고자 하는 사람이 공감대 형성할 수 있는 분위기여야해.

     

      예를들어 타팀에 한 직원이 ‘속도가 느리다’ 라는 평가를 받고 있었어. 근데 내가 볼 땐, 리팩토링에 투자를 엄청 해서 그랬던거거든. 즉, 운영 요구사항을 바로 처리하는게 아니라 이후로 발생하지 않도록 만드는 직원이었어. 근데 팀에선 인정못받는 것 같아 우리팀으로 올 수 있도록 처리했어. 그리고 역시나 엄청난 고수였지. 이 케이스는 해당 팀의 팀원들의 의식 문제라기 보다는, 그 팀을 이끄는 사람의 무지에서 나온 나쁜 평가를 받은 평가긴한데, 어쨌든 팀 문화에 따라 고수가 안좋은 평가를 받을 수도 있다는 예시야.

     

      단순함을 유지하려다 실패한 경험이라면, 역시 일정 때문인 경우가 많아. 당장 hotfix가 필요한데 '단순하게 유지'를 위해 시간을 많이 들여서 짜는건 오히려 안좋지. 물론 그 이후 TDD 책의 말 처럼 ‘죄악을 없애자’ 단계가 필요하긴 하겠지. 근데 어떨땐 너무 할게 많아서 도저히 시간을 못낼때도 있어. 그러다가 한두달 지나고, 운영팀에 넘어가고 그러다보면 사실 죄악을 없애지 못하는 경우가 생겨. TDD의 '죄악 없애기' 단계에 대한 예시는 'TDD, Mock, SOLID 얘기 - 도시 가스 요금 계산' 이 글에 약간 적어뒀어.

     

     

    Q4. 책의 시스템 요구사항을 봤을 때, 세영이 가장 먼저 고려할 '복잡성의 중십축'은 뭐야?

    피플그로우! 시스템 요구사항을 봤을 때, 이 시스템을 설계하면서 세영이 가장 먼저 고려할 “복잡성의 중심축”은 뭐야?

    • 교육 일정 관리?
    • 사용자 역할?
    • 외부 시스템 연동 (위키, 캘린더, 채팅)?
    • 관리자 UI?

    그리고 그걸 “단순하게 설계하려면 무엇을 가장 먼저 잘라내야 할까?”

     

    내 답변

      내가 책에 나온 피플그로우 시스템의 요구사항을 받고, 그걸 설계하는 입장이라 생각하며 어떤 흐름으로 설계할지 생각해볼께. 우선 난 end point를 기준으로, 내가 생각하기 편한 추상화 수준으로 나누는걸 좋아해. 책에 나온 요구사항은 큰 시스템은 아니니 아래 정도로만 생각을 나누면 될 것 같아. 사실 추상화 수준이 동일하다고 느끼지 않을 수도 있어. 그냥 내가 생각하기 편한 그룹이라고 보면 될 것 같아. 애초에 어떤걸 기준으로 추상화하냐에 따라서도 또 얘기가 달라지는거라 정답은 없다고 봐.

     

      비즈니스 로직, 피플그로우에서 시작해 접근하는 외부 시스템, 외부에서 시작해 피플그로우로 들어오는 외부 시스템, 관리자 UI, API 인터페이스, 클라우드 구성 등의 인프라.

     

      추상화 수준을 나누고나서 보통 비즈니스 로직같은건 오히려 익숙한 부분이기도 하고 이미 요구사항이 잘 정의되어 있으므로 제쳐두고 외부 시스템 학습 테스트부터 할 것 같아. 기술 검증이라고 하지. 외부 시스템을 써보면서 어떤식으로 써야하는지, 필요한건 뭔지 파악하는거지. 단순히 외부 시스템의 가이드 문서만 보고 판단하기 힘든 부분이 많거든. 예를들어 결제 관련 외부 시스템의 경우, 전용선이 필요한 경우도 있어. 또 어떤 곳은 가이드만 보면 문제 없었는데, 실제로 확인해보니 지원하지 않는 기능일수도 있어. 이런 부분은 내가 잘 알지 못하는 블랙박스에 가까워서, 우선 블랙박스부터 열어서 확인해보는거야.

     

      그 후에 비즈니스 로직을 설계해. 여기에서 사용자 역할, 강의, 커리큘럼(강의의 집합), 사용자, 관리자 등에 대한 전반적인 도메인 로직을 설계하겠지. 이 때는 DDD 설계 개념으로 접근하는걸 선호해. 현재 내가 가장 편하게 생각할 수 있는 방식이거든.

     

      그리고 비즈니스 로직과 기술검증 해본걸 기반으로 러프한 API를 정의해. 범위는 상황에 따라 다른데, 일반적으로 외부에서 시작해 피플그로우로 들어오는 경우라면 내가 API를 정의하고, 그걸 외부 시스템 개발자가 사용하는 방식이니 그 부분도 필요해. 보통은 백엔드 - 프론트엔드 사이의 API 인터페이스를 정의하지. 여기엔 HTTP STATUS에 따른 동작 방식, 프론트의 동작 방식도 어느정도 정의해두는 편이야. 예를들어 UI에 띄울 모든 에러메시지를 백엔드에서 정의하도록 하는 경우도 있어. 심지어 공통 에러페이지를 띄울지, 팝업으로 띄울지, 그 메시지는 뭐일지까지 정해주는 경우도 있어. 그 외에 인프라도 내게 역할이 있다면 생각해보겠지. 뭐 사용량이 높을 것 같다면 DB write, read 연산을 나눠볼수도 있겠고, 로드밸런서나 서버의 갯수도 생각해봐야겠지. 전체적인 인프라 아키텍처도 크기가 크다면 생각해봐야겠고.

     

      음 좀 질문에 대해 답변이 산으로 가긴 했는데, 결국 ‘복잡성의 중심축’은 내게 있어서 어떤 단위로 프로젝트의 요구사항을 나눠서 생각할지에 대한 부분이야. 위는 작은 시스템이라 그리 어렵지 않게 처음부터 나눠서 개별적으로 생각했지. 근데 통합 서비스로 막 여러 시스템이 붙은 하나의 큰 프로젝트라면 또 얘기가 달라져. ‘비즈니스 로직’ 이렇게 하나로 나눌수가 없지. A서비스 비즈니스 로직, B서비스 비즈니스 로직, … 이런식으로 될테니까. 결국, ‘단순하게 설계하려면’ 이 단위를 잘 정해야 한다고 생각해.

    댓글