Clean Architecture 정리 - 5부 24장 부분적 경계
2025. 7. 25. 16:44

📌 아키텍처 경계를 완벽하게 만드는 것은 힘들다.

  • 완벽한 경계는 구현과 유지에 많은 비용과 노력이 필요함.
    • 쌍방향의 다형적 경계 인터페이스
    • Input과 Output을 위한 데이터 구조
    • 독립적 컴포넌트로 컴파일 및 배포할 수 있는 구조 및 의존성 관리
  • YAGNI(You Aren’t Gonna Need It) 원칙에 따라, 모든 경계를 미리 만들기보다는 필요할 때 경계를 도입하는 접근도 고려해볼 수 있음 → 부분적 경계

📌 전략 1: 마지막 단계를 건너뛰기 (Partial Boundary)

  • 이 전략은 논리적으로는 컴포넌트를 분리하지만, 물리적으로는 분리된 배포를 생략하는 방식임.
  • 즉, 컴포넌트 내부적으로는 입력/출력 인터페이스, 데이터 구조, 책임 분리를 모두 갖추고 있음.
    그러나 각 계층을 다른 배포 단위로 나누지 않고 하나의 컴포넌트로 묶어 컴파일하고 배포함.
  • 이렇게 마지막 분리 단계를 생략함으로써 배포의 복잡도와 관리 비용을 줄이는 것이 핵심임.
  • 이러한 전략은 초기에는 배포와 운영을 단순화하는 장점이 있지만, 시간이 지남에 따라 경계가 흐려지고 컴포넌트 간 의존성이 강화되며, 경계의 의미가 퇴색되는 위험을 수반함.
    결국 나중에는 경계를 다시 복원하거나 분리해야 하는 비용과 기술적 부채로 되돌아올 수 있음.

📌 전략 2: 일차원 경계 (Strategy Pattern)

  • 이 전략은 인터페이스로 경계를 느슨한 결합으로 만듦
    (Client → Interface ←  Impl)
  • 이 방식은 추후 컴포넌트가 분리될 가능성을 대비해 구조적 유연성을 확보할 수 있음.
// 완벽한 경계를 위한 쌍방향 다형적 인터페이스 예시
interface InputBoundary {
    void handle(InputData input);
}

interface OutputBoundary {
    void present(OutputData output);
}

class Interactor implements InputBoundary {
    private final OutputBoundary output;

    Interactor(OutputBoundary output) {
        this.output = output;
    }

    public void handle(InputData input) {
        // ...도메인 처리
        output.present(new OutputData(...));
    }
}

class Presenter implements OutputBoundary {
    public void present(OutputData output) {
        // UI 렌더링 로직
    }
}
// 전략 패턴을 사용한 단순화된 경계 예시
interface ServiceBoundary {
    OutputData execute();
}

class ServiceImpl implements ServiceBoundary {
    public OutputData execute() {
        return new OutputData("결과");
    }
}

class Client {
    private final ServiceBoundary service;

    Client(ServiceBoundary service) {
        this.service = service;
    }

    public void run() {
        OutputData result = service.execute();
        System.out.println("Service 결과: " + result.value());
    }
}

record OutputData(String value) {}
  • 코드를 보면, 전략 2는 완벽한 구조에 비해 설계와 구현이 훨씬 간단하다는 장점이 있음.
  • 하지만, ServiceImpl이 Client를 직접 참조하게 되면 경계가 무너질 수 있음을 주의

📌 전략 3: 퍼사드 패턴 (Facade Pattern)

  • 가장 단순한 경계 설정 방식 중 하나
  • Facade 클래스를 통해 [인터페이스 없이] 모든 서비스 클래스를 간접 호출함.
  • 즉, 퍼사드는 클라이언트가 Core 계층(Service)에 직접 접근하지 못하게 하는 중간 계층 역할을 함.
Client → Facade → Service1 / Service2 / Service3 ..
  • Client는 결국 서비스 클래스에 추이 종속성을 가짐.
  • 정적 언어일 경우, Service에 변화가 생기면 Client도 재컴파일이 필요함.
Client → Interface Adapter ← Service1 / Service2 / ...
  • 인터페이스로 경계가 나뉘어 있다면, Service가 바뀌어도 바로 Client에 영향을 주지 않으며, 인터페이스의 변화에만 영향을 받음.

📌 마무리

  • 아키텍처 경계를 설정하는 방법에는 수준별로 더 많은 전략이 있음.
  • 완전한 경계 vs 현실적인 비용 간의 균형이 중요함.
  • 언제, 어디에, 얼마나 강한 경계를 설정할지 결정하는 것이 아키텍트의 핵심 역할임.

 

 

※ 본 글은 『Clean Architecture』(로버트 C. 마틴 저) 5부 24장을 기반으로 학습 목적으로 요약한 글입니다.

※ 이 글은 책의 내용을 요약한 것으로, 원문 없이 읽을 경우 오해의 여지가 있을 수 있습니다. 정확한 이해를 위해 원서의 정독을 권장합니다.