AI시대에서 클린 아키텍처 이해하기
- 개발
- 2026. 3. 3. 23:44
현재 우리는 AI 시대에 살고 있습니다. "AI가 코드를 다 작성해주는데, 이제 아키텍처를 공부하는 것이 과연 의미가 있을까?"
이런 질문을 종종 듣게 됩니다. 어느 정도는 공감이 가는 말입니다. 실제로 AI는 빠르게 코드를 생성하고, 기본적인 구조를 만들어내며, 심지어 리팩토링까지 제안합니다. 과거에 비해 구현의 진입 장벽은 확실히 낮아졌습니다. 하지만 한 가지는 분명합니다. AI가 작성한 코드의 책임은 결국 사람이 져야 한다는 점입니다. AI는 코드를 생성할 수 있지만, 그 코드가 시스템의 맥락에 맞는지, 장기적으로 유지 가능한지, 확장 시 문제가 발생하지는 않는지까지 완전히 보장해주지는 않습니다. 설령 AI를 신뢰한다고 하더라도, 언제나 동일한 환경과 도구를 사용할 수 있다는 보장도 없습니다. 만약 AI의 도움 없이 코드를 작성해야 하는 상황이 온다면, 결국 설계와 구조에 대한 이해는 사람의 몫이 됩니다. 그래서 저는 이번에 클린 아키텍처를 학습해보려 합니다. AI가 코드를 대신 작성해주는 시대일수록, 오히려 시스템의 중심이 되는 구조와 책임에 대해 더 깊이 고민해야 하지 않을까 하는 생각에서입니다.
클린 아키텍처란?
클린 아키텍처는 소프트웨어 시스템을 여러 계층으로 분리하여 각 계층이 명확한 책임을 가지도록 설계하는 구조입니다. 그렇다면 왜 여러 계층으로 나누는 것이 "클린하다"고 표현하는 걸까요? 클린하다는 것은 계층이 많아서 깨끗하다는 뜻이 아닙니다.
오히려 계층이 늘어나면 구조는 더 복잡해질 수도 있습니다. 그럼에도 불구하고 클린이라고 부르는 이유는 따로 있습니다.
소프트웨어가 "더러워지는" 순간은 언제일까요?
- 컨트롤러에 비즈니스 로직이 섞여 있고,
- 도메인 객체가 DB 어노테이션에 강하게 묶여 있으며,
- 서비스가 HTTP 응답 포맷을 알고 있고,
- 하나의 클래스가 여러 이유로 변경되는 상황입니다.
이때 코드가 복잡해지는 이유는 하나입니다. 기술과 책임이 뒤섞였기 때문입니다.
클린 아키텍처는 이 혼합을 강제로 분리합니다.
- 이 코드는 비즈니스인가?
- 기술 구현인가?
- 외부 시스템과의 연결인가?
이 질문을 끊임없이 던지게 만듭니다. 책임이 명확해질수록 코드의 의도는 선명해집니다. 그 상태를 우리는 "클린하다"고 부릅니다.
또한, 클린 아키텍처는 사실 "시간"을 고려한 설계입니다.
시간이 지나면 프레임워크는 바뀌고, DB는 교체되며, API 방식은 변하고, 팀 구성도 달라집니다.
하지만 비즈니스 규칙은 비교적 오래 유지됩니다. 그래서 클린 아키텍처는 변하지 않는 것을 중심에 두고, 쉽게 변하는 것을 바깥으로 밀어냅니다. 의존성의 방향을 통제하여 변화가 중심을 오염시키지 않도록 합니다. 시간이 지나도 핵심이 유지되는 구조. 그것이 바로 깨끗한 상태를 유지하는 구조입니다. 즉, 클린하다는 것은 책임이 명확하고, 의존성이 통제되어 있으며, 변화가 중심을 침범하지 못하는 상태를 의미합니다.

핵심 원칙 (Key Principles)
클린 아키텍처는 유지보수성과 확장성, 테스트 용이성을 확보하기 위한 몇 가지 핵심 원칙을 기반으로 합니다.
이 원칙들은 시스템의 책임을 명확히 하고, 변화에 강한 구조를 만들기 위한 기준이 됩니다.
관심사 분리 (Separation of Concerns)
관심사 분리는 각 계층이 자신의 책임만 가지도록 명확히 분리하는 원칙입니다.
하나의 구성 요소가 여러 역할을 동시에 수행하지 않도록 하여 책임의 경계를 분명히 합니다.
예를 들어, 컨트롤러는 요청을 받고 응답을 반환하는 역할만 수행해야 합니다.
실제 비즈니스 규칙은 서비스나 유스케이스 계층에서 처리하며, 데이터 저장과 같은 기술적 세부사항은 리포지토리 계층에서 담당합니다.

이 접근 방식에는 두 가지 주요 이점이 있습니다.
첫째, 각 계층이 단일 책임을 가지므로 코드의 의도가 명확해지고 유지보수가 쉬워집니다.
변경이 필요할 때 어디를 수정해야 하는지 명확해지기 때문입니다.
둘째, 특정 계층의 변경이 다른 계층에 영향을 주지 않습니다.
예를 들어 SQL 데이터베이스를 NoSQL로 전환하더라도, 데이터 접근 계층만 수정하면 되며 핵심 비즈니스 로직은 그대로 유지될 수 있습니다.
관심사를 분리한다는 것은 단순히 코드를 나누는 것이 아니라, 변화의 범위를 통제하는 설계 전략입니다.
의존성 규칙 (Dependency Rule)
의존성 규칙은 클린 아키텍처의 핵심 원칙입니다.
이 규칙에 따르면 모든 의존성은 시스템의 중심을 향해, 즉 안쪽으로만 흘러가야 합니다.

프레임워크, UI, 데이터베이스와 같은 외부 계층은 내부 계층(엔티티, 유스케이스)에 의존할 수 있지만, 내부 계층은 외부 계층을 알지 못해야 합니다. 핵심 비즈니스 로직은 기술적 세부사항에 의해 오염되어서는 안 됩니다.
예를 들어, 사용 사례는 특정 데이터베이스 스키마나 UI 프레임워크에 직접 의존해서는 안 됩니다. 대신 데이터 접근 계층은 리포지토리와 같은 인터페이스를 통해 계약만 제공하고, 실제 구현은 외부 계층에서 담당합니다. 데이터베이스가 변경되더라도 유스케이스와 엔티티는 수정할 필요가 없습니다.
이 규칙의 이점은 명확합니다.
첫째, 데이터베이스 교체와 같은 외부 변화가 핵심 비즈니스 로직에 영향을 주지 않습니다. 기술은 바뀌더라도 비즈니스 규칙은 안정적으로 유지됩니다.
둘째, 내부 레이어는 외부 시스템과 결합되지 않기 때문에 독립적으로 테스트할 수 있습니다. 실제 데이터베이스 없이도 핵심 로직을 검증할 수 있습니다.
셋째, 사용자 인터페이스나 데이터베이스와 같은 구성 요소를 최소한의 영향으로 교체할 수 있습니다. 예를 들어 REST API를 GraphQL로 전환하더라도 인터페이스 어댑터 계층만 수정하면 됩니다.
넷째, 새로운 기술이나 기능을 도입하더라도 핵심 논리를 건드리지 않고 시스템을 확장할 수 있습니다.
외부 시스템은 본질적으로 변동성이 큽니다. 웹 프레임워크는 업데이트되고, 라이브러리는 deprecated되며, 인프라는 교체됩니다. 의존성 규칙은 이러한 변화를 코어에서 격리함으로써 장기적으로 안정적인 구조를 유지하게 합니다.
단일 책임 원칙 (SRP)
단일 책임 원칙은 클래스나 모듈이 오직 하나의 이유로만 변경되어야 한다는 원칙입니다.
즉, 하나의 구성 요소는 하나의 책임에만 집중해야 합니다. 여기서 말하는 "책임"은 단순한 기능 단위가 아니라, 변경의 이유를 의미합니다.
만약 하나의 클래스가 서로 다른 이유로 수정될 가능성이 있다면, 이미 여러 책임을 동시에 가지고 있는 것입니다.
예를 들어, 하나의 클래스가 사용자 인증 로직을 처리하면서 동시에 이메일 알림까지 담당하고 있다면 이는 좋은 설계가 아닙니다. 인증 정책이 변경될 때도 수정되어야 하고, 이메일 발송 방식이 바뀌어도 수정되어야 하기 때문입니다. 서로 다른 변화가 하나의 클래스에 영향을 미치게 됩니다. 이 경우 인증을 담당하는 클래스와 알림을 담당하는 클래스를 분리하는 것이 바람직합니다. 각 클래스는 자신의 책임에만 집중하고, 서로 다른 변경 요인이 서로에게 영향을 주지 않도록 해야 합니다.
단일 책임 원칙은 코드의 길이를 줄이기 위한 규칙이 아니라, 변화의 방향을 분리하기 위한 설계 원칙입니다. 책임이 명확해질수록 변경의 영향 범위는 작아지고, 시스템은 더 안정적으로 유지될 수 있습니다.

이 레이어들은 동심원을 형성하며, 코어는 시스템의 가장 근본적이고 변하지 않는 부분을 포함하고 있으며, 외부 레이어는 사용자 인터페이스와 데이터베이스와 같은 기술별 세부 사항을 처리합니다.
각 레이어를 더 자세히 살펴보겠습니다.
계층 구조 (Layered Structure)
1. Entities (핵심 비즈니스 규칙)
한국에서는 “도메인”이라는 용어가 더 익숙하게 사용됩니다. 실제로 많은 프로젝트에서 엔티티 계층을 도메인 계층이라고 부르기도 합니다.
다만 엄밀히 구분하자면, 클린 아키텍처에서 말하는 Entities는 단순한 도메인 모델을 넘어서는 개념입니다. 도메인은 문제 영역 전체를 의미하는 포괄적인 개념인 반면, 엔티티는 그 안에서 변하지 않는 핵심 비즈니스 규칙을 직접적으로 담고 있는 객체를 가리킵니다.
즉, 도메인이 “문제 영역 전체”라면, 엔티티는 그 영역 안에서 가장 본질적인 규칙을 표현하는 중심 모델에 가깝습니다.
그래서 용어는 익숙한 “도메인”을 사용할 수 있지만, 클린 아키텍처의 맥락에서는 “엔티티”라는 표현이 조금 더 정확하다고 볼 수 있습니다.
예를 들어 다음과 같은 코드가 있다고 가정해보겠습니다.
@Getter
@Entity
@Table(name = "boards")
public class Post extends BaseEntity {
...
public void reWrite(String title, String content) {
...
}
public void checkAuthor(String author) {
...
}
}
이 코드는 게시판 엔티티를 표현하고 있습니다.
재작성(reWrite)과 작성자 확인(checkAuthor)이라는 행위를 포함하고 있는 점은 긍정적으로 볼 수 있습니다. 데이터만 담고 있는 단순한 객체가 아니라, 게시글이라는 개념이 가져야 할 행위를 함께 표현하고 있기 때문입니다. 그러나 한 가지 생각해볼 부분이 있습니다.
이 클래스는 @Entity, @Table과 같은 JPA 어노테이션에 의존하고 있습니다. 즉, 이 객체는 이미 특정 ORM 프레임워크에 결합되어 있습니다. 클린 아키텍처 관점에서 보면, 핵심 엔티티는 프레임워크와 무관해야 합니다. (하지만 편의상 넣는 경우도 있습니다.)
데이터베이스가 JPA인지, MyBatis인지, 심지어 NoSQL인지조차 알 필요가 없습니다.
이처럼 기술적 어노테이션이 직접 붙어 있는 순간,
이 객체는 "순수한 엔티티"라기보다는 "영속성 모델에 가까운 객체"가 됩니다.
결국 질문은 이것입니다. 이 객체는 비즈니스 규칙을 표현하는 중심 모델인가? 아니면 데이터 베이스 매핑을 위한 모델인가?
클린 아키텍처에서 말하는 엔티티는 전자에 더 가깝습니다. 기술이 아닌, 비즈니스 자체를 표현하는 모델이어야 하기 때문입니다.
정확히 클린아키텍처에서 말하는 코드는 다음과 같습니다.
public class Post extends BaseEntity {
...
public void reWrite(String title, String content) {
...
}
public void checkAuthor(String author) {
...
}
}
2. Use Cases (애플리케이션 비즈니스 규칙)
많은 경우, 클린 아키텍처에서 말하는 Use Case는 우리가 흔히 사용하는 Service와 매우 비슷하게 느껴집니다.
저 역시 처음에는 "Use Case 계층이 내 코드에는 없는 것 같은데?"라는 생각이 들었습니다. 하지만 다시 구조를 들여다보면, 이미 Service가 Use Case의 역할을 수행하고 있는 경우가 많습니다.
스프링 기반의 전형적인 구조에서는 보통 Controller → Service → Repository 형태로 계층이 구성됩니다. 이때 우리는 비즈니스 로직을 Service에 작성해왔습니다. 클린 아키텍처 관점에서도 핵심은 동일합니다. 다만 용어가 조금 다를 뿐입니다.
Controller는 Interface Adapter에 가깝고,
Repository는 Gateway의 역할을 하며,
우리가 Service라고 부르던 영역이 사실상 Use Case 계층에 해당합니다.
즉, Use Case는 "반드시 새로운 레이어를 추가해야 한다"는 의미가 아니라, 애플리케이션의 특정 기능 흐름을 책임지는 계층을 어떻게 바라볼 것인가에 대한 관점에 가깝습니다. 결국 중요한 것은 이름이 Service냐 Use Case냐가 아니라, 그 코드가 하나의 사용자 행동(기능)을 명확히 표현하고 있는지, 그리고 기술적 세부사항으로부터 얼마나 독립적으로 유지되는지입니다.
정리하자면, 제 사례에서 Use Case 계층이 없어 보였던 이유는 이미 Service가 그 역할을 하고 있었기 때문입니다. 다만 클린 아키텍처에서는 이를 더 명확히 "행위 단위"로 분리하고, 비즈니스 규칙을 보호하는 구조로 강화하는 것을 권장합니다.
3. Interface Adapters
Interface Adapters는 핵심 로직(Entities, Use Cases)과 외부 시스템을 연결하는 계층입니다. 하지만 단순히 연결하는 통로라기보다는, 서로 다른 계층의 형식을 변환하고 조정하는 역할을 담당합니다.
외부 세계는 보통 HTTP 요청, JSON, 메시지, DB 레코드와 같은 형태로 데이터를 주고받습니다. 반면, 내부의 유스케이스와 엔티티는 순수한 객체와 비즈니스 규칙 중심으로 동작합니다. Interface Adapter는 이 둘 사이에서 데이터를 변환하여 핵심 로직이 외부 기술에 오염되지 않도록 보호합니다.
예를 들면 다음과 같은 구성 요소들이 해당됩니다.
- UI ↔ Controller
- API ↔ Controller
- Producer ↔ Consumer
- Repository 구현체 ↔ Database
Controller는 HTTP 요청을 받아 유스케이스가 이해할 수 있는 형태로 전달합니다.
Consumer는 메시지를 받아 내부 로직이 처리할 수 있는 구조로 변환합니다.
Repository 구현체는 데이터베이스와 통신하지만, 유스케이스에는 인터페이스만 노출합니다.
즉, Interface Adapter는 외부와 연동할 수 있는 경로이면서 동시에,
핵심 비즈니스 로직을 기술적인 세부사항으로부터 보호하는 방패 역할을 합니다.
핵심은 이것입니다. 내부는 외부를 몰라도 된다. 대신 외부가 내부를 이해할 수 있도록 번역해주는 계층이 필요하다.
그 역할을 수행하는 곳이 바로 Interface Adapters입니다.
4. Frameworks & Drivers (가장 바깥)
Frameworks & Drivers는 시스템의 기술적 세부사항을 담고 있는 가장 바깥 계층입니다.
이 계층에는 프레임워크, 데이터베이스, 외부 API, 메시징 시스템 등과 같은 구현 기술이 포함됩니다.
가장 중요한 특징은 변경 가능성이 가장 높은 영역이라는 점입니다.
라이브러리는 버전이 바뀌고, 데이터베이스는 교체되며, 외부 API는 변경됩니다. 이 계층은 언제든지 바뀔 수 있는 기술 요소를 모아두는 영역입니다.
이 계층의 역할은 애플리케이션이 동작하기 위한 인프라를 제공하는 것입니다.
하지만 여기에는 핵심 비즈니스 로직이 포함되어서는 안 됩니다. 오직 외부 시스템과 상호작용하기 위한 구현 세부사항만 존재해야 합니다.
infra 아래에 다음과 같은 폴더가 있습니다:

이 구성은 전형적인 Frameworks & Drivers 계층입니다.
여기에는
- 데이터 접근 기술 (jpa, pg, redis)
- 외부 통신 (gRpc, restTemplate)
- 인증 기술 (jwt)
- 인프라 유틸리티 (threadpool, retry, lock, logging)
- 직렬화 (jackson)
같은 것들이 모여 있습니다. 이건 정확히 클린 아키텍처가 말하는 "가장 바깥"입니다.
결론
AI시대에서 클린아키텍처를 이해를 해야할까 고민이 많았습니다.
AI가 주니어 개발자를 쓸모없게 만들고 있다 | GeekNews
AI 도구가 주니어 개발자에게 얕은 역량만 만들어주고 있으며, 코드를 빠르게 출력하지만 왜 그런 접근을 택했는지 설명하지 못하는 상황이 빈번해짐시니어 개발자의 진정한 가치는 코드 작성
news.hada.io
이 글을 읽고 생각이 완전히 바뀌었다는건 아니지만, 뭐랄까 AI가 완전히 개발 시장을 장악하기전까지는 최선을 다해 그들위에 서야 한다고 된다고 느꼈던거 같습니다. 이 글도 AI를 통해 적고 읽고를 반복했습니다. 적다보니 이해가 되더라구요. AI가 쓴글을 바로 복사해서 사용하는것이 아닌 읽고 옮기다보니 새로운 글이 되어지는 느낌이 듭니다.
클린 아키텍처를 보면 대부분 제가 아는것들이었습니다. 다만 용어가 조금 다른경우도 더러있었습니다. 일단 최선을 다할 생각입니다.
하고 싶은게 있는데 그걸 하는게 제일 행복이 아닐까 생각합니다.
출처: https://blog.bytebytego.com/p/clean-architecture-101-building-software
'개발' 카테고리의 다른 글
| 데이터베이스의 종류 (0) | 2026.03.05 |
|---|---|
| kafka가 Zookeeper대신 KRaft를 사용하는 이유가 뭘까? (1) | 2026.03.04 |
| minikube 간단하게 사용해보기 (0) | 2026.03.02 |
| 완벽한 일관성은 존재하지 않는다. (0) | 2026.02.27 |
| 무중단 배포를 적용해보자. (0) | 2026.02.26 |