복잡한 시스템을 클린하게 구현하는 법

묘니·2025년 7월 24일
0

TL;DR: 업무와 과제 속 복잡성과 싸우며, 유비쿼터스 언어와 도메인 주도 설계 원칙을 통해 문제를 해결해 나간 과정을 공유

들어가며

요즘은 회사에선 프론트 라이브러리 설계를 하고, 퇴근 후엔 백엔드 과제를 하다 보니 머릿속이 밤낮으로 쪼개지는 기분이다.
이 글은 그 쪼개짐의 흔적이자, 그 안에서 고민했던
"복잡한 시스템을 클린하게 구현하는 방법"에 대한 기록이다.

클린 코드의 시작: 유비쿼터스 언어의 정의

업무를 하다 보면, 다른 사람들과의 소통이 의외로 어렵다는 사실을 자주 실감하게 된다.
다른 직군은 물론이고, 같은 개발자 사이에서도 의사소통이 쉽지만은 않다.

최근 1년 동안 회사에서 "무슨 말인지 잘 모르겠다"는 피드백을 평생 통틀어 들은 것보다 많이 듣고 나니, '말을 잘한다'는 게 어떤 의미인지 점점 헷갈리기 시작했다.

지시나 설명에 대해 "그런 의미가 아니었다"는 피드백이 반복되면서 처음으로 '내가 언어 능력이 부족한가?' 하는 걱정이 들었다.

그러다 알게 됐다.
개발자로서 '말을 잘한다'는 건 화려하게 설명하는 능력이 아니라,
상대와의 컨텍스트를 얼마나 정확히 맞출 수 있느냐에 달려 있다는 것을.

컨텍스트를 맞추는 법

이건 첫번째 레슨 : 좋은 건 나만 알지 않기

예전의 나는 설명할 때 별다른 고민 없이 바로 코드부터 보여주는 스타일이었다.
"다들 이 정도는 알고 있겠지" 하는 생각으로 설명을 생략하거나 건너뛰기 일쑤였다.

하지만 사람마다 이해하고 있는 배경지식이 다르고,
같은 개념도 서로 다른 용어로 인식하는 경우가 많았다.

그래서 설명 이전에, 내가 이해한 내용을 정리해 공유하는 과정이 꼭 필요하다는 걸 느꼈다.

이건 두번째 레슨 : 도메인 용어를 '정의' 하기

각 도메인에는 고유한 언어가 존재한다.
그리고 도메인이 복잡할수록 그 언어도 더 복잡하고 특수하다.

이런 용어들이 개발자마다, 혹은 비개발자에게까지 다르게 사용되기 시작하면
단순한 이슈 티켓 하나를 이해하는 데도 시간이 오래 걸리게 된다.

그래서 팀 차원에서 도메인 용어를 정의하고, 합의된 언어로 소통하는 것이 중요하다는 것을 절실히 느꼈다.
컨텍스트를 파악하는 데 들어가는 시간과 비용은 생각보다 크기 때문이다.

설계가 구현보다 중요하다

IDE를 끄고 머리를 켜기

"코드 짜기 전에 생각했나요?"

  • 일정 산정이 안 되고
  • 일정이 자꾸 틀어지는 이유

그건 바로 설계를 안 했기 때문이다.

쉬워 보이니까 금방 끝낼 수 있을 거라는 판단으로 코드부터 짜기 시작하면 일은 금방 꼬이기 시작한다.
그렇게 일정이 틀어지는 경험을 몇 번 하고 나서야, 내 감각을 의심하기 시작했다.

그리고 내가 가장 먼저 한 건 IDE를 끄는 일이었다.

목표가 보이지 않는 설계는 실패한다

요구사항은 있지만 구현 방법이 명확하지 않을 때,
일단 일정부터 맞추기 위해 기능부터 만들어보는 경우가 있다.

하지만 그렇게 시작된 코드는
처음부터 복잡하게 꼬일 가능성이 크다.

설계는 내가 무엇을, 구현하는지를 명확히 하는 과정이며,
역설적이게도 이것이 기능을 가장 빠르게 완성하는 지름길이다.

구현은 설계의 번역

🧭 설계 예시: 비동기 통신에서 요청-응답 매핑과 타임아웃 관리

최근 실시간 통신에서, 소켓 연결이 끊기거나 응답이 오지 않는 문제를 다룰 일이 있었다.
처음엔 단순한 예외 상황처럼 보여 바로 구현할 수 있을 것 같았다.

하지만 간단하게라도 먼저 설계를 해보기로 했고,
그 결과 처음엔 보이지 않았던 문제들이 하나둘 드러나기 시작했다.

  • 어떤 요청에 대해 어떤 응답을 받아야 '정상 처리'로 간주할 수 있는지를 명확히 정의해야 했고,
  • 동일한 요청을 반복 보낼 경우, 이전 타이머가 그대로 남아 중복 타임아웃이 발생하는 상황에 대한 방어 로직이 필요했으며,
  • 하나의 커맨드가 여러 종류의 응답을 받을 수 있는 경우,
    어떤 응답이 어떤 요청에 대한 것인지 명확히 식별하고, 타이머를 정확히 종료하기 위한 설계도 필요했다.
💡 아래의 플로우차트는 요청 전송부터 응답 수신 및 
타임아웃 처리까지의 흐름을 간략히 나타낸 것이다.

이처럼 겉보기엔 단순한 문제도,
실제로는 응답-요청 매핑, 타이머 중복 해제, 분기 처리 등 다양한 논리적 고려 요소들이 얽혀 있었다.

끝없는 책임의 분리

"그렇게까지 나눠야 돼?"
"네."

구현을 하다 보면 처음엔 오히려 번거롭고 불편해 보이지만,
"미리 나눠놓는 게 가장 편하다는 것"을 반드시 깨닫게 된다.

중요한 건 무조건 나누는 게 아니라, '왜 나누는가'에 대한 분명한 이유가 있어야 한다는 점이다.

📦 설계 예시: Product ERD

이번 과제에서는 상품(Product) 도메인을 설계하면서 세부 엔티티들을 다음과 같이 분리했다.

서브 도메인분리 이유
PRODUCT_DETAILdescription 등 자주 조회되지 않는 필드를 분리해 리스트 쿼리 성능 최적화
PRODUCT_IMAGE / BRAND_IMAGE정규화 원칙 준수 + 이미지 관리 책임 분리
PRODUCT_STOCK쓰기 성능 / 동시성 대응 + 옵션 단위 재고 관리 확장성 확보

→ 기능은 늘어났지만, 구조는 오히려 간결하고 변경에 강한 구조로 개선되었다.

클린 아키텍처의 4가지 핵심 의도

원칙설명예시
SRP (단일 책임 원칙)변경의 영향 최소화컨트롤러 / 서비스 / 레포지토리 분리
SoC (관심사 분리)각 역할의 독립성 확보Product vs ProductDetail vs Stock
Dependency Rule도메인 → 외부 의존 금지도메인은 DB/프레임워크를 몰라도 됨
테스트 용이성계층 분리로 테스트 쉬움서비스/레포지토리를 Mock 처리해 빠른 테스트 가능

마무리하며: 클린한 코드를 위한 태도

'클린 코드'는 단지 예쁜 코드가 아니라,
복잡성을 제어하려는 태도와 철학에서 비롯된다.

그리고 그 시작은 IDE가 아니라
내 머릿속에서 먼저 구조를 쪼개는 일부터 시작된다.

설계는 머리로 하고 구현은 그 설계를 번역하는 일이다.
그 철학이 반복될수록 시스템은 점점 더 명확하고 단단해진다.

0개의 댓글