자주 쓰는 서버 구조와 설계 패턴

서버 애플리케이션 개발시 아키택처 수준에서 자주 반복되는 패턴 6가지를 살펴봄

MVC 패턴

  • Model-View-Controller
    • Model : 비즈니스 로직 처리
    • View : 사용자에 대한 응답
    • Controller : 사용자의 요청 처리와 흐름 제어

  • MVC의 핵심 두 가지
    • 비즈니스 로직을 처리하는 모델과 결과를 생성하는 뷰를 분리
    • 애플리케이션 흐름 제어나 사용자 요청처리는 컨트롤러에 집중
  • 관심사(역할)별로 처리할 컴포넌트를 분리해서 서로 영향을 안받게함, 이를 통해 유지보수를 용이하게 만듦

계층형 아키텍처

  • 계층마다 특정 역할을 수행, 상위 계층은 하위 계층만 의존가능
  • 웹 애플리케이션의 일반적인 4계층
    • 표현 (또는 UI)
    • 응용
    • 도메인 (또는 모델)
    • 인프라 (또는 영속)
  • 도메인 계층 없이 단순히 서비스/DAO로만 구성된 계층형 아키텍처를 사용하면 3계층의 계층형 아키텍처가됨
    • 도메인 로직이 인프라와 응용 계층으로 분산되어서 유지보수 하기 어렵게 만들기도 함
update member set status = 20 where member_id = ? and status = 10
int cnt = mdao.updateMemberStatus(id);
if (cnt == 0) {
  // 변경건이 없어서 실패 처리
}
  • 위 코드는 어떤 도메인 로직도 없음
    • 회원의 상태를 변경하고 결과 건수가 없으면 실패 처리
    • 이렇게 도메인 로직이 쿼리나 컨트롤러에 흩어지는것을 방지하려면 한계층으로 모아야함
    • DDD의 전술 패턴이 도메인 로직을 한 곳에 모는 방법중 하나

DDD와 전술 패턴

  • 로직이 복잡한 도메인을 구현할때는 DDD에 소개된 패턴 사용을 검토해 볼 수 있음
  • DDD의 구성요소
    • 엔티티
    • 밸류
    • 애그리거트
    • 리포지토리
    • 도메인 서비스
    • 도메인 이벤트
  • DDD는 위 구성요소들을 사용해서 도메인 로직을 애그리거트 단위로 묶음
    • 복잡한 모델을 애그리거트 단위로 관리함으로써 복잡도를 낮추고 애그리커드에 관련 로직을 모아 응집도를 높힘
// 전술패턴은 도메인 로직을 모으는데 도움이 됨
public class CancelOrderService {
  private OrderRepository orderRepository;

  @Transactional
  public void cancel(OrderNumber orderNum) {
    // 응용서비스는 도메인 모델을 사용해서 사용자 요청을 처리
    Optional<Order> orderOpt = orderRepository.findById(orderNum);
    Order order = orderOpt.orElseThrow(() -> new NoOrderException());
    // 취소 로직은 Order 애그리거트에 위치
    order.cancel();
  }
}
  • 전술 패턴 외에도 바운디드 컨텍스트가 존재
    • 도메인 간 경계를 설정
    • 상위 수준의 복잡한 도메인을 관리하는데 도움을 줌
    • 마이크로서비스 아키텍처와도 잘 어울림

마이크로서비스 아키텍처

  • 하나의 애플리케이션에 모든 것을 구현하는 모놀리식이 일반적이었지만 더 작은 단위로 서비스를 분리하고 각서비스를 연동하는 방식이 마이크로서비스 아키텍처
  • 장단점
    • 모놀리식
      • 장점
        • 배포가 단순, 코드 관리 쉬움
        • 성능을 높이기 위해 복잡한 구조를 가질 필요가 없음
        • 테스트와 디버깅이 쉬움
      • 단점
        • 규모가 커질수록 개발 속도가 느려질수 있음
        • 한 기능의 문제가 전체로 전파 가능
        • 구현 기술 변경이 어려움
        • 작은 변경도 전체 배포 필요
    • 마이크로서비스
      • 장점
        • 독릭적인 배포, 지속적 배포가 용이
        • 성능 확장 용이
        • 기술에 대한 유연성
        • (보통) 개발자의 만족더가 더 높다
      • 단점
        • 테스트와 디버깅이 어려움
        • 인프라가 복잡해짐
        • 소통이 필요
  • [마이크로서비스 아키텍처 구축] 책에서 제시한 6가지 개념
    • 독립적 배포, 도메인을 중심으로 모델링, 자신의 상태를 가짐, 크기, 유연함, 아키텍처와 조직의 맞춤
    • 위 책 저자는 독립적 배포가 가장 중요하다고 함, 이를 위해서는 서비스간 결합도를 최대한 낮춰야함
  • 마이크로서비스가 만능은 아님
  • 모놀리식에서 모듈을 잘 나누어 사용하는 Modular Monolithic도 존재
    • https://docs.spring.io/spring-modulith/reference/

이벤트기반 아키텍처

  • 시스템간 통신할때 이벤트를 사용하는 방식
  • 다음 3가지 구성 요소로 이루어짐
    • 이벤트 생산자
      • 이벤트를 생성해서 브로커에게 전달
    • 이벤트 브로커
      • 관심 있는 소비자에게 전달
    • 이벤트 소비자
      • 이벤트를 받아 적절하게 반응

  • 이벤트는 메시지의 한 형태

CQRS 패턴

  • 시스템이 복잡해지면 상태변경과 상태 조회에 사용하는 데이터에 차이가 발생함
    • 예를 들어 주문의 경우
      • 주문 생성은 주문ID, 회원 ID, 배송지 주소, 상품 ID를 사용
      • 주문 조회는 추가적으로 회원 이름, 상품명 등의 데이터가 사용됨
  • 명령과 조회에서 사용하는 데이터가 다른데 하나의 모델로 모두 구현하면 모델이 복잡해지면 유지보수가 어려워짐
    • 예를 들어 주문 상태를 변경하는 코드에서 주문 조회에서만 사용하는 속성이 함께있다면 코드 분석이 어려워짐
    • 변경하는 코드를 수정하는데 조회하는 코드에 영향이 갈 수 있음
  • 도메인이 복잡해 질 경우 CQRS(Command Query Responsibility Segregation) 패턴을 사용해서 복잡도 문제를 해결가능
    • 명령을 위한 모델과 조회를 위한 모델을 분리하는 패턴

  • 장점
    • 명령과 조회 서로 영향없이 변경가능
    • 조회 모델이 별도로 존재해서 조회 성능을 향상시키기 쉬움
  • 단점
    • 기능별로 모델을 따로 만들어야하므로 작업해야할 코드가 늘어남
      • 단순한 모델에 적용하면 작업량만 늘어나고 이점을 적을 수 있음
    • 구현 기술이 늘어날 수 있음
      • 명령 모델과 조회 모델을 서로 다른 기술로 구현할때가 많음
      • 예를 들어 조회 전용 DB를 따로 사용하는 경우 쓰기 DB에서 조회 DB로 동기화를 하거나 등 부가적인 작업이 필요