• 아키텍처 설계의 기본 원칙부터 실무 적용까지?

  • 소프트웨어 아키텍처: 일관된 방침과 체계적인 구조로 기술 부채를 감소시켜 소프트웨어 어질리티를 얻기위한 방법 → 기술 부채는 결국 생산성 저하를 초래한다

  • 아키텍트에게 필요한 것

    • 설계 능력과 코딩 실력
    • 추상화 능력: 일반화된 패턴과 방침을 도출하는 능력
    • 비즈니스 이해
    • 기술에 대한 호기심
    • 완벽보다는 합리주의
  • 소프트웨어 설계

    • CRC 카드: 클래스, 책임, 협력 클래스를 정의하는 카드로 클래스 모델링의 한가지 기법
    • 비즈니스(도메인) 로직: 업무에 대한 지식과 규칙을 직접적으로 담는 코드
    • 핵심(어플리케이션) 로직: 일련의 업무 처리 절차를 나타내고 도메인 객체 간 동작을 조율하는 로직
    • 아키텍처 스타일: 소스 코드의 구성 방식과 상호 작용 방식에 관한 포괄적인 구조
      • 전체적인 어플리케이션의 구조를 나타낸다
    • 아키텍처 패턴: 특정 문제를 해결하기 위한 구조적인 설계 방식
      • 어플리케이션이 문제를 해결하기 위해 선택한 코드의 구조를 나타낸다
    • 추상화 레벨 ( 하위로 갈 수록 큰 단위 )
      • 클래스: 프로그램의 최소 단위인 구성 요소
      • 컴포넌트: 특정한 동작을 수행하는 책임을 가진 명확한 인터페이스로 정의된 구성 요소
        • 스프링 기준으로는 DI의 대상
      • 모듈: 유즈케이스 실행에 필요한 기능을 제공하기 위한 컴포넌트 집합
      • 아키텍처: 어플리케이션의 기본 구조로 모듈의 집합
        • 시스템의 요소, 관계에 따라 구체화된 시스템의 기본 개념과 특성
    • SOLID 설계 원칙
      • SRP : 단일 책임 원칙. 클래스는 단 하나의 이유로만 변경되어야 한다
        • 유즈케이스나 관련된 사용자 관점에서 생각해야 한다
      • OCP : 개방 폐쇄 원칙. 확장에는 개방되고 수정에는 폐쇄되어 있어야 한다
        • 변경이 적은 코드와 많은 코드를 분리하는데 목적이 있다
      • LSP: 리스코프 치환 원칙. 기본형(부모)으로 동작하는 코드는 파생형(자식)으로도 동작해야 한다
        • 사전 조건을 파생형이 강화해서는 안된다
        • 사후 조건을 파생형이 약화해서는 안된다
        • 불변 조건은 항상 유지되어야 한다
      • ISP: 인터페이스 분리 원칙: 사용하지 않는 메서드는 클라이언트에게 노출되지 않아야 한다
        • 단일 책임 원칙과 연관됨
      • DIP: 의존성 역전 원칙: 상위 모듈은 하위 모듈에 의존하지 않고 추상에 의존해야 한다
        • 인터페이스를 통해 의존해야 하며 인터페이스는 상위 모듈에 존재해야 한다
    • CLEAN 설계 원칙
      • Cohesive (응집성): 컴포넌트나 모듈의 요소가 하나의 목적을 중심으로 모여있는 상태
      • Loosely Coupled (느슷한 결합): 간접 의존을 통해 확장성을 가져야 한다
      • Encapsulated (캡슐화): 관심없는 세부 사항을 숨겨야 한다.
        • 클라이언트가 요구하는 것과 구현 방법을 분리해보자
      • Assertive (단정적): 객체가 다른 객체에 과도하게 의존하지 않고 필요한 책임을 할 수 있어야 한다
      • Nonredundant (비중복): 동일한 코드가 여러 곳에 있으면 안된다
  • 아키텍처 설계

    • 아키텍처 드라이버: 구조를 검토할 때 중요한 고려사항이 되는 제약, 품질 속성, 영향력 있는 기능 요구 사항
    • 시스템 아키텍처 설계 순서
      • 모놀리식과 분산형 중에 선택한다
      • 모놀리식 선택 시
        • 모듈러 모놀리스를 채택하려고 노력해 보자
      • 분산형을 선택 시
        • 서비스 기반 아키텍처, 마이크로서비스 기반 아키텍처와 같은 아키텍처를 선택한다
        • 서비스를 트랜잭션 경계, 사가 패턴 적용 범위에 따라 경계를 선정한다
        • BFF 패턴 적용 여부를 선택한다
      • 어플리케이션 아키텍처를 레이어드 , 파이프라인, 마이크로 커널(플러그인)과 같은 아키텍처를 선정한다
        • 의존성 역전형인 클린,어니언, 헥사고날 아키텍처도 함께 고려해 보자
      • 인프라 접근에 사용할 방식 선정(ORM, CQRS, DAO 등등)
  • 아키텍처 구현

    • 어플리케이션 특성과 유스케이스에 적합한 공통 기능 개발
      • 인증, 승인, 오류 처리, 로깅, 보안, 데이터베이스 접근, 국제화, 캐시, 로그인 유저 상태 관리 등등
    • 프레임워크와 라이브러리를 추상화하여 더 쉽게 활용 가능하도록 개발
    • 프레임워크와 라이브러리 교체 가능성을 염두해 두고 개발
  • 품질 보증과 테스트

    • 시프트 레프트: QA가 소프트웨어 개발 초기단계부터 참여하는 개발 프로세스
      • 품질 보증 활동을 조기에 수행하여 재 작업을 줄이고 품질을 높임
    • 리그레션(회귀) 테스트: 기존 기능이 사양 변경이나 결함 수정 이후에도 정상적으로 동작하는지 검증하는 테스트
    • 단위 테스트를 최소 단위(클래스, 함수) 단위가 아닌 컴포넌트 단위로 취급하여 리팩토링에 대한 부담을 줄이고, 컴포넌트 분할에 도움이 되게 하는 방식도 존재한다
      • 프로세스 로직의 정확성은 통합,E2E 테스트에서 검증하는 것이 유용하다
    • 통합 테스트는 인프라 계층을 제외하고 모듈 단위의 테스트를 수행하는 것을 목표로 한다
      • 유즈 케이스 또는 일부 흐름 테스트에 적합
      • 도메인 엔티티와 유즈케이스를 포함하는 것이 일반적
    • E2E 테스트는 사용자의 동작까지 시뮬레이션하여 테스트를 수행한다
      • 시뮬레이션 툴을 이용하여 자동화하는 것이 주요 특징
    • 부하 테스트는 응답 시간, 처리량이 주요 목표이다
      • 주로 백분위수로 측정하여 SLA 에 만족할 수 있도록 노력해야 한다
      • P95, P99에 주목해 보자
  • 학습과 성장

    • 좋은 책을 선택해 반복해서 읽는 것이 중요
    • 아키텍트를 증명하기에는 호흡이 길기 때문에 조급해 하지 말자
    • 샘플 버전을 만들고 공유해 보자
    • 관계자들에게 그림으로 그려서 설명하는 것은 좋은 방법이다