과거 스타트업에서 아무것도 모른 채 기초만 막 떼고 개발을 시작했었다. 그러다 보니 일단 기능 위주로 돌아가게만 구현을 했었는데 나중에 요구사항의 변경으로 기능을 변경하거나 기술 스택이 변경되는 경우 매우 오랜 시간 수정을 하거나 많은 작업을 수행했어야 했다. 그렇기에 아키텍처가 중요하다는 것을 느끼게 되었다.
새로운 딥다이브 주제는 클린아키텍처이다. 단순하게 "어떻게 구현하나?"가 아니라 왜 이런 아키텍처가 등장했는지 어떤 것을 추구하는지 이해하려고 노력하겠다.

1. 불편했던 경험담
꼭 스프링이 아니더라도 개발을 하면서 경험했던 것이 있다.
“Repository는 JPA를 쓰니까 JpaRepository를 상속하면 끝.”
그런데 이런 질문을 할 수 있다.
- DB를 NoSQL로 바꾸면?
- 테스트에서 메모리 저장소로 바꾸고 싶으면?
- 특정 저장 기술을 걷어내고 다른 방식으로 교체하면?
저장 기술을 바꾸려면 사용하는 곳 전체가 영향을 받거나, 구조 자체를 갈아엎어야 했다. 결국 선택한 기술에 끌려다니는 것 같았다.
이 문제는 스프링이 “나쁘기” 때문이 아니라, 핵심(정책)과 디테일(기술)이 섞이는 구조가 반복되기 때문이다.
2. 객체지향에서 반복되는 '정책 vs 디테일' 충돌
클린 아키텍처는 특정 프레임워크의 회피법이 아니다. 더 근본적으로는 객체지향에서 늘 등장하는 질문을 다룬다.
- 정책(Policy): 비즈니스 규칙, 유스케이스, “무슨 일을 해야 하는가”
- 디테일(Detail): DB, ORM, 외부 API, 웹 프레임워크, 메시지큐, UI
문제는 시간이 지날수록 디테일이 코드를 지배하기 쉽다는 점이다.
원래 기대: 정책(핵심)이 디테일(기술)을 바꿔 끼운다
Policy -> Detail
현실에서 자주 발생: 디테일이 정책을 침식한다
Policy <- Detail
"디테일이 정책을 침식한다"는 것은 이런 의미다.
- 핵심 로직이 프레임워크 타입/ORM 타입을 직접 참조
- 저장 기술의 제약(@Entity, lazy loading, 트랜잭션)에 맞춰 도메인이 변형
- 테스트는 스프링/DB 없이는 못 돌리는 형태가 됨
이 지점부터 변경 비용이 눈덩이처럼 불어난다.
3. 왜 변경 비용이 폭발할까?
겉으로는 "DB 교체가 어렵다", "테스트가 무겁다" 같은 현상으로 보이지만, 더 근본 원인은 의존성 방향이 잘못 잡혀 있기 때문이다.
정책(고수준)이 디테일(저수준)에 의존하는 순간, 디테일의 변화가 정책까지 전염된다.
(좋은 상태)
[정책/핵심] ---> [디테일/기술]
핵심은 기술을 갈아끼울 수 있다.
(나쁜 상태)
[정책/핵심] <--- [디테일/기술]
기술을 바꾸면 핵심이 흔들린다.
클린 아키텍처는 여기서 한 문장 규칙을 꺼낸다.
4. 클린 아키텍처의 출발점
클린 아키텍처의 핵심은 "레이어 이름"이 아니라, 다음의 규칙이다.
의존성은 항상 안쪽(핵심)을 향해야 한다. 즉, 안쪽은 바깥을 몰라야 한다.
여기서 "안쪽"은 도메인/유스케이스 같은 핵심 규칙이고, "바깥"은 DB/프레임워크/UI/외부 API 같은 구현 세부 사항이다.

이 규칙이 지켜지면 기대 효과는 명확하다.
- 저장 기술이 바뀌어도 핵심 로직은 그대로
- 단위 테스트가 쉬워짐(핵심은 순수 자바로 테스트 가능)
- 프레임워크 교체가 “전체 리라이트”가 아니라 “바깥 교체”로 축소
5. 이 규칙을 지키기 어려운 이유
스프링 같은 프레임워크가 문제를 "만드는"것은 아니지만, 편의 기능이 많을수록 바깥의 코드가 안쪽으로 새어 들어오기 쉬운 환경이 된다.
- 상속한 줄(extends ...)
- 어노테이션 몇 개(@Entity, @Service, @Transactional)
- 프레임워크 객체 직접 사용
처음에는 생산성이지만, 규모가 커질수록 “핵심이 디테일에 예속”되는 비용으로 돌아온다.
6. 은탄환은 없다.
개발업계에서 매우 유명한 말이다. 완벽한 해결방법은 없다는 것이다. 지금까지 클린아키텍처가 등장한 배경을 봤는데 그렇다고 클린아키텍처가 모든 문제를 완벽하게 해결하는 것은 아니다.
클린아키텍처는 장점이 명확하지만, 비용도 분명히 존재한다.
파일/계층/매핑이 늘어난다 (초기 생산성 하락)
Port/Adapter, 도메인 모델 vs JPA 엔티티 분리 같은 구조를 잡으면 클래스 수가 늘고, 매핑 코드도 생긴다. 작은 프로젝트에서는 “과하다”는 느낌이 들 수 있다.
잘못하면 "추상화만 늘고" 오히려 복잡해진다.
- 유스케이스가 사실상 CRUD인데도 포트를 과하게 쪼개거나
- 의미 없는 인터페이스를 남발하면
코드는 길어지는데 얻는 이득이 적다.
즉, 변경 가능성이 높은 축에만 추상화해야 한다.
팀 합의가 없으면 일관성이 무너진다.
안쪽에 스프링 어노테이션이 들어오거나, DTO/엔티티가 경계를 넘나들기 시작하면 구조는 “있는데 없는” 상태가 된다. 클린 아키텍처는 “원칙을 지키는 습관/규칙”이 같이 필요하다.
개발 속도가 중요한 단계에서는 부담이 될 수 있다.
프로토타입/POC 단계에서는 “빠르게 검증”이 우선이라 단순한 레이어드 + 적절한 테스트로도 충분할 수 있다. 즉, 언제 도입할지 타이밍이 중요하다.
7. 그래서 언제 가치가 클까?
클린 아키텍처가 빛나는 순간은 보통 이런 때다.
- 기능이 늘고 변경이 잦다(정책 변화가 빈번)
- DB/외부 연동/메시지큐 등 디테일 교체 가능성이 있다
- 테스트 속도가 개발 속도를 좌우한다
- 팀이 커지고 코드 소유권이 분산된다
반대로 아래라면 과할 수 있다.
- 단기 POC / 일회성 프로젝트
- 변경 가능성이 낮고 단순 CRUD에 가까움
- 팀/조직이 아직 규칙을 유지할 여력이 없음
다음으로 넘어가기 전에 다음과 같은 것을 생각해 보자
- 지금 내 구조에서 의존성 방향은 어디로 흐르고 있나?
- “서비스(핵심)”가 “DB 라이브러리 타입”을 직접 참조하면 방향이 바깥으로 새는 신호
- 내 테스트가 무거운 이유는 ‘DB가 필요해서’인가, ‘경계가 없어서’인가?
- 단위 테스트를 만들려면 스프링 컨텍스트/DB가 필수인가?
다음 편에서는 ‘안쪽은 바깥을 몰라야 한다’를 말로만이 아니라 자바 패키지/의존성 규칙으로 어떻게 강제할지(Dependency Rule의 구현)로 들어간다.
출처
https://velog.io/@inseo24/clean-architecture-01-03
만들면서 배우는 클린 아키텍처 01-03
책을 읽자
velog.io
