아키텍처와 종류

위승현·2024년 10월 24일

CS

목록 보기
1/6

아키텍처란?

어떤 대상의 구성과 동작 원리, 구성 요소간의 관계 및 시스템 외부환경과의 관계를
설명하는 하나의 설명서이다.

간단히 말해서 시스템의 설계 및 동작하는 방식이라고 이해할 수 있다.

아키텍처의 예시이다. 여러 애플리케이션으로 구성되어있고
각각의 요소들이 무엇이 있는지 보여준다.

소프트웨어 아키텍처

소프트웨어 구성요소들 사이 관계를 표현하는 하나의 설명서이다.
소프트웨어 설계 구조 계획을 설명한다.
아키텍처가 왜 중요할까?
소프트웨어 아키텍처는 소프트웨어가 제공하는 가치 중 구조에 해당한다.

구조가 왜 중요할까?
기능과 구조 중에서 뭐가 더 중요할까?
보통은 기능이 더 중요할 것이라고 생각한다. 어떤 기능을 만들것인지 고민하고
기능이 잘 돌아가게 하기위해 많은 시간을 투자한다.

하지만 클린 코드와 클린 아키텍처를 쓴 로버트 마틴은 구조가 더 중요하다고 설명한다.

예시를 바탕으로 설명해보자.
방에 물건을 배치한다고 생각하자.
방 구조를 설계하지않고 아무렇게나 기능을 놓으면 방이 꽉 차버린다.
무엇이 있는지 알기도 힘들고 더 추가할 공간도 없다.
기능이 많아도 너무 복잡한 구조라면 사용할 수 없게되는 것이다. 이렇게 구조를 설계하고 물건을 놓는다면 어떤 곳에 어떤 물건이 있는지도 알 수 있고
물건을 추가로 놓을 공간도 존재한다. 새로운게 추가되더라도 배치하기도 쉽다.

이러한 방 구조가 모두가 생각하는 이상적인 방 구조이다.

우리도 코드를 작성할 때 이러한 좋은 구조를 가지도록 작성해야한다.

또 구조가 중요한 이유는 소프트웨어 비용과도 관계가 있다.
개발 초기에는 많은 기능을 개발하기에 비용도 높다.
하지만 개발이 완료되고나서는 계속해서 변화하는 소프트웨어를 유지보수 해야한다.
이 때 기능 개발 비용보다 유지보수 비용이 늘어나는 것을 확인할 수 있다.

장기적으로 봤을 때는 유지보수 비용이 훨씬 크다는 것을 알 수 있다.

기능이 좋고 구조가 부족하다면 당장은 좋지만 수정비용이 너무 커 언제 버려질지 모른다.
하지만 반대로 구조가 좋고 기능이 부족하더라도 당장은 부족하겠지만 수정하는데 비용이 적고
개선과 확장에 유연한 모습을 보인다.


좋은 구조를 만드는 법

패러다임, 설계원칙을 잘 지키고 응집성을 높이고 결합도를 낮추면서 설계하면된다.
하지만 이런 모든 것들을 지키면서 개발하는 것은 어려운 일이다.

기능 개발을 하면서 원칙까지 지키기란 쉽지않다.

그래서 더 쉽게 가능하도록 방향을 제시해주는 것이 아키텍처 패턴이다.
앞서 말한 모든 원칙을 꼼꼼히 지킬 수는 없지만 따라만 해도 일부 지켜지고 기능 개발도 보다
쉽게 할 수 있게 도와준다.

아키텍처를 적용하면 소프트웨어를 통해 이루고자하는 핵심 목표인 도메인을 지킬 수 있따.
UI API DB 같은 외부요소에 도메인이 의존하지 않도록 보호한다.


레이어드 아키텍처

관심사가 같은 코드들을 계층으로 그룹화한 아키텍처를 의미한다.

특징

  1. 계층화로 인한 분리된 책임
  2. 편의에 따라 여러 계층을 추가할 수 있다.
    ex)Presentation, application, domain, persistence
  3. 구조가 쉽고 단순하고 익숙하다.
  4. 데이터베이스 주도 설계가 될 수 있다는 단점도 존재한다.

문제점

프레젠테이션 애플리케이션 펄시스턴스 순으로 의존성이 발생하는데
상위레이어인 프레젠테이션 계층이 하위레이어인 펄시스턴스 계층을 알 수 있는 상황을 만든다.

하위 계층의 변경이 상위 계층의 변경을 유발시킬 수 있다.
펄시스트 계층의 변화가 애플리케이션 계층 변화를 유발 시키고 프레젠테이션까지 변화를 만든다.

테스트도 쉽지않다는 단점도있다.
애플리케이션 계층에 존재하는 비즈니스 로직을 검증하기위해 데이터와 함께 테스트해야한다.
변화가 빈번하게 발생하는 UI와 데이터로부터 영향을 받아 가장 중요한 비즈니스 로직도 수정해야한다.

영속성 계층 변화의 전파

개발자 A가 B에게 DB 구조를 바꿔도 되냐고 물어본다.
개발자 B는 영향 범위를 묻는데 A가 프로젝트 전체라고 대답한다.

관심사를 분리하고자 계층을 나누는데 한 계층의 변화가 프로젝트 전체에 영향을 미치게 된 꼴이다
의존성과 관련된 이 사항을 어떻게 해결할까?


클린 아키텍처

프로젝트 전체가 DB에 의존하는 구조가 문제라면 그렇게 안되게하면 문제가 해결되지않나?
클린아키텍처에선 의존성 역전을 통해 도메인을 의존하게 하면서 문제를 해결했다.
사진처럼 의존성 방향이 한 방향으로 흐르는 것은 레이어드 아키텍처와 큰 차이는 없다.

특징

  1. 핵심 규칙을 담고있는 도메인이 중심
  2. 도메인이 세부사항에 의존하지않음
  3. 팀원이 클린 아키텍처에 익숙하지않을 수 있고 레퍼런스가 적다는 단점이 있다.
  • 세부사항?
    세부사항 또는 외부요소란 우리가 사용하는 라이브러리나 DB를 생각해볼 수 있다.
    Spring과 mysql 같은 코드를 제외한 모든 부분이라고 보아도 된다.
    ex) 입출력 장치, 디비, 웹시스템, 서버, 프레임워크, 통신 프로토콜 등

계층

가장 간단하게 두가지 계층으로 나눌 수 있다.
첫 번째 계층은 Entity와 Use Cases가 있는 계층이다.
이 계층은 비즈니스 로직을 캡처한 계층이며 외부 요소에 대해서는 절대로 알아선 안된다.

두 번째 계층은 외부요소가 있는 계층이다.
어떤 DB를 사용하는지 어떤 프레임워크를 사용해야하는지 같은 것들이 있다.

이 두가지를 분리하는 것이 핵심이다.

상세하게 따지면 4개의 계층으로 구성되어있다.

Entites (엔터프라이즈 비즈니스 규칙)
첫번째는 엔티티 레이어다. 가장 핵심적인 비즈니스 로직이나 규칙을 담고있다.
대부분 프로젝트에서 도매인 패키지에 들어있는 것들이라고 보아도된다.
어떠한 외부 의존성을 가져서도 안된다.

Use Cases (애플리케이션 비즈니스 규칙)
두번째는 유스케이스 레이어이다.
특정 비즈니스 로직을 포함하고 있고 보통 레포지토리에서 객체를 받아와 특정한 행위를 진행하고
업데이트하는 부분을 처리한다.

Interface Adapters 인터페이스 어댑터
세번째는 어댑터 레이어이다. 데이터를 유스케이스에서 사용하는 형태로 변경하거나
외부에서 사용하는 형태를 내부에 적합한 형태로 변환한다.

Frameworks & Drivers
네번째는 Infrastructure 레이어다.
DB와 프레임워크와 같은 외부와 통신 작업을 해주는 것들이다.

이렇게만 보아서는 세부사항을 알기 쉽지않다. 경계가 모호하고 추상적이기 때문이다.

클린 아키텍처는 추상적이라 지침도 따로 존재하지 않는다.

이런 것을 프로젝트에 어떻게 도입할까?

클린 아키텍처를 적용하기에 아주 좋은 레퍼런스로 헥사고날 아키텍처가 존재한다.


헥사고날 아키텍처

클린 아키텍처를 구현하는 방법을 구체화한 아키텍처이다.

특징

클린 아키텍처의 특징과 대부분 일치한다.

  1. 큰 비즈니스 가치를 가지고 있는 도메인 모델에 큰 관심
  2. 레퍼런스가 클린 아키텍처에 비해 많다.
  3. 포트와 어댑터를 구성하고 관리하는데 복잡성이 따르는 단점
  4. 도메인에 라이브러리를 직접 활용하기 어렵다는 단점

각 계층

핵심 업무규칙이 육각형 내부에 위치하는 것이다. 외부의 변경으로부터 보호된다.

육각형의 경계에는 인터페이스가 감싸져있다. 좌측은 어댑터에서 들어오는 입력을 받는 인풋 포트
오른쪽에는 처리 결과를 저장한 아웃풋 포트가 위치한다.
외부의 어댑터들은 핵심 업무규칙에 직접 접근하지 못하고
반드시 사전에 약속된 즉, 인터페이스로 정의된 포트를 통해서만 접근 가능하다.
유스케이스에서 포트를 구현하거나 호출해 외부 요소와 통신을 할 수 있다.

어댑터는 외부 요소를 직접 다룬다.
라이브러리나 프레임워크에 종속적인 코드는 모두 여기 해당된다.
포트를 구현하기도하고 포트를 통해 유스케이스를 직접 호출하기도 한다.

조금 더 직관적인 그림이다.

도메인 계층은 Use Case와 Entity가 있다.

노란색 엔티티는 기업의 업무 규칙 즉 업무도메인을 설계하고 개발한다.
가장 핵심적이고 업무와 직접적으로 연관된 업무규칙을
가장 안쪽에 독립적으로 구성해 외부의 변경으로부터 보호한다.

붉은색 유스케이스는 어플리케이션이 제공하는 기능을 구현한 요소로
도메인 엔티티를 참조해 구현한다.

유스케이스는 양쪽의 어댑터를 통해 외부와 연결된다.
(Controller = Presenter, repository Impl)
어댑터는 외부의 데이터 흐름을 내부 도메인에서 사용할 수 있는
데이터 형식으로 변환해주는 역할을 한다.
프레젠터는 화면의 입력을 도메인에 맞는 데이터로
레포지토리는 외부의 db 또는 api 데이터를 도메인에 맞는 데이터로 변환하는 역할을 한다.

어댑터가 도메인을 참조하는 화살표 즉 의존성의 방향이 안쪽을 향한다.
특히 레포지토리의 방향은 처리의 방향인 좌에서 우로 향하지않고 반대로 향한다.
이것은 의존성 역전이라는 용어를 쓴다.

사실 유스케이스와 어댑터 사이에는 인터페이스라는 것이 위치한다. 이것은 일종의 약속이다.
유스케이스가 제공하는 기능과 필요한 기능을 좌우에 인터페이스로 사전 정의하면
두개의 인터페이스 즉 약속에 따라 프리젠터와 레포지토리는 약속된 방식으로 구현하면 된다.

주문을 접수하는 프로세서로 예를 들어보자.
배민 앱에서 주문을 하면 배달의 민족 주문이 울린다.
주문 발생 시 표시되는 신규 주문 팝업을 확인하고 조리 시간을 변경하고 접수 버튼을 누른다.
주문 번호와 조리시간을 오더 프레젠터에 전달하면 인터페이스에 사전 약속된
주문을 접수하다를 호출해 접수를 시도한다.

주문을 접수하다가 정의된 OrderAcceptService 유스케이스는 다시 인터페이스에 사전 정의된
주문정보를 조회하라를 호출해 조회한다.
조회된 order 엔티티를 통해 주문접수 처리로 형태를 변경하고

인터페이스에 정의된 주문정보를 저장하라를 호출해 저장을 시도한다.
Order Repository는 중계서버에 변경을 요청하고 변경 정보를 로컬 DB에 저장한다.

이런 구조의 장점은

  1. 세부사항 변경 시 도메인 계층의 업무규칙이 영향을 받지 않는다.
    의존성이 안쪽으로만 향하기 때문이다.
    화면을 변경하거나 중계서버의 API 구조가 변경되어도 도메인 계층에는 영향을 주지않는다.

  2. 그래서 바깥쪽 세부사항을 독립적으로 손쉽게 변경이 가능하다.
    도메인 양측의 인터페이스 약속을 지킨다면 윈도우 화면을 획기적인 웹뷰로 바꾸거나
    로컬의 DB를 원격 DB로 변경하는 큰 작업도 진행 가능하다

  3. 이렇게 의존성을 낮추면 각 요소를 독립적으로 테스트 가능하다.
    도메인 계층의 엔티티와 유스케이스는 참조하는 것이 없어 독립적으로 테스트가 가능하고
    테스트 데이터가 필요한 경우 레포지토리 어댑터 대신 주문 정보를 조회/저장하라 라는
    인터페이스만 맞춘다면 테스트 객체로 변경해서 테스트 데이터 제공이 가능할 것이다.

무엇보다 가장 중요한 것은 가장 핵심적인 도메인 즉 업무 규칙이 세부사항의 변경에
영향을 받지 않는다는 것이다.


실제 프로젝트를 헥사고날 아키텍처로 바꾸는 과정

가장 처음에 해야하는 작업은 서비스와 레포지토리의 메서드들을 인터페이스로 추출하는 것이다.
서비스의 퍼블릭 메서드를 추출해서 멤버 유스케이스를 만들고
멤버 Jpa 레포지토리의 메서드들을 추출해서 멤버리포지토리 인터페이스를 만든다.

지금은 애플리케이션 패키지와 데이터베이스 패키지가 양방향 의존성을 가지지만 개선 가능하다.

다음 작업은 도메인을 분리해야한다. JPA멤버를 서비스에서 직접 사용하지않고
도메인의 멤버를 따로 구현해 서비스에서 이를 의존하도록 바꿔 패키지간 양방향 의존성을
끊을 수 있다.

마지막으로 패키지 네이밍만 바꾸면 헥사고날 아키텍처로 바꾸는 과정이 끝이난다.

실제 패키지 구조이다. 이는 레이어드 아키텍처의 패키지 구조이다.

다음 화면은 헥사고날 아키텍처를 적용한 후의 패키지 구조이다.

딱 봐도 복잡해보인다.

파일과 패키지 수도 많이 증가한 모습이다.

규모가 더 크다면 패키지가 특히 많이 증가하게된다.
레이어드 아키텍처와 비교해 코드량과 복잡성이 증가된다.

어떤 아키텍처도 완벽하지않다 의존 관계가 그렇게 복잡하지 않다면 레이어드 아키텍처를 이용하는 것이 더 좋을 수 잇따.

헥사고날 아키텍처는 대규모의 프로젝트를 진행할 때, 프로젝트 일원들이 모두 클린 아키텍처를 이해할 때, 외부요소의 변화가 잦을 때 사용하는게 좋다.


결론

아키텍처는 결국 소프트웨어를 제공하는 목적인 도메인을 외부의 세부사항의 변경으로부터
철저히 보호하는 것이다.

도메인에 집중을 하기위해 아키텍처를 잘 짜야한다는 사실을 알았다.

실제 적용해보기 위해서는 다양한 시도를 해봐야할 것 같다.

profile
개발일기

0개의 댓글