티스토리 뷰

어플리케이션 아키텍처

  • 아키텍처
    • 어떤 경계 안에 있는 내부 구성요소들이 어떤 책임을 갖고 있고, 어떤 방식으로 서로 관계를 맺고 동작하는지를 규정하는 것

계층형 아키텍처

  • 관심, 책임, 성격, 변하는 이유와 방식이 서로 다른 것들을 분리하고 분리된 각 요소의 응집도는 높이고 서로의 결합도는 낮춰주는 방식
  • 책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것

3계층

  • 데이터 엑세스 계층 (DAO)
    • DB, ERP, 레거시 시스템, 메인 프레임 등에 접근하는 역할
    • 사용 기술에 따라서 혹은 추상화 수준에 따라서 수직 구분 (785p)
      • 추상화 계층은 필요하다면 얼마든지 추가 가능
  • 서비스 계층
    • 비즈니스 로직을 담고 있음
    • 잘 만들어진 스프링 어플리케이션의 서비스 계층 클래스는 이상적인 POJO 로 작성됨
    • DAO 계층, 서버나 시스템 레벨에서 제공하는 기반 서비스를 호출해서 사용
    • 787p 그림 9-16
    • 원칙적으로 서비스 계층 코드가 기반 서비스 계층의 구현에 종속되어선 안 됨. 즉, 서비스 계층의 코드는 추상화된 기반 서비스 인터페이스를 통해서만 접근하도록 만들어서 특정 구현과 기술에 대한 종속성을 제거해야 함.
      • e.g. MailSender
  • 프레젠테이션 계층
    • 주로 웹 기반의 UI 를 만들어내고 그 흐름을 관리
    • 대부분의 엔터프라이즈 어플리케이션을 사용하는 클라이언트들은 HTTP 프로토콜을 선호. 따라서 이런 클라이언트와 연결돼서 동작하는 프레젠테이션 계층은 자바에서 http 프로토콜을 처리하는 가장 기본 엔진인 서블릿 기술을 바탕으로 함.

계층형 아키텍처 설계의 원칙

  • 각 계층은 응집도가 높으면서 다른 계층과는 낮은 결합도를 유지해야 함
  • 각 계층은 자신의 계층의 책임에만 충실
    • e.g. 데이터 엑세스 계층에 비즈니스 로직을 담거나 웹 파라미터를 파싱하는 코드나 결과를 화면에 어떻게 뿌릴지 결정하는 코드가 들어가면 응집도가 낮아짐
  • 계층 간 결합이 생기면 유연성이 떨어지기 때문에 각 계층의 내부 구현이 변화되면 다른 계층의 코드도 함께 수정해줘야 함.
    • 또한 코드의 중복이 일어날 가능성이 높고 전체 코드를 이해하기 힘들어짐.

실수 예시

  • 잘못된 예시
    public ResultSet findUsersByName(String name) throws SQLException

  • 올바른 예시
    public List<User> findUsersByName(String name) throws DataAccessException


  • 잘못된 예시

    • 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층으로 전달하는 것
    • e.g. HttpServletRequest, HttpSession 과 같은 타입을 서비스 계층 인터페이스 메소드의 파라미터 타입으로 사용하면 안 됨
  • Q. 790p. 계층 사이의 호출은 인터페이스를 통해 이뤄져야 한다(?)

어플리케이션 정보 아키텍처

  • 어플리케이션을 사이에 두고 흘러다니는 정보를 어떤 식으로 다룰지를 결정하는 일도 아키텍처를 결정할 때 매우 중요한 기준이 됨
  • 데이터 중심 아키텍처
    • DB/SQL 중심의 로직 구현 방식
    • 거대한 서비스 계층 방식
  • 오브젝트 중심 아키텍처
    • 빈약한 도메인 오브젝트 방식
    • 풍성한 도메인 오브젝트 방식

데이터 중심 아키텍처

  • 애플리케이션에 흘러다니는 정보를 단순히 값이나 값을 담기 위한 목적의 오브젝트 형태로 취급하는 구조
  • DB 에서 돌려주는 내용을 그대로 맵이나 단순 결과 저장용 오브젝트에 넣어서 전달.

DB/SQL 중심의 로직 구현 방식

  • 하나의 업무 트랜잭션에 모든 계층의 코드가 종속되는 경향
  • 주요 비즈니스 로직이 SQL과 DB에 존재
    • SQL의 결과는 컬럼 이름을 키로 갖는 맵에 저장되거나 조회 페이지에 필요한 네 가지 정보를 담을 수 있는 단순한 오브젝트에 저장되어 전달됨.
  • 장점
    • 처음에는 개발하기 쉬움
  • 단점
    • 자바 코드를 단지 DB와 웹 화면을 연결해주는 단순한 인터페이스 도구로 전락시킴
    • 변화에 매우 취약하다
      • 각 계층의 코드가 긴밀하게 연결되어 있기 떄문
    • 중복 제거가 쉽지 않음

거대한 서비스 계층 방식

  • DB 에 많은 로직을 두는 개발 방법의 단점을 피하면서 어플리케이션 코드의 비중을 높이는 방법.
  • 여전히 SQL 의 결과를 그대로 담고 있는 단순한 오브젝트 또는 맵을 이용해 데이터를 주고 받음
  • 상대적으로 단순한 DAO 로직을 사용하고 비즈니스 로직의 대부분을 서비스 계층에 집중하는 방식 -> 거대한 서비스 계층 (fat service layer)
  • 장점
    • 어플리케이션 코드에 비즈니스 로직이 담겨 있기 때문에 자바 언어의 장점을 활용해 로직 구현 가능. 테스트 수월
  • 단점
    • SQL은 서비스 계층의 비즈니스 로직의 필요에 따라 만들어지기 쉬움
      • 계층 간 결합도가 여전히 크다
      • 비슷한 코드가 중복돼서 나타나기 쉬움

데이터 중심 아키텍처 문제점

  • 계층 사이의 결합도가 높고 응집도가 떨어진다.

오브젝트 중심 아키텍처

  • 도메인 모델을 반영하는 오브젝트 구조를 만들어두고 그것을 각 계층 사이에서 정보를 전송하는 데 사용
  • 오브젝트를 만들어두고 오브젝트 구조 안에 정보를 담아서 각 계층 사이에 전달하게 만드는 것

데이터 vs 오브젝트

  • 예시
    • Category, Product 라는 두 개의 entity 가 있는 경우
    • 조건에 맞는 모든 카테고리와 상품 정보를 가져와서 화면에 출력하는 기능 구현
데이터
//sql 결과를 맵에 담는 DAO 코드
while (rs.next()) {
    Map<String, Object> resMap = new HashMap<>();
    resMap.put("categoryId", rs.getString(1));
    resMap.put("description", rs.getString(2));
    //...
    list.add(resMap);
}
  • 서비스 계층에 전달되느 것은 List<Map<String, Object>> 타입
  • 위 결과를 사용하는 서비스 계층의 코드에서는 DAO 메소드에서 가져온 필드 값의 종류, 이름을 알아야만 한다. 또한 DAO 에서 필드 개수나 순서, 이름을 바꾸면 서비스 계층의 코드도 함께 변경해야 함.
  • DAO 가 만드는 SQL 결과에 모든 계층의 코드가 의존하게 됨
오브젝트
public class Category {
    int categoryid;
    String description;
    //자바 레퍼런스를 통해 products 참조 가능
    Set<Product> products;
}

public class Product {
    int productid;
    String name;
    int price;
    //자바 레퍼런스를 통해 category 참조 가능
    Category category;
}
  • 어플리케이션 어디에서도 사용될 수 있는 일관된 형식의 도메인 정보를 담고 있음
  • dao는 자신이 DB에서 가져와서 도메인 모델 오브젝트에 담아주는 정보가 어떤 업무 트랜잭션에서 어떻게 사용될지 신경쓰지 않아도 됨
  • 서비스 계층 또한 DAO 에서 어떤 sql을 사용했는지 몰라도 됨

도메인 오브젝트 사용

  • 장점

    • 코드를 이해하기 쉽고 로직 작성이 수월함
      • e.g. category 내 product price 합 구하기
      • e.g. 특정 product 가 속한 category 의 product 가 몇 개 인지 구하기
    • 테스트 수월
  • 단점

    • 최적화된 sql 을 매번 만들어 사용하는 경우에 비해 성능상 손해
    • 도메인 오브젝트의 모든 필드 정보를 채워서 전달하는 것이 낭비일 수 있음
    • 해결방법
      • 지연된 로딩 기법
      • ORM 기술 사용

빈약한 도메인 오브젝트 방식

  • 도메인 오브젝트에 정보만 담겨 있고 정보를 활용하는 아무런 기능도 갖고 있지 않는 경우
  • 다루는 정보의 구조가 다를 뿐이지 빈약한 도메인 오브젝트 방식은 데이터 중심 아키텍처의 거대 서비스 계층구조와 비슷하다
    • 서비스 계층의 메소드에 대부분의 비즈니스 로직이 들어 있기 때문에 로직의 재사용성이 떨어지고 중복 문제가 발생하기 쉬움

풍성한 도메인 오브젝트 방식

  • 빈약한 도메인 오브젝트의 단점을 극복하고 도메인 오브젝트의 객체지향적인 특징을 잘 사용할 수 있도록 개선한 것
  • 비즈니스 로직 중 특정 도메인 오브젝트나 그 관련 오브젝트가 가진 정보와 깊은 관계가 있는 로직을 서비스 계층의 코드가 아니라 도메인 오브젝트에 넣어주고 서비스 계층의 비즈니스 로직에서 이를 재사용하게 만드는 것
public class Category {
    int categoryid;
    String description;
    List<Product> products;

    public int calcTotalOfProductPrice() {
        int sum = 0;
        for (Product product : products) {
            sum += product.getPrice();
        }
        return sum;
    }
}
public class InventoryService {

    //DI
    private CategoryService categoryService;

    public void complexInventoryAnalysis() {
        //...
        int total = this.categoryService.calcTotalOfProductPrice(category);

    }
}
  • 각 비즈니스 로직을 담고 있는 서비스 오브젝트를 DI 해서 로직을 담은 메소드를 호출
public class InventoryService {
    public void complexInventoryAnalysis() {
        //...
        int total = category.calcTotalOfProductPrice();

    }
}
  • Category 오브젝트에게 필요한 계산 작업을 요청.

  • CategoryService 를 DI 받을 필요 없음.

  • 간결하고 객체지향적인 코드

  • 비슷한 코드가 여기저기 비즈니스 로직에 중복되어 나타나지 않음

  • 풍성한 도메인 오브젝트 방식을 사용하더라도 서비스 계층은 여전히 필요하다.

    • 도메인 오브젝트는 스프링이 관리하는 빈 오브젝트가 아님. DAO 나 서비스 오브젝트 같은 빈 기능 사용 불가능
    • 수식 계산이나 조건에 따른 데이터의 변경 또는 자신이 가진 정보에 대한 분석 같은 도메인 오브젝트 자신에 국한된 로직은 도메인 오브젝트 안에 추가할 수 있지만 그 결과를 DB 에 저장하거나 메일로 발송하거나 DB를 검색해서 원하는 정보를 가져와 활용하는 작업은 도메인 오브젝트에서는 불가능. 서비스 계층의 코드가 필요.

도메인 계층 방식

  • 도메인 오브젝트가 스스로 필요한 정보를 DAO를 통해 가져올 수 있고, 생성이나 변경이 일어났을 때 직접 DAO에게 변경사항을 반영해달라고 요청하게 만들고 싶다.
  • 도메인 오브젝트가 기존 3계층과 같은 레벨로 격상되어 하나의 계층을 이루게 하는 것
    • 서비스 계층의 도움 없이 비즈니스 로직의 대부분의 작업을 수행
  • 도메인 오브젝트는 스프링 빈이 아니기 때문에 DI를 적용하기 위해서는 특별한 설정이 추가되어야 함
    • Aspect J 활용 -> 오브젝트가 만들어지는 시점을 조인 포인트로 사용

도메인 계층 도입시 고려할 점

  • 도메인 오브젝트가 도메인 계층을 벗어나서도 사용되게 할 것인지?
    • 모든 계층에서 도메인 오브젝트 사용
      • 막강한 기능을 가진 도메인 오브젝트를 프레젠테이션 계층이나 뷰 등에서 사용하게 해주면 이를 함부로 사용하는 위험 발생
        • 개발 정책
        • AspectJ 정책/표준 강제화 기능
    • 도메인 오브젝트는 도메인 계층을 벗어나지 못하도록 함
      • 도메인 계층 밖으로 전달될 때는 별도의 정보 전달용 오브젝트(Data Transfer Object) 에 도메인 오브젝트 내용을 복사해서 넘겨줌
  • 도메인 계층이 유용한 경우
    • 매우 복잡하고 변경이 잦은 도메인을 가졌을 때
      • 도메인이 가진 복잡함을 객체지향적인 설계의 모든 장점을 동원해서 유연하게 대응 가능

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

계층형 아키텍처

  • 3계층 아키텍처 응용
    • 서비스 계층과 데이터 엑세스 계층을 통합
    • 프레젠테이션 계층과 서비스 계층을 통합 -> 권장 X
      • 트랜잭션 경계 설정 애매
    • 스프링을 처음 학습한다면 3계층 구조에 먼저 익숙해진 후 차차 다양한 방식으로 계층구조의 통합과 분산 시도

정보 전송 아키텍처

  • 우선 빈약한 도메인 오브젝트 방식으로 시작하는 것이 가장 쉽다.
    • 가능하다면 도메인 오브젝트에 단순한 기능이라도 추가하도록 노력해보는 게 좋다.
  • 객체지향적인 도메인 분석과 모델링에 자신이 있고 도메인 오브젝트 설계와 구현, 독립적인 테스트를 자유롭게 적용할 수 있다면 도메인 계층 방식 도입

상태 관리와 빈 스코프

  • 아키텍처 설계시 ‘상태 관리’ 를 어떻게 할 것인지 신경써야 함.
    • 스프링은 기본적으로 상태가 유지되지 않는 빈과 오브젝트 사용을 권장
    • 클라이언트, 백엔드에 저장
    • HTTP 세션 활용
    • 싱글톤 이외의 다른 스코프를 갖는 빈 활용

스프링이 지원하는 기술이란?

  • 해당 기술을 스프링의 DI 패턴을 따라 사용 가능
    • 혹은 프레임워크나 라이브러리의 핵심 클래스를 빈으로 등록할 수 있게 지원
  • 스프링의 서비스 추상화가 적용
    • 비슷한 기능을 제공하는 기술에 대한 일관된 접근 방법을 정의해줌
    • 필요에 따라 호환 가능한 기술로 손쉽게 교체해서 사용
  • 스프링이 지지하는 프로그래밍 모델 적용
    • e.g. 데이터 엑세스 기술에 대한 일관된 예외 적용
      • 불필요하게 예외를 처리하는 코드를 피하도록 런타임 위주의 예외르 사용한다는 스프링의 개발철학이 적용된 예시
  • 템플릿/콜백 지원
    • e.g. JDBC
    • 대부분의 템플릿 클래스는 빈으로 등록해서 필요한 빈에서 DI 받아 사용 가능
  • 스프링에 새로운 기술을 연동하려면 스프링의 프로그래밍 모델과 개발 철학을 따르면서 위 네가지 방법을 이용하면 된다.
  • 스프링을 사용하려면 스프링의 프로그래밍 모델과 개발 철학을 따르는 일관된 코드를 만드는 데 많은 관심을 기울여야 한다.
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함