Architecture / / 2024. 3. 9. 13:30

Clean Architecture

개요

과거에 Clean Architecture를 공부했던 때와 현재의 내가 바라보는 Clean Architecture에 대한 이해가 달라졌습니다. 따라서 해당 내용을 정리하기 위한 글입니다.

 

설명

Clean Architecture는 소프트웨어 설계의 복잡성을 관리하고 유지보수성을 향상시키기 위해 Robert C. Martin이 제안한 개념입니다. 이는 SOLID 원칙을 기반으로 하는 아키텍처 접근 방식으로, 특히 개방-폐쇄 원칙(Open-Closed Principle, OCP)과 의존성 역전 원칙(Dependency Inversion Principle, DIP)을 강조합니다. 이 두가지 원칙을 바탕으로 시스템의 유연성과 확장 가능성을 확보하는 Architecture 이론입니다.

계층은 엔티티(Entity) 계층, 유스케이스(Use Cases) 계층, 인터페이스 어댑터(Interface Adapters) 계층, 그리고 프레임워크 및 드라이버(Frameworks and Drivers) 계층으로 구성됩니다. 이 구조는 고수준의 비즈니스 로직과 저수준의 기술적 세부사항을 분리하여 각 계층의 역할을 명확히 하고 시스템의 결합도를 낮춥니다.

 

고수준의 정책과 의존성 역전 원칙

Clean Architecture는 고수준의 정책에 중점을 두고 이를 바탕으로 시스템을 설계합니다. 고수준 정책은 비즈니스 로직과 애플리케이션의 핵심 기능을 의미합니다. 이는 비즈니스 규칙과 요구 사항을 반영한 추상화된 인터페이스로 표현됩니다. 이러한 추상화는 시스템의 유연성을 높이고, 비즈니스 로직의 변경이 저수준의 구현에 미치는 영향을 최소화합니다.

Clean Architecture에서 의존성 역전 원칙은 핵심적인 역할을 합니다. 구체화된 클래스에 직접 의존하지 않고 추상화에 의존하도록 함으로써, 이 원칙은 시스템 내의 결합도를 낮추고 유지보수 및 확장을 용이하게 합니다. 이는 고수준의 정책이 저수준의 구현 세부사항에 독립적이라는 것을 의미합니다. 결과적으로, 기술 스택의 변경이나 구현 세부사항의 수정이 비즈니스 로직에 영향을 미치지 않게 됩니다.

만약 어떤 Application의 유저에 대한 요구사항으로 아이디와 패스워드 그리고 유저의 타입을 제공하도록 요구한다면 유저에 대한 정책은 아래와 같습니다.

public enum UserType {
    ADMIN, DEFAULT, GUEST;
}

public interface User {
    String getId();
    String getPassword();
    UserType getType();
}

 

저수준의 세부구현과 개방-폐쇄 원칙

Clean Architecture에서 저수준의 세부구현은 고수준의 정책이나 추상화된 개념을 구체화한 실체입니다. 이러한 구현은 시스템의 기능적 요구사항을 실제로 수행하며, 고수준의 정책을 실현하는 데 필요한 기술적 세부사항을 포함합니다. 저수준의 세부구현은 고수준의 정책을 효과적으로 지원하기 위해 필요한 구체적인 로직과 데이터를 처리합니다.

예를 들어, 사용자 관리 시스템에서는 사용자의 정보를 추상화하는 User 인터페이스를 정의할 수 있습니다. 이 인터페이스는 사용자의 기본적인 정보와 행동을 나타내는 고수준 정책을 표현합니다. 구체적으로, GuestUser 클래스는 User 인터페이스를 구현하여 방문자 사용자의 정보를 관리하는 저수준의 구현을 제공합니다:

public class GuestUser implements User {

    private String id;
    private String password;
    
    //.. 생성자 및 Setter

    @Override
    public String getId() {
        return this.id;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public UserType getType() {
        return UserType.GUEST;
    }
}

또 다른 예로, 사용자 정보를 조회하는 고수준의 정책을 UserFindPort 인터페이스로 정의할 수 있으며, 이를 통해 데이터베이스 접근이나 네트워크 통신 등의 저수준의 세부 구현을 다룹니다:

public interface UserFindPort {

    User find();
}

public class UserFindByDB implements UserFindPort {

    @Override
    public User find() {
        //DataBase Find Logic...
    }
}

public class UserFindByNetwork implements UserFindPort {

    @Override
    public User find() {
        //Network Find Logic...
    }
}

이러한 접근 방식은 개방-폐쇄 원칙(Open-Closed Principle, OCP)을 지키는 데 중요한 역할을 합니다. 시스템은 새로운 요구사항이나 기능의 추가에 대해 "열려" 있어야 하며, 기존의 코드를 변경하지 않고도 이를 수용할 수 있어야 합니다. 저수준의 세부구현을 통해 Clean Architecture는 고수준 정책의 변경 없이도 새로운 구현체를 손쉽게 추가할 수 있는 구조를 제공합니다. 따라서, 추상화된 인터페이스를 기반으로 하는 이러한 설계는 시스템의 확장성과 유연성을 향상시키며 유지보수성을 보장합니다.

 

엔티티(Entity) 계층

엔티티 계층은 Clean Architecture의 핵심으로 애플리케이션의 비즈니스 규칙과 도메인 모델을 캡슐화합니다. 이 계층에는 시스템의 핵심 비즈니스 로직이 구현되어 있으며 애플리케이션의 기본적인 데이터 구조와 이 데이터를 조작하는 비즈니스 규칙(함수)이 포함됩니다.

엔티티는 애플리케이션의 고수준 정책을 반영하며 변경이 필요한 경우 이 계층만을 수정하여도 다른 계층에 영향을 미치지 않도록 설계됩니다. 가장 내부에 위치하는 이 계층은 시스템의 다른 부분으로부터 비즈니스 로직을 격리시킵니다.

 

유스케이스(Use Cases) 계층

유스케이스 계층은 애플리케이션의 비즈니스 요구사항을 구체적으로 구현하는 역할을 합니다. 이 계층에서는 엔티티 계층을 활용하여 특정 비즈니스 시나리오나 사용자의 요구 사항을 실현합니다.

유스케이스는 수행해야 할 구체적인 작업들을 정의하고, 이를 통해 애플리케이션의 비즈니스 로직을 구현합니다. 엔티티 계층 바깥에 위치하는 이 계층은 엔티티의 재사용성을 높이고, 비즈니스 로직의 복잡성을 관리합니다.

 

인터페이스 어댑터(Interface Adapters) 계층

인터페이스 어댑터 계층은 내부 계층(엔티티와 유스케이스)과 외부 세계(사용자 인터페이스, 데이터베이스, 웹 API 등) 사이를 중개합니다. 이 계층의 주요 역할은 데이터의 형식을 변환하여 내부 계층이 처리할 수 있는 형태로 만들거나, 내부 계층의 처리 결과를 외부에서 요구하는 형태로 변환하는 것입니다.

MVC 아키텍처에서 볼 수 있는 컨트롤러와 뷰가 이 계층에 해당합니다. 인터페이스 어댑터는 시스템의 인터페이스를 담당하며, 사용자의 입력을 애플리케이션의 비즈니스 로직으로 전달하고, 처리 결과를 사용자에게 표시하는 역할을 합니다.

 

프레임워크 및 드라이버(Frameworks and Drivers) 계층

프레임워크 및 드라이버 계층은 애플리케이션의 가장 바깥쪽 계층으로 모든 외부 시스템과의 통신을 담당합니다. 이 계층에는 웹 프레임워크, 데이터베이스 드라이버, UI 프레임워크 등 애플리케이션을 실행하는 데 필요한 기술적인 구성 요소가 포함됩니다.

프레임워크 및 드라이버 계층은 나머지 시스템으로부터 인프라스트럭처와 관련된 세부사항을 분리하며 이를 통해 애플리케이션의 핵심 로직이 특정 기술 스택에 종속되지 않도록 합니다.

이 계층은 시스템의 다른 부분과는 다르게 자주 변경될 수 있으며, 이러한 변경이 내부 계층에 영향을 미치지 않도록 설계됩니다.

 

결론 : 유지보수성과 확장성 보장

Clean Architecture의 이러한 설계 원칙은 시스템의 유지보수성과 확장성을 보장합니다. 의존성 역전을 통한 고수준과 저수준의 분리는 기술적 변경사항이나 새로운 기능의 추가가 기존 시스템에 미치는 영향을 최소화합니다. 예를 들어, 데이터베이스나 외부 서비스의 변경이 비즈니스 로직의 수정을 요구하지 않도록 설계할 수 있습니다. 이는 개발 팀이 비즈니스 요구사항에 더 집중할 수 있게 하며, 기술적 부채의 축적을 방지합니다.

또한, 시스템의 각 계층은 독립적으로 개발, 테스트, 배포될 수 있으므로, 팀 간의 협업이 간소화되고 프로젝트의 전체적인 관리가 용이해집니다. 이는 애플리케이션의 빠른 시장 출시와 지속적인 반복 개선을 가능하게 합니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유