앱을 하나 만드는 것은 쉽습니다. 하지만 3년 뒤에도, 기획이 수십 번 바뀌어도 유지보수할 수 있는 앱을 만드는 것은 완전히 다른 이야기입니다. 많은 개발자가 ‘기능 구현’이라는 근시안적 목표에 매몰되어 아키텍처라는 기초 공사를 소홀히 하곤 합니다. 오늘은 초기 개발의 편안함을 버리고 장기적 생산성을 택하는 ‘Clean Architecture’의 본질에 대해 깊이 있게 다뤄보겠습니다.

[그림 1] 앱의 견고함을 결정짓는 아키텍처 설계의 중요성
1. 왜 MVVM만으로는 부족한가?
대부분의 Flutter나 Android 프로젝트는 MVVM(Model-View-ViewModel)으로 시작합니다. 훌륭한 패턴이죠. 하지만 비즈니스 로직이 복잡해질수록 ViewModel은 이른바 **’Fat ViewModel’**이 되어버립니다. API 호출, 데이터 가공, 상태 업데이트, 에러 처리까지 모든 일을 ViewModel이 도맡게 되면서, 코드는 다시 스파게티처럼 엉키기 시작합니다.
Pro-tip: 아키텍처의 핵심은 ‘역할 분담’입니다. ViewModel이 너무 많은 일을 하고 있다면, 그것은 아키텍처가 한계에 다다랐다는 신호입니다.
2. Clean Architecture: 레이어 간의 엄격한 경계선
Uncle Bob이 제안한 Clean Architecture의 핵심은 **’의존성 규칙(Dependency Rule)’**입니다. 안쪽 원은 바깥쪽 원에 대해 전혀 알지 못해야 합니다. 즉, 핵심 비즈니스 로직(Entity, Use Case)은 UI나 데이터베이스가 어떤 프레임워크를 쓰는지 전혀 상관하지 않아야 한다는 뜻입니다.

[그림 2] 계층별 역할과 안쪽으로 흐르는 의존성 방향
이 구조를 도입하면 데이터베이스를 Realm에서 Hive로 바꾸거나, UI를 순수 위젯에서 복잡한 라이브러리로 바꿔도 비즈니스 로직은 단 한 줄도 수정할 필요가 없습니다. 이것이 바로 **’독립적인 테스트’**와 **’비즈니스 중심 개발’**이 가능해지는 비결입니다.
3. 실전 코드: Use Case의 도입 (비즈니스 로직의 파편화 방지)
기존 MVVM 코드가 모든 로직을 ViewModel에 때려 넣었다면, Clean Architecture는 기능을 ‘Use Case’ 단위로 쪼갭니다.
// [Bad] ViewModel에서 모든 로직 처리
class UserViewModel extends ChangeNotifier {
final ApiClient api;
void login(String id) async {
// Validation logic
// API call
// Logic for saving session
// Logic for updating UI state
}
}
// [Good] Clean Architecture의 Use Case 적용
class LoginUseCase {
final UserRepository repository;
LoginUseCase(this.repository);
Future<User> execute(String id) async {
// 핵심 비즈니스 규칙만 포함
return await repository.login(id);
}
}
이렇게 Use Case를 분리하면, 로그인 로직이 필요한 다른 화면에서도 코드를 재사용할 수 있고, 개별 기능에 대한 단위 테스트가 비약적으로 쉬워집니다.
4. Spaghetti vs Clean: 당신의 코드는 어느 쪽인가요?

[그림 3] 무질서한 코드와 질서 있는 아키텍처의 극명한 대비
위 그림처럼, Clean Architecture는 초기 학습 곡선이 높고 보일러플레이트 코드가 많다는 단점이 있습니다. 하지만 서비스가 커질수록 이 질서정연한 구조는 개발팀의 엄청난 속도를 보장해 주는 **’황금 거위’**가 됩니다.
5. 리팩토링 과정에서의 고찰
[문제 상황]
실제 프로젝트에서 Clean Architecture를 도입할 때 가장 큰 문제는 ‘과도한 엔지니어링’이었습니다. 아주 단순한 조회 기능임에도 Entity-Domain Model-DTO를 모두 생성하느라 시간이 너무 많이 소모되었습니다.
[해결 전략]
저는 모든 기능에 엄격한 Clean Architecture를 적용하기보다, 복잡한 비즈니스 로직이 들어가는 핵심 모듈에만 우선 적용하는 **’하이브리드’** 방식을 택했습니다. 실용성과 결합도를 동시에 잡는 전략이었죠.
마치며: 결국 사람이 쓰는 코드입니다
아키텍처는 목적이 아니라 수단입니다. 완벽한 원칙에 집착하기보다, **’이 코드가 동료가 읽기에도 명확한가?’**를 먼저 질문하는 개발자가 되어야 합니다. Clean Architecture는 그 질문에 대한 가장 근사한 답안지 중 하나일 뿐입니다.