ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 9장) 9.3 애플리케이션 아키텍처 (vol1 마지막 정리)
    Java & Spring/토비의 스프링 3.1 2021. 9. 24. 12:11
    반응형

    9장 스프링 프로젝트 시작하기

    9.3 애플리케이션 아키텍처

    • 스프링 웹 애플리케이션 아키텍처 결정하기
      • 아키텍처
        • 어떤 경계 안에 있는 내부 구성 요소들이 어떤 책임을 갖고, 어떤 방식으로 서로 관계를 맺고 동작하는지를 규정하는 것
        • 동적인 행위와 관계

    9.3.1 계층형 아키텍처

    • 관심, 책임, 성격, 변하는 이유와 방식이 서로 다른 것들을 분리
      • 응집도가 높아지고 결합도가 낮아짐
      • 장점
        • 불필요한 부분까지 변경이 일어나고 이로 인해 작업이 더뎌지고 오류가 발생할 가능성이 적어짐
        • 어느 부분을 수정할지 파악하기 쉬워지고 변경이 필요한 부분만 각각 변경이 필요하고, 독립적인 발전이 가능
      • 인터페이스와 같은 유연한 경계를 만들어두고 분리하거나 모아주는 작업이 필요
    • 아키텍처와 SoC
      • 지금까지는 성격이 다른 코드가 얽혀 있는 것을 분리하고, 유연한 결합을 위해 인터페이스를 두고, DI 컨테이너로 직접적인 관계를 알지 못하도록 유연한 설계와 구현 전략을 진행
        • 아키텍처 레벨에서 좀 더 큰 단위에 대해서 동일하게 적용 가능
        • 오브젝트를 하나의 모듈 단위라고 생각하면,
          애플리케이션을 구성하는 오브젝트들을 비슷한 성격과 책임을 가진 것들끼리 묶을 수 있음
          • 데이터 엑세스 로직을 담당하는 DAO
          • 비즈니스 서비스 오브젝트
          • 웹을 처리하는 코드 or 독자적인 성격의 코드 등
      • 아키텍처 레벨에서 성격이 다른 것 분리
        • 독자적 개발 가능
        • 테스트 가능
        • 개발과 변경 작업이 빨라짐
        • 구현 방법이나 세부 로직에 서로 영향을 주지 않고 변경될 수 있을 만큼 유연
      • 계층형 아키텍처
        • 위에서 설명한 것을 간략하게 말하면, 책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것 === 계층형 아키텍처
        • === 멀티 티어 아키텍처(Multi tier Architecture)
        • === 3계층 애플리케이션(3-tire or 3-layer Application)
          • 웹 기반 엔터프라이즈 애플리케이션이 일반적으로 세 개의 계층을 갖음
          • 세분화해서 더 작은 단위의 계층으로 나눌 수 있지만, 전형적인 웹 엔터프라이즈 애플리케이션은 책임과 성격으로 보면 3 계층의 논리적인 분류가 가능
    • 3계층 아키텍처와 수직 계층
      • 3 계층
        • 데이터 엑세스 계층
        • 서비스 계층
        • 프레젠테이션 계층
      • 데이터 엑세스 계층
        • DAO 패턴을 보편적으로 사용해서, DAO 계층이라고 불림
        • DB 외에도 ERP, 레거시 시스템, 메인 프레임 등에 접근하는 역할을 해서 EIS(Enterprise Information System) 계층이라고도 불림
        • 대개는 장기적인 데이터 저장을 목적으로 하는 DB 이용이 주된 책임
        • 사용 기술에 따라 세분화된 계층으로 구분 가능
          • 추상화 수준에 따른 구분이기 때문에, 수직적인 계층이라고 부름
            • 그림으로 표현할 때, 세로로 표현
          • 기본 3 계층은 기술 계층이 아닌 역할에 따라 구분 ~> 수평 계층
            • 그림으로 표현할 때, 가로로 표현
        • 새로운 계층을 추가하면 애플리케이션 코드에 지대한 영향 ~> 신중하게 결정
        • 추상 계층을 새로 추가하는 것이 부담스럽고 경우에 따라 유연하게 하위 계층의 API를 활용할 필요가 있다면, 공통 기능을 분리해 유틸리티나 헬퍼 메소드 or 오브젝트로 제공하는 것도 좋은 방법
      • 서비스 계층
        • 잘 만들어진 스프링 애플리케이션의 서비스 계층 클래스는 이상적인 POJO
          • 비즈니스 로직의 핵심을 잘 담고, 쉽게 테스트하고 유연하게 확장
          • DAO 계층을 호출하고 활용해서 만들어짐
          • 때론 데이터 엑세스를 위한 기능 외에 서버나 시스템 레벨에서 제공하는 기반 서비스를 활용할 필요가 존재
            • 웹 서비스와 같은 원격 호출로 정보를 가져오거나 메일, 메시징 등
            • 3계층 어디에서나 접근이 가능하도록 만들 수 있고, 서비스 계층을 통해 사용되도록 제한할 수도 있음
            • 코드의 특징과 장단점, 활용 예를 고려하여 적절하게 결정
        • 특별한 경우가 아니라면, 추상화 수직 계층구조를 가질 필요가 없음
          • 기반 서비스 계층을 사용하는 경우 독립된 계층의 서비스를 이용하는 것으로 봐야함
          • 비즈니스 로직을 담은 서비스 계층과 엔터프라이즈 서비스를 제공하는 기반 서비스 계층은 다르다! (이름 때문에 혼동 X)
        • 기반 서비스 계층 -> 서비스 계층 오브젝트를 호출하는 경우
          • 일반적으로 필요에 따라 서비스 계층 -> 기반 서비스 계층 API 호출
          • 스케쥴링이 기반 서비스 -> 서비스 계층 호출의 대표적인 예
        • 서비스 계층 코드는 기반 서비스 계층의 구현에 종속되면 안됨
          • 서비스 계층의 코드는 추상화된 기반 서비스 인터페이스를 통해서만 접근하도록 설계
            • 특정 구현과 기술에 대한 종속성 제거
          • AOP를 통해 서비스 계층의 코드를 침범하지 않고 부가기능을 추가하는 방법 활용
      • 프레젠테이션 계층
        • 기술과 구조 선택이 까다로움
        • 엔터프라이즈 애플리케이션의 프레젠테이션 계층은 클라이언트 종류와 관계 없이 HTTP 프로토콜을 사용하는 서블릿이 바탕
        • 다른 계층과 달리 클라이언트까지 그 범위를 확장할 수 있음
        • 최근 추세는 프레젠테이션 로직이 Client Side에 이전됨 (CSR)
          • RIA, SOFEA 아키텍처가 대표 예시
    • 계층형 아키텍처 설계의 원칙
      • 대부분의 객체지향 설계 원칙은 아키텍처 레벨의 계층과 관계에 동일하게 적용 가능
        • 각 계층은 응집도가 높으면서 다른 계층과는 낮은 결합
        • 각 계층은 자신의 역할에만 충실, 자신과 관련없는 기술 API의 사용을 삼가
          • 자신의 역할 외에 필요한 작업은 다른 계층에 요청하여 사용
          • 인터페이스를 통해 요청, 계층 간에 사용되는 인터페이스 메소드에는 특정 계층의 기술이 최대한 드러나지 않게 해야함
      • 예외 또한 객체지향 설계 원칙이 적용됨
        • SQLException이라는 JDBC 기술 종속적인 예외를 던지면, 이를 사용하는 서비스 계층에서는 SQLException을 해석해서 예외상황을 분석하고 이를 처리하는 코드를 만들어야함
          • 특정 데이터 엑세스 계층의 구현에 종속되는 강한 결합이 만들어짐
          • 스프링의 DataAccessException 처럼 런타임 예외로 만들어 전달해야함
          • 또한, JDBC, JPA, hibernate 등 특정 구현 방식에 종속되지 않는 추상적인 형태로 만들어줘야함
          • 그래야 낮은 결합도를 유지하며 유연한 변경 가능
      • 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층으로 전달하는 실수도 빈번
        • 계층의 경계를 넘어갈 때는 반드시 특정 계층에 종속되지 않는 오브젝트 형태로 변환
        • 주로, HttpServletRequest, HttpServletResponse, HttpSession과 같은 타입을 서비스 파라메터로 전달하는 경우가 예시
        • 서비스 계층에서 웹과 관련된 예외 발생 ~> 문제의 원인을 찾기 어려워지고 테스트하기 힘들어짐
      • 인터페이스 활용
        • 인터페이스 하나 더 만드는 것이 번거롭다고 클래스를 이용하면 안됨
        • 인터페이스는 각 계층의 경계를 넘어 들어오는 요청을 명확하게 정의하겠다는 의미
        • 단순하게 자바의 interface 키워드를 사용하라는 의미 X
          • 인터페이스에 클래스의 모든 public 메소드를 추가하면 인터페이스 사용 가치가 떨어짐
          • 계층 내부의 예상되는 변화에도 쉽게 바뀌지 않도록 주의해서 만들어야함
          • 다른 계층에서 꼭 필요한 메소드만 노출 (접근제한자 설정)
      • 스프링의 모든 DI는 기본적으로 오브젝트 사이의 관계를 다룸
        • 계층 사이의 경계나 관계에 직접 관여 X
        • 모든 경계에는 오브젝트가 존재하고 그 사이의 관계도 오브젝트 대 오브젝트로 정의됨
          • 스프링의 DI는 계층 사이의 관계에도 적용
          • DI는 계층을 구분해주지 않기 때문에 빈 사이의 의존관계를 만들 때는 주의 필요

    9.3.2 애플리케이션 정보 아키텍처

    • 엔터프라이즈 시스템은 본질적으로 동시에 많은 작업이 빠르게 수행돼야 하는 시스템
      • 애플리케이션을 사이에 두고 흘러다니는 정보를 어떤 식으로 다룰지를 결정하는 일도 아키텍처를 결정할 때 매우 중요한 기준
      • 엔터프라이즈 애플리케이션의 정보를 다루는 두 가지 기준
        • 단순한 데이터로 다루는 경우
        • 오브젝트로 다루는 경우
        • 정보를 단순히 값이나 값을 담기 위한 목적의 오브젝트 형태로 취급
        • DB나 백엔드 시스템에서 가져온 정보를 값으로 다루고 그 값을 취급하는 코드를 만들어 로직을 구현하고, 값을 그대로 뷰와 연결해주는 형태
          • 객체지향 기술이나 언어를 사용하지 않던 시절의 앤터프라이즈 애플리케이션 개발과 크게 다를 바 없음
          • 비즈니스 로직이 DB 저장 프로시저나 SQL에 담겨있는 경우가 많고, DB 리턴 값을 맵이나 단순 결과 저장 오브젝트에 넣어서 전달하는 경우가 많음
            • DB에 무리
        • 데이터 중심 아키텍처는 비즈니스 로직을 어디에 많이 두는지에 따라
          DB에 무게를 두는 구조 vs서비스 계층의 코드에 무게를 두는 구조로 구분
    • DB / SQL 중심의 로직 구현 방식
      • 하나의 업무 트랜잭션에 모든 계층의 코드가 종속되는 경향
        • ex) 사용자 이름으로 id, password, 이름, 가입일자(일부 데이터)만 검색
          • 검색 조건은 SQL로 만들어지고, SQL이 이미 화면에 어떤 식으로 출력될지 알고 있음
          • 모든 계층의 코드는 이름을 이용한 고객 조회라는 업무에 종속
          • 업무의 내용이 바뀌면 모든 계층의 코드도 함께 변경됨
          • 종속적이고 배타적이라 다른 단위 업무에 재사용되기 힘듦
        • 서비스 계층이 의미가 없음 ~> 2계층 구조에서도 비슷하게 발견됨
        • 자바 코드를 단지 DB와 웹 화면을 연결해주는 단순한 인터페이스 도구로 전락시키는 것
      • DB 중심의 업무 단위로 코드를 만들면 애플리케이션 내에 흘러다니는 정보는 항상 단순한 포맷의 데이터
        • 겉으로 보기에는 각 계층이 독립적으로 보이지만, 그 사이를 이동하는 데이터가 일종의 접착제 역할을 해서 강한 결합을 만듦
      • 단점
        • 변화에 취약
        • 객체지향 장점이 활용되지 못하고 각 계층 코드가 긴밀하게 연결됨
        • 중복 제거가 어려움
        • 업무 트랜잭션에 따라 필드 하나가 달라도 거의 비슷한 DAO 메소드를 새로 만들어야함
        • 로직을 DB와 SQL에 많이 담을수록 확장성이 떨어짐
        • 테스트가 어려움
        • DB에 큰 부담
          • 애플리케이션 서버와 그 안의 오브젝트는 비용이 적게 들어감
            • 서버를 늘려 쉽게 확장 가능
            • 여러 대의 서버를 클러스터로 하나의 서버처럼 동작하게 만들 수 있음
            • 같은 작업 대비 DB에 비해 저렴
            • 안정성이 높아지고 코드 검증이 편함
            • 오브젝트 로직을 간단히 검증 가능
            • 객체지향 분석과 모델링 결과로 나온 모델을 가져다 쉽게 오브젝트로 만들어낼 수 있음
    • 거대한 서비스 계층 방식
      • DB에 부하가 걸리지 않도록 저장 프로시저의 사용을 자제하고 복잡한 SQL을 피하면서, 주요 로직은 서비스 계층이 코드에서 처리
      • SQL의 결과를 그대로 담고있는 단순한 오브젝트 or 맵을 이용해 데이터를 주고 받음
        • 대신, 비즈니스 로직을 애플리케이션 코드에 이전
        • 구조가 단순해지고 객체지향 개발의 장점을 살릴 수 있음
        • DAO가 돌려준 정보를 분석, 가공하면서 비즈니스 로직을 적용하는 책임을 서비스 계층으로 이전
      • 거대한 서비스 계층
        • 비즈니스 로직이 복잡해지면 서비스 계층의 코드도 복잡해지고 커짐
        • 여러 메소드로 분산하면 메소드 크기는 줄지만 전체 클래스 코드 양은 그대로
        • 상대적으로 단순한 DAO 로직을 사용하고, 비즈니스 로직의 대부분을 서비스 계층에 집중하는 접근 방법
      • 장점
        • 애플리케이션의 코드에 비즈니스 로직이 담겨 있어서 자바의 장점을 활용해 로직을 구현 가능
        • 테스트하기 수월함
        • DAO가 다루는 SQL이 복잡하지 않음
        • 뷰와 1:1로 매핑되지 않아도 되기 때문에, 일부 DAO 코드는 여러 비즈니스 로직에서 공유하여 사용
        • 각 단위 업무별로 독립적인 개발이 가능 ~> 초기 개발속도가 빠르고 독립적인 개발 가능
      • 단점
        • 서비스 계층의 코드는 여전히 큰 업무 트랜잭션 단위로 집중돼서 만들어져서 코드의 중복이 적지 않게 발생
          • 일반화가 어려움
        • 데이터 엑세스 계층의 SQL은 서비스 계층의 비즈니스 로직의 필요에 따라 만들어지기 쉬움
        • 계층 간의 결합도가 여전히 큼
        • 본격적인 객체 지향적 설계를 적용하기 힘듦
        • 개발자 개인 코딩 습관이나 실력에 따라 비슷한 로직이라도 전혀 다른 스타일의 코드가 나올 수 있음
        • 개발 능력이 떨어지는 경우, 자바 코드의 비즈니스 로직 이해가 SQL에 비즈니스 로직이 있는 것 보다 어려울 수 있음
        • 계층별로 독립된 설계와 개발이 어려움
        • 비즈니스 로직이나 설계에 변경이 생기거나 수정사항이 생기면 코드를 수정하기 쉽지 않을 수 있음
        • 테스트가 불충분하거나 아예 없으면 SQL보다 더 다루기 힘든 코드로 전락할 수 있음
      • 데이터 중심 아키텍처의 특징은 높은 결합도와 낮은 응집도
        • 개발하기 편하지만 중복이 많아지기 쉽고 장기적으로 코드를 관리하고 발전시키기 힘듦

    9.3.3 오브젝트 중심 아키텍처

    • 데이터 중심 아키텍처와의 차이
      • 도메인 모델을 반영하는 오브젝트 구조를 만들고 이를 각 계층 사이에서 정보를 전송하는데 사용
        • 객체지향 분석과 모델링 결과로 나오는 도메인 모델을 오브젝트 모델로 활용
        • 도메인 모델은 DB 엔티티 설계에도 반영 ~> 유사한 형태일 가능성이 높음
    • 데이터와 오브젝트
      • 예시
        • 카테고리와 상품이라는 두 엔티티가 있는 경우
        • Category
        • 필드명 타입 설정
          CategoryId int Primary Key
          Description varchar(100)  
        • Product
        • 필드명 타입 설정
          ProductId int Primary Key
          Name varchar(100)  
          Price int  
          CategoryId int Foreign Key (Category)
        • while(rs.next()) {
              Map<String, Object> resMap = new HashMap<>();
              resMap.put("categoryId", rs.getString(1));
              resMap.put("description", res.getString(2));
              ...
              list.add(resMap);
          }
          • 결과를 맵에 담는DAO 코드
          • 서비스 계층에서는 List<Map<String, Object>> 타입만 보고 어떤 내용인지 알 수 없기 때문에, DAO 메소드에서 두 테이블을 조인해서 각 필드 값을 가져오고, 필드 이름을 키로 갖는 맵에 값을 가져와야 알 수 있는 형태
          • 만약, 웹 페이지 내에서 해당 정보를 수정에서 DB에 다시 반영해야 하는 경우 map이나 배열에 담겨 전달 ~> 데이터 중심의 아키텍처는 SQL 결과에 모든 계층의 코드가 의존하게됨
        • 오브젝트 중심의 경우 도메인 모델 구조를 반영해서 만들어진 오브젝트에 담김
          • public class Category {
                int categoryId;
                String description;
                Set<Product> products;
            
                // getter & setter
            }
            
            public class Product {
                int productId;
                String name;
                int price;
                Category category;
            
                // getter & setter
            }
          • 매번 달라지는 SQL 결과를 담는 오브젝트(위의 Map 같은 경우)와 달리 애플리케이션 어디에서도 사용될 수 있는 일관된 형식의 도메인 정보를 담고 있음
          • 정보를 가공하거나 DB에 수정하는 경우에도 SQL 결과에 의존하지 않고 반영 가능
          • Category에 속한 Product를 가져오는 경우에도, Set<Product> products = myCategory.getProducts(); 로 간단하게 가져올 수 있음
      • 도메인 모델을 따르는 오브젝트 구조를 만드려면, DB에서 가져온 데이터를 도메인 오브젝트 구조에 맞게 변환해줘야 함
        • DAO는 자신이 DB에서 가져와 도메인 모델 오브젝트에 담아주는 정보가 어떤 업무 트랜잭션에 어떻게 사용될지 신경쓰지 않아도 됨
        • 서비스 계층도 DAO에서 어떤 SQL을 사용했는지 몰라도 됨
        • 프레젠테이션 계층 또한 어떤 DAO가 사용되고, 어떤 비즈니스 로직을 가쳤는지 알 필요가 없음
        • 오직 전달된 도메인 오브젝트를 활용해 필요한 정보만을 핸들링
    • 도메인 오브젝트를 사용하는 코드
      • 오브젝트 중심 방식의 비즈니스 로직 구현
        • public int calcTotalOfProductPrice(Category cate) {
              int sum = 0;
              for (Product product : cate.getProducts()) {
                  sum += product.getPrice();
              }
              return sum;
          }
          • 어떤 Dao를 이용해서 Category를 가져왔는지, 어떤 조건인지 알 필요 없이 Category 오브젝트를 통해 Product들을 가져오고, product의 price만 더하는 행위에만 관심
          • 테스트하기 간단하고 로직이 변경될 때도 코드를 수정하기 수월
          • Category 자체가 독립된 오브젝트 ~> 서비스 계층 어디든 Category 상품 가격을 계산할 때는 이 메소드를 사용하면 됨
        • 만약, 데이터 중심 방식이라면 SQL 실행에 비즈니스 로직이 포함될 것이고, join 문이 적용되어 있다면 위의 메소드에 if 문을 통해 값을 필터링하는 등 복잡함이 증가
    • 도메인 오브젝트 사용의 문제점
      • 장점
        • 코드 이해가 쉬움
        • 로직 작성이 수월함
        • 각 영역에서는 도메인 오브젝트 구조만 알고, 해당 오브젝트를 기준으로 로직을 구성할 수 있음
        • 코드의 재사용성이 높아지고, DAO는 작고 효율적으로 만들 수 있음
      • 단점
        • 최적화된 SQL을 매번 만들어 사용하는 경우에 비해 성능이 조금 떨어짐
        • DAO는 비즈니스 로직의 사용 방식을 알지 못해서, 도메인 오브젝트의 모든 필드 값을 다 채워 전달해야하는 경우가 생김
          • 오브젝트에 담긴 필드 수가 많아지면 사용 빈도가 낮은 필드가 존재 ~> 낭비
          • 비즈니스 로직에 따라 필요한 정보가 달라질 수 있기 떄문에 발생하는 문제
        • 가끔 비즈니스 로직에 필요한 필드가 null이 들어가는 경우 NPE가 발생할 수 있음
          • 최적화를 위해 DAO 작성 시에, 비즈니스 로직에서 각 오브젝트를 어디까지 사용할지는 알아야함
      • 단점 해결 방법
        • lazy loading(지연된 로딩)
          • 최소한의 오브젝트 정보만 읽어두고 관계하고 있는 오브젝트가 필요한 경우 다이내믹하게 DB에서 다시 읽어서 제공
          • 도메인 오브젝트를 사용하는 코드는 이런 사실을 전혀 의식하지 않고 처음부터 모든 오브젝트 정보가 제공된다고 생각하고 작성하면 됨
        • 필드가 너무 많다면, 자주 사용되는 것을 골라 별도의 오브젝트로 정의하고 필요에 따라 사용
          • 이에 따라 DAO 메소드가 추가돼야하고, 어느 DAO를 사용할지 서비스 계층에서 알고있어야 해서 계층 사이의 약한 결합이 생김
        • JPA, JDO, 하이버네이트 등과 같은 RDB 매핑 기술인 ORM을 사용(권장)
          • lazy loading을 기본적으로 제공
          • SQL 결과를 가지고 도메인 오브젝트를 만들고 값을 채우는 등의 DAO 코드를 만들지 않아도 되고, 내부적으로 최적화된 SQL을 사용하도록 튜닝 가능
          • 자주 변경되지 않으면서 많은 로직에서 참조하는 레퍼런스 테이블은 ORM이 제공하는 오브젝트 캐시에 담아두고 사용
            • 매번 DB에 접근하지 않아도 돼서 DB 부하 감소
      • 자바 오브젝트
        • 도메인 오브젝트는 자바 오브젝트
        • 원래 데이터를 저장하기 위해 사용하는 것이 아니라 내부 정보를 이용하는 기능도 함께 가지고 있어야함
        • 클래스는 속성과 행위의 조합 ~> 필드와 그에 대한 getter, setter만 가지고 있는 오브젝트는 불충분
    • 빈약한 도메인 오브젝트 방식
      • 빈약한 오브젝트
        • 도메인 오브젝트에 정보만 담겨있고, 정보를 활용하는 아무 기능도 없는 오브젝트
        • 스프링 사용에 흔히 사용
        • 거대 서비스 계층 방식의 하나라고 보면 됨 (그렇다고 비즈니스 로직이 포함된다는 의미가 아님)
      • 비즈니스 로직은 서비스 계층으로 분리
      • SQL에 의존적인 데이터 방식보다 유연하고 간결하지만, 서비스 계층 메소드에 대부분의 비즈니스 로직이 있어서 로직의 재사용성이 떨어지고 중복 가능성이 높음
      • 비즈니스 로직이 복잡하지 않다면 만들기 쉽고 3계층 구조의 특징을 살려 개발 가능
    • 풍성한 도메인 오브젝트 방식
      • 풍성한 도메인 오브젝트(영리한 도메인 오브젝트)
        • 빈약한 오브젝트의 단점을 극복하고, 오브젝트의 객체지향적 특징을 잘 사용하도록 개선
        • public clas Category {
              ...
              List<Product> products;
          
              public int calTotalOfProductPrice() {
                  int sum = 0;
                  for (Product prodcut : this.products) {
                      sum += product.getPrice();
                  }
                  return sum;
              }
          }
          • 서비스 계층의 메소드에 따로 만드는 방식에 비해 도메인 오브젝트 안에 로직을 담는 것이 응집도가 높음
            • 데이터와 이를 사용하는 기능이 한 곳에 모여있기 때문
        • public class InventoryService {
              private CategoryService categoryService;
          
              public void setCategoryService(CategoryService categoryService) {
                  this.categoryService = categoryService;
              }
          
              public void complexInventoryAnlysis() {
                  ...
                  int total = this.categoryService.calTotalOfProductPrice(category)    ;
                  ...
              }
          }
          • 여러 도메인 오브젝트에 대해 로직을 사용해야하는 복잡한 코드라면, 각 비즈니스 로직을 담고 있는 서비스 오브젝트를 DI해서 로직을 담은 메소드를 호출해야함
          • 정보를 담고있는 오브젝트가 있음에도, 그 정보를 다루는 메소드가 별개의 서비스 오브젝트에 분리되어 있기 때문
        • public class InventoryService {
              public void complexInventoryAnlysis() {
                  int total = category.calTotalOfProductPrice(category);
              }
          }
          • 풍성한 도메인 오브젝트 방식을 사용하면, service를 DI할 필요 없이 가지고 있는 도메인 오브젝트로 해결 가능
          • 도메인 오브젝트를 사용한다는 점에서 빈약한 도메인 오브젝트 방식과 비슷하지만, 구현 코드는 간결하고 객체지향적
          • 객체지향 분석과 설계를 통해 만들어진 도메인 모델의 정보를 정적인 구조 뿐만 아니라 동적 동작방식에도 적극 활용 가능
        • 도메인 오브젝트에 비즈니스 로직을 넣는다고 서비스 계층 오브젝트가 필요 없어지는 것이 아님
          • 도메인 오브젝트가 직접 데이터 엑세스 계층이나 기반 계층 or 다른 서비스 계층의 오브젝트에 접근할 수 없기 때문에 서비스 계층이 필요
          • 도메인 오브젝트는 스프링 컨테이너가 관리하는 오브젝트(빈)이 아니기 때문에, DAO 오브젝트를 DI 받을 수 없음 (책임과 관심이 아니기도 하지만)
          • 따라서, DAO와 기반계층 오브젝트를 DI 받아 사용할 수 있는 서비스 계층의 코드가 필요
      • 스프링 빈으로 관리되는 3계층의 오브젝트들은 도메인 오브젝트를 자유롭게 이용할 수 있지만, 반대는 안됨
      • 빈약한 도메인 오브젝트 방식보다 서비스 계층의 코드가 간결
        • 비즈니스 로직 코드를 이해하기 쉬움
        • 빈약한 도메인 오브젝트를 피하고, 도메인 오브젝트가 스스로 처리 가능한 기능과 도메인 비즈니스 로직을 갖도록 만드는 것이 바람직
        • 빈약한 도메인 오브젝트가 항상 나쁜 방식은 아님
          • 도메인 모델링과 도메인 오브젝트 개발이 선행되고, 개발자가 이 사실을 모른다면 빈약한 도메인 오브젝트 방식이 혼란을 피하고 쉽게 접근하는 대안이 됨
          • 하지만, 시스템이 복잡해지면 단점이 명확해짐
    • 도메인 계층 방식
      • 도메인 계층의 역할과 비중을 극대화하려면, 기존의 풍성한 도메인 오브젝트 방식으로는 불가능
      • 도메인 계층 방식
        • 도메인 오브젝트가 기존 3계층과 같은 레벨로 격상되어 하나의 계층을 이루게하는 도메인 계층 방식
        • 도메인 오브젝트들이 하나의 독립적인 계층을 이뤄 서비스 계층과 데이터 엑세스 계층 사이에 존재하도록 설계
      • 특징
        • 도메인에 종속적인 비즈니스 로직의 처리는 서비스 계층이 아닌 도메인 계층의 오브젝트 안에서 진행
        • 도메인 오브젝트가 기존 데이터 엑세스 계층이나 기반 계층의 기능을 직접 활용 가능
          • 스프링이 관리하지 않는 도메인 오브젝트에 DI를 적용하기 위해 AOP가 필요
          • 스프링 AOP 대신 AspectJ AOP를 사용하여 클래스의 생성자가 호출되면서 오브젝트가 만들어지는 시점을 조인 포인트로 사용 가능, 일반 오브젝트에도 AOP 부가기능 적용 가능
          • 오브젝트의 수정자 메소드나 DI용 애노테이션을 참고하여 DI 가능한 대상을 스프링 컨테이너에서 찾아 DI 해주는 기능을 도메인 오브젝트가 생성되는 시점에 이용
      • 도메인 오브젝트 기능의 제약이 사라지지만, 여전히 서비스 계층의 역할은 필요
        • 여러 도메인 오브젝트의 기능을 조합해서 복잡한 작업을 진행해야하는 경우
        • 도메인 계층을 거치지 않고 데이터 엑세스 계층으로부터 정보를 가져와 클라이언트에 제공해야하는 경우
        • 트랜잭션 경계 설정 or 특정 도메인 로직에 포함되지 않는 애플리케이션에서 필요로 하는 기반 서비스를 이용해야하는 작업
        • 서비스 계층이 위의 경우들의 인터페이스 역할을 담당
      • 도메인 오브젝트를 독립 계층으로 만드려고 할 때, 도메인 계층을 벗어나서도 사용되게 할지 말지 결정해야함
        • 모든 계층에서 도메인 오브젝트를 사용
          • 편리한 방법이지만, 모든 계층에서 도메인 오브젝트를 사용하기 떄문에 심각한 혼란을 초래할 수 있음
          • DB나 백엔드 시스템에 작업 결과를 반영하거나 도메인/비즈니스 로직 등이 담긴 도메인 오브젝트를 프레젠테이션 계층이나 뷰 등에서 사용하게 하는 것은 위험
          • 확실한 정책을 정해두거나 AspectJ의 정책/표준 강제화 기능을 사용하여 해결
            • 간단한 포인트컷 표현식을 통해 특정 계층의 오브젝트가 사용할 수 있는 메소드의 범위를 제한
        • 도메인 오브젝트를 도메인 계층에서 벗어나지 못하게 설정
          • 도메인 계층 밖으로 전달될 때는 별도로 준비된 정보 전달용 오브젝트에 도메인 오브젝트의 내용을 복사해서 전달 ~> DTO
          • DTO는 기능을 가지고 있지 않기 때문에 안전하고, 도메인 오브젝트를 외부 계층의 코드로부터 보호해줌
          • 하지만, 도메인 오브젝트와 비슷한 구조를 가진 오브젝트를 따로 만들어야하고 이를 매번 변환해야하는 번거로움이 존재
          • AOP와 같은 방법을 이용해 변환을 자동으로 해주도록 할 필요가 있음
        • 위의 두 방식의 좋고 아님을 정의할 수 없기 때문에, 각 프로젝트의 특징과 개발 방법에 따라 설정
      • 도메인 계층의 오브젝트는 짧은 라이프 사이클을 가짐
        • 사용자별 요청에 대해 독립적인 상태를 유지하고 있어야하기 때문
        • 상태 정보를 담고 있기 때문에, 싱글톤으로 사용 불가
        • DAO나 Controller, 스프링 외의 라이브러리를 통해 오브젝트가 만들어지는 경우가 많기 때문에 스프링이 관리하는 빈으로 등록 불가 ~> AspectJ의 AOP를 통한 DI
      • 여러 제약과 불편을 감수함에도, 복잡하고 변경이 잦은 도메인을 가진 경우는 사용
        • 도메인 계층이 응집도가 높기 때문에 단위 테스트 작성이 용이해짐
        • 복잡하지 않은 애플리케이션에서 사용한다면 큰 부담
    • DTO와 리포트 쿼리
      • 리포트 쿼리(Report query)
        • DB 쿼리의 실행 결과를 담는 경우
        • 리포트를 출력하기 위해 생성하는 쿼리라는 의미로, 종합 분석 리포트처럼 여러 테이블에 걸쳐 존재하는 자료를 분석하고, 그에 따른 분석/통계 결과를 생성하는 쿼리라는 의미
        • DB 쿼리 실행 결과를 담을만한 적절한 도메인 오브젝트를 찾을 수 없기 때문에, DTO라고 불리는 단순한 자바빈이나 key, value 값을 갖는 맵에 담아 전달해야함
      • DB 쿼리 하나로 최종 결과를 만들기 힘들어 데이터를 가공하고 분석하는 작업이 필요한 경우 사용
      • 웹 서비스 등의 시스템과 자료를 주고 받을 때, 전송 규약에 맞춰 도메인 오브젝트에 담긴 정보를 가공해야할 경우에도 사용

    9.3.4 스프링 애플리케이션을 위한 아키텍처 설계

    • 계층구조를 어떻게 나눌 것인가와 애플리케이션 정보를 어떻게 다룰지를 결정하는 것이 기본
    • 그 위에 각 계층에 사용될 구체적 기술의 종류와 수직 추상화 계층의 도입, 세세한 기술적인 조건을 결정
    • 계층형 아키텍처
      • 스프링에서 가장 많이 사용되는 구조는 3계층 구조
      • 3계층은 논리적이고 개념적인 구분
        • 오브젝트 단위로 끊어져서 만들어지는게 아님
        • 서비스 계층을 굳이 도입하지 않아도 될 만큼 비즈니스 로직이 단순하면 서비스 계층과 데이터 액세스 계층을 통합해서 사용 가능
          • 트랜잭션 경계설정 위치를 DAO 메소드로 삼으면 됨
        • 프레젠테이션 계층에 서비스 계층을 통합하는 방법도 가능
          • 스프링에서는 권장되지 않음
          • AOP를 이용해 트랜잭션의 경계를 설정하기 애매하기 때문
            • 트랜잭션 전파를 통해 조합하기 애매함
      • 프레젠테이션 계층
        • MVC라는 이름의 패턴 or 아키텍처를 주로 사용
        • 스프링의 대표적인 프레젠테이션 기술은 SpringMVC
          • MVC 패턴을 지원하고, MVC 중 부담을 가장 많이 지닌 컨트롤러에 해당하는 부분을 다시 세분화해서 여러 단계의 오브젝트로 만들 수 있도록 설계됨
        • 클라이언트까지 확장 가능
          • SOFEA ~> 프레젠테이션 계층 코드가 서버 ~> 클라이언트로 다운로드
          • 클라이언트 장치 안에서 동작하며 서버의 여러 서비스 or 부분 프레젠테이션과 통신하는 구조
    • 정보 전송 아키텍처
      • 도메인 오브젝트 방식
        • 스프링의 기본 기술에 가장 잘 들어맞고 쉽게 적용 가능한 오브젝트 중심 아키텍처
        • 빈약한 도메인 오브젝트 방식으로 시작
        • 도메인 오브젝트를 계층 간 정보 전송을 위해 사용하고, 각 계층의 코드에서 활용
        • 도메인 오브젝트에 단순한 기능을 추가해보는 연습 필요
        • 도메인 오브젝트를 이용해 애플리케이션 정보를 일관된 형태로 유지하는게 스프링에 가장 잘 들어맞음
        • 도메인 계층에 DI를 적용하기 위해 스프링의 고급 기술을 활용해야하고 여러 고려 사항이 많으므로 충분한 사전 학습 및 검증이 선행돼야함
    • 상태 관리와 빈 스코프
      • 애플리케이션은 하나의 HTTP 요청의 범위를 넘어 유지해야하는 상태정보가 존재
      • 서버 기반의 애플리케이션의 무상태성(stateless)
        • 하나의 애플리케이션이 동시에 수많은 사용자의 요청을 처리하기 위해 매번 간단한 요청을 받아 결과를 돌려주는 방식으로 동작
        • 따라서, 서버의 자원이 특정 사용자에게 일정하게 할당되지 않음
        • 클라이언트의 요청을 처리하는 매우 짧은 시간 동안만 현재 상태정보가 보관되고, 요청 결과를 return한 후 바로 폐기
      • 애플리케이션의 상태와 장시간 진행되는 작업정보의 유지
        • URL, 파라미터, 폼 히든 필드, 쿠키 등을 이용
        • 많은 양의 정보는 계속해서 주고받을 수 없기 때문에 중요한 상태정보는 파일 시스템, 데이터그리드, DB 등에 저장되기도 함
        • 고급 상태 관리 기법을 이용할 수도 있음
        • 스프링을 이용해서 상태유지 스타일의 애플리케이션을 얼마든지 만들 수도 있음
          • 싱글톤 외에도 다른 스코프를 갖는 빈을 간단하게 생성 가능
      • 스프링은 기본적으로 상태가 유지되지 않는 빈과 오브젝트 사용을 권장
        • 웹의 생리에 가장 잘 들어맞고 개발이 쉬움
        • 서버를 여러 대로 확장하기 쉬움
        • 웹 클라이언트에 폼 정보를 출력하고 이를 수정하는 등의 작업을 위해서는 HTTP 세션을 적극 활용
    • 서드파티 프레임워크, 라이브러리 적용
      • JavaEE의 세부 기술과 함께 사용 가능
        • JSP, JavaMail, JPA 등
      • 스프링이 직접 지원하는 오픈소스 ORM(Hibernate, iBatis), 오픈소스 JPA(OpenJPA, EclipseLink), 웹 프레임워크 (스트럿츠 1/2 등), 다양한 OXM 라이브러리 등 적용 가능
    • 스프링이 지원하는 기술의 의미
      • 해당 기술을 스프링의 DI 패턴을 따라 사용 가능
        • 프레임워크나 라이브러리의 핵심 클래스를 빈으로 등록할 수 있게 지원해주는 것으로 이해
        • 프로퍼티를 이용해 세부 설정을 조정 가능
        • DI를 통해 다른 오브젝트에서 손쉽게 활용 가능
        • 추상화 서비스를 통해 다른 리소스에 투명하게 접근 가능
        • ex) 하이버네이트는 설정파일 정보, 설정 값의 프로퍼티, DB 연결 정보를 담은 Configuration 오브젝트를 통해 SessionFactory를 만들어야함
          • 스프링에서는 하이버네이트의 SessionFactory를 스프링이 제공하는 빈을 등록하는 것만으로 간단히 생성 가능한 LocalSessionFactoryBean 클래스 제공
        • 스프링 외의 기술을 접목할 때는, 가장 먼저 스프링 빈으로 등록해서 DI가 가능한지가 중요
          • 빈으로 등록해서 사용할 수 있는 구조로 핵심 API나 클래스가 만들어져 있지 않다면, Factory 빈을 도입해서 사용
      • 스프링의 서비스 추상화가 적용됐다
        • 서비스 추상화 적용 ~> 기능을 제공하는 기술에 대한 일관된 접근 방법을 정의
          • 위의 첫 번째 방법은 사용할 기술을 스프링 빈으로 등록하고 설정 가능하도록 지원만 해줬을 뿐, 사용 기술의 API는 애플리케이션에 그대로 노출
          • 서비스 추상화는 서드파티 프레임워크를 적용 가능하고 필요에 따라 호환 가능한 기술로 쉽게 교체해서 사용
        • 스프링의 서비스 추상화
          • 다양한 비표준 기술과 영역에 확장해서 적용한 것
          • 표준 기술 스펙과 다른 점은, 서비스 추상화는 이미 존재하는 다양한 기술의 공통점을 분석해서 추상화를 했다는 점
          • 따라서, 추상 서비스 인터페이스를 구현해서 각 기술과 연동하는 어댑터 클래스가 필요
          • 서비스 추상화 인터페이스를 구현한 클래스는 모두 스프링 빈으로 등록되도록 만들어졌고, 세부 기술 특성에 맞는 설정이 손쉽게 가능하도록 다양한 프로퍼티 제공
      • 스프링이 지지하는 프로그래밍 모델을 적용
        • 대표적인 예가 스프링의 데이터 엑세스 기술에 대한 일관된 예외 적용
        • 데이터 엑세스 기술의 종류에 상관없이 일관된 예외 계층구조를 따라 예외가 던져짐
        • 서비스 계층의 비즈니스 로직을 담은 코드가 데이터 엑세스 계층의 기술에 종속되지 않게 도움
      • 템플릿/콜백이 지원됨
        • 반복적이고 이해하기 힘든, 추상화하기 힘든 코드를 간편하게 사용할 수 있도록 템플릿/콜백 기능을 제공
        • 대부분의 템플릿 클래스는 빈으로 등록해서 필요한 빈에서 DI 받아서 사용
    • 스프링이 어떤 기술을 지원한다는 건, 스프링이 지지하는 개발철학과 프로그래밍 모델을 따르면서 해당 기술을 사용할 수 있다는 의미
      • 스프링에 새로운 기술을 연동하려면 스프링의 프로그래밍 모델과 지지하는 개발철학을 따르며 위의 네 방법을 이용
      • 가장 기초는 스프링 빈으로 해당 기술의 핵심 오브젝트가 등록되도록 만드는 것
      • 코드에 의한 초기화 작업이 필요 ~> 팩토리 빈을 만들어 사용
        • 애플리케이션 내의 빈들이 새로운 기술에 대해 빈을 DI하는 방법으로 접근하고,
          기존 빈 오브젝트를 새로 추가할 기술에서 사용할 수 있게 해줄 필요가 있음
      • 서비스 추상화를 시도하여 유연한 설정과 테스트에 용이하게 구현
      • 템플릿 / 콜백 적용
        • 네트워크 접근이나 파일 IO처럼 반복적으로 try/catch/finally 블록이 필요한 기술에 적용
      • AOP나 예외 전환 적용
    반응형

    댓글

Designed by Tistory.