토비의 스프링 Chapter 9.3.3 ~ 9.3.4 정리

종명·2021년 6월 7일
0

토비의 스프링 정리

목록 보기
11/11

도메인 오브젝트 사용의 문제점

일관된 의미를 가지고 유연하며 애플리케이션 전반에 공유 가능한 도메인 모델을 따르는 오브젝트로 정보를 다루면 많은 장점이 있다. 코드는 이해하기 쉽고 로직을 작성하기도 수월하다. 하지만 단점도 있다.

  1. 최적화된 SQL을 매번 만들어 사용하는 경우에 비해 성능 면에서 조금은 손해를 감수해야 할 수도 있다.

    • DAO는 비즈니스 로직의 사용 방식을 알지 못하므로, 도메인 오브젝트의 모든 필드 값을 다 채워서 전달하는 경우가 대부분이다.
    • 그런데 하나의 오브젝트에 담긴 필드의 개수가 많아지다 보면 그중에는 드물게 사용되는 필드도 있을 수 있다. 어떤 비즈니스 로직에서 필요한 정보가 몇 개의 필드 뿐이라면 DAO에서 도메인 오브젝트의 모든 필드 정보를 채워서 전달하는 것은 낭비일 수도 있다.
  2. 오브젝트 관계에도 문제가 있다.

    • DAO가 돌려준 Product 오브젝트에는 관계를 갖고 있는 Category 오브젝트도 함께 담겨 있을 것이다. Category에 담긴 정보까지 사용할 때도 있겠지만, 어떤 경우에는 Product에 담긴 정보만 필요할 때도 있다.
    • 그럼에도 Category오브젝트까지 조회해서 오브젝트로 만들어서 가져오는 것은 상당한 낭비다.

해결방안

  1. 지연된 로딩(lazy loading) 기법을 이용하면 일단 최소한의 오브젝트 정보만 읽어두고 관계하고 있는 오브젝트가 필요한 경우에만 다이내믹하게 DB에서 다시 읽어올 수 있다.
  2. 사실 가장 좋은 방법은 JPA, JDO, 하이버네이트 같은 RDB 매핑(ORM) 기술을 사용하는 것이다. 이런 데이터 액세스 기술은 기본적으로 지연된 로딩 기법 등을 제공해주기 때문에 번거로운 코드를 만들지 않고도 도메인 오브젝트의 생성을 최적화할 수 있다.

그래서 도메인 오브젝트를 사용하는 오브젝트 중심 아키텍처에서는 가능하다면 ORM과 같은 오브젝트 중심 데이터 액세스 기술을 사용하는 것을 권장한다.

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

도메인 오브젝트에 정보만 담겨 있고, 정보를 활용하는 아무런 기능도 갖고 있지 않다면 이는 온전한 오브젝트라고 보기 힘들다. 그래서 이런 오브젝트를 빈약한 오브젝트라고 부른다.
물론 이렇게라도 도메인 모델을 반영한 오브젝트에 정보를 담아 활용하는 편이 도메인 오브젝트를 사용하지 않는 것보다는 훨씬 낫다.

도메인 오브젝트에 넣을 수 있는 기능은 어떤 것일까?

  • 도메인 모델을 반영해서 만들어진 도메인 오브젝트이니 그 기능이라고 하면 도메인의 비즈니스 로직이라고 볼 수 있다.

그렇다면 빈약한 도메인 오브젝트 방식에서는 비즈니스 로직이 어디에 존재할까?

  • 서비스 계층이다. 사실 다루는 정보의 구조가 다를 뿐이지 빈약한 도메인 오브젝트 방식은 데이터 중심 아키텍처의 거대 서비스 계층구조와 비슷하다.

빈약한 도메인 오브젝트 방식의 한계는 거대 서비스 계층 방식과 유사하다. 비록 도메인 오브젝트라는 일관된 오브젝트를 활용하기 때문에 SQL에 의존적인 데이터 방식 보다는 유연하고 간결하지만, 여전히 서비스 계층의 메소드에 대부분의 비즈니스 로직이 들어 있기 때문에 로직의 재사용성이 떨어지고 중복의 문제가 발생하기 쉽다. 하지만 비즈니스 로직이 복잡하지만 않다면 가장 만들기 쉽고 3계층 구조의 특징을 잘 살려서 개발할 수 있는 유용한 아키텍처다.

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

풍성한 도메인 오브젝트 또는 영리한 도메인 오브젝트 방식은 빈약한 도메인 오브젝트의 단점을 극복하고 도메인 오브젝트의 객체지향적인 특징을 잘 사용할 수 있도록 개선한 것이다. 어떤 비즈니스 로직은 특정 도메인 오브젝트나 그 관련 오브젝트가 가진 정보와 깊은 관계가 있다. 이런 로직을 서비스 계층의 코드가 아니라 도메인 오브젝트에 넣어주고, 서비스 계층의 비즈니스 로직에서 재사용하게 만드는 것이다.

서비스 계층 코드에서 만들었던 calcTotalOfProductPrice()는 Category와 Product의 정보만 사용하는 간단한 로직이다. 이것을 굳이 서비스 계층에 두지 않고 Category 클래스안에 둘 수있다.

public class Category {
    List<Product> products;
    
    // Category를 따로 파라미터로 받을 필요가 없다. 자신이 가진 정보를 직접 사용하면 되기 때문이다.
    public int calcTotalOfProductPrice() { 
        int sum = 0;
        for (Product p : this.products) { // 오브젝트가 가진 내부 정보를 활용해서 필요한 계산이나 로직을 수행하면 된다.
            sum += p.getPrice();
        }
        return sum;
    }
}

이렇게 만드는 경우는 서비스 계층의 메소드에 따로 만드는 것보다 응집도가 높다. 데이터와 그것을 사용하는 기능이 한곳에 모여있기 때문이다.

물론 도메인 오브젝트에 비즈니스 로직을 넣는다고 해서 비즈니스 로직을 담고 있던 서비스 계층 오브젝트가 필요 없어지는 건 아니다.
1. 여러 종류의 도메인 오브젝트 기능을 조합해서 복잡한 비즈니스 로직을 만드는 경우는 서비스 계층의 오브젝트에 두는 것이 적당하다.
2. 도메인 오브젝트는 직접 데이터 액세스 계층이나 기반 계층 또는 다른 서비스 계층의 오브젝트에 접근할 수 없기 때문에 서비스 계층이 필요하기도 하다.

도메인 계층 방식

지금까지 살펴본 바로는 도메인 모델을 따르는 오브젝트를 만들고 이를 활용하는 방법에는 한계가 있다.

도메인 오브젝트에 담을 수 있는 비즈니스 로직은 데이터 액세스 계층에서 가져온 내부 데이터를 분석하거나, 조건에 따라 오브젝트 정보를 변경, 생성 하는 정도에 그칠 수 밖에없다. 이렇게 변경된 정보가 다시 DB등에 반영되려면 서비스 계층 오브젝트의 부가적인 작업이 필요하다.

도메인 오브젝트가 스스로 필요한 정보는 DAO를 통해 가져올 수 있고, 생성이나 변경이 일어났을 때 직접 DAO에게 변경사항을 반영해달고 요청할 수는 없을까?

도메인 계층의 역할과 비중을 극대화하려다 보면 기존의 풍성한 도메인 오브젝트의 방식으로는 만족할 수 없다. 그래서 등장한 것이 바로 도메인 오브젝트가 기존 3계층과 같은 레벨로 격상되어 하나의 계층을 이루게 하는 도메인 계층 방식이다. 개념은 간단한다. 도메인 오브젝트들이 하나의 독립적인 계층을 이뤄서 서비스 계층과 데이터 액세스 계층의 사이에 존재하는 것이다.

도메인 오브젝트가 독립된 계층을 이뤘기 때문에 기존 방식과는 다른 두 가지 특징을 갖게 된다.

  • 도메인에 종속적인 비즈니스 로직의 처리는 서비스 계층이 아니라 도메인 계층의 오브젝트 안에서 진행된다는 점이다.
  • 도메인 오브젝트가 기존 데이터 액세스 계층이나 기반 계층의 기능을 직접 활용할 수 있다는 것이다.
    • 스프링에 등록돼서 싱글톤으로 관리되는 빈이 아니기에 DI 받을 수 없다. 그렇다면 어떻게 다른 빈을 이용할 수 있을까? 물론 방법은 DI다.

스프링이 관리하지 않는 도메인 오브젝트에 DI를 적용하기 위해서는 AOP가 필요하다. 물론 AOP의 적용 대상도 스프링의 빈 오브젝트 뿐이다. 하지만 스프링 AOP대신 AspectJ AOP를 사용하면 클래스의 생성자가 호출되면서 오브젝트가 만들어지는 시점을 조인 포인트로 사용할 수 있고 스프링 빈이 아닌 일반 오브젝트에도 AOP 부가기능을 적용할 수 있다. 이를 이용해서 도메인 오브젝트가 생성되는 시점에 특별한 부가기능을 추가하게 만들어 줄 수 있다.

도메인 계층 방식은 이전의 어떤 방식보다 도메인 오브젝트에 많은 비즈니스 로직을 담아낼 수 있다. 그럼에도 서비스 계층의 역할이 완전히 사라지는 건 아니다. 때로는 여러 도메인 오브젝트의 기능을 조합해서 복잡한 작업을 진행해야 하는 경우가 있다. 특정 도메인 오브젝트에 담길 수 없는 이런 작업은 서비스 계층에서 도메인 계층과 협력을 통해 진행하는 것이 바람직하다.

도메인 오브젝트를 독립적인 계층으로 만들려고 할 때 고려해야 할 중요한 사항이 있다. 도메인 오브젝트가 도메인 계층을 벗어나서도 사용되게 할지 말지 결정해야 한다. 도메인 오브젝트가 계층을 이루기 전에는 모든 계층에 걸쳐 사용되는 일종의 정보전달 도구 같은 역할을 했다. 하지만 독자적인 계층을 이뤘을 때는 상황이 달라질 수 있다.

선택할 수 있는 방법은 두 가지가 있다.
1. 여전히 모든 계층에서 도메인 오브젝트를 사용한다.
- 도메인 계층은 물론이고 서비스, 프레젠테이션, 뷰에서도 도메인 오브젝트를 사용할 수 있게 하는 것이다.
- 하지만, 막강한 기능을 가진 오브젝트를 프레젠테이션, 뷰 등에서 사용하게 해주면 이를 함부로 사용하는 위험이 뒤따를 수 있다.
- 이런 문제를 피하려면 어떻게 해야할까? 철저한 개발 가이드를 만들어 강력하게 적용하는 것이다.
2. 도메인 오브젝트는 도메인 계층을 벗어나지 못하게 하는 것이다.
- 도메인 계층 밖으로 전달될 때는 별도로 준비된 정보 전달용 오브젝트에 도메인 오브젝트의 내용을 복사해서 넘겨줘야 한다. 이런 오브젝트는 데이터 전달에 사용된다고 해서 DTO라고한다.
- DTO는 변화를 허용하지 않고 읽기전용으로 만들어지기도 한다.
- DTO는 기능을 갖지 않으므로 사용하기 안전하다.

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

계층형 아키텍쳐

  • 3계층 구조는 스프링을 사용하는 엔터프라이즈 애플리케이션에서 가장 많이 사용되는 구조다.
  • 프렌젠테이션 계층 : Spring MVC를 이용한다.
  • 서비스 계층 : POJO로 구현하면서 트랜잭션 AOP를 적용한다.
  • 데이터 액세스 계층 : JDBC를 비롯해 스프링의 데이터 액세스 전략이 적용된 JPA, 하이버네이트 등을 활용하도록 한다.

정보 전송 아키텍쳐

  • 스프링의 기본 기술에 가장 잘 들어맞고 쉽게 적용햅볼 수 있는 것은 오브젝트 중심 아키텍처의 도메인 오브젝트 방식이다. 일단은 빈약한 도메인 오브젝트 방식으로 시작하는게 가장 쉽다.

  • 도메인 오브젝트를 계층 간의 정보 전송을 위해 사용하고, 이를 각 계층의 코드에서 활용한다.

  • DAO는 그 기술이 어떤 것이든 상관없이 서비스 계층에서 요청을 받거나 결과를 돌려줄때 도메인 오브젝트 형태를 유지하게 만든다.

  • 서비스 계층의 비즈니스 로직 또한 도메인 오브젝트를 사용해서 작성한다.

  • 프레젠테이션 계층에서도 도메인 오브젝트를 직접 활용하도록 만든다. 프레젠테이션 계층의 MVC 아키텍처에서도 모델은 도메인 오브젝트를 그대로 사용한다. 뷰에 전달하는 정보도 물론 도메인 오브젝트를 사용하는 모델이고, 사용자가 입력하는 폼의 정보도 도메인 오브젝트로 변환해서 사용한다.

  • 이렇게 도메인 오브젝트를 사용해 애플리케이션의 정보를 일관된 형태로 유지하는게 스프링에 가장 잘 들어맞는 방식이다.

상태 관리와 빈 스코프

  • 아키텍처 설계에서 한 가지 더 신경 써야 할 사항은 상태 관리다. 크게는 사용자 로그인 세션 관리부터, 작게는 하나의 단위 작업이지만 여러 페이지에 걸쳐 진행되는 위저드 기능까지 애플리케이션은 하나의 HTTP 요청의 범위를 넘어서 유지해야 하는 상태정보가 있다.
  • 엔터프라이즈 애플리케이션은 특정 사용자가 독점해서 배타적으로 사용되지 않는다. 하나의 애플리케이션이 동시에 수많은 사용자의 요청을 처리하게 하기 위해 매번 간단한 요청을 받아서 그 결과를 돌려주는 방식으로 동작한다. 따라서 서버의 자원이 특정 사용자에게 일정하게 할당되지 않는다. 그래서 서버 기반의 애플리케이션은 원래 지속적으로 유지되는 상태를 갖지 않는다(stateless)는 특징이 있다.
  • 하지만 어떤 식으로든 애플리케이션의 상태와 장시간 진행되는 작업정보는 유지돼야 한다. 이를 위해서 웹 클라이언트에 URL, 파라미터, 폼 히든 필드, 쿠키 등을 이용해 상태정보 또는 서버에 저장된 상태정보에 키 값 등을 전달해야 한다. 클라이언트와 서버 사이에서 많은 양의 정보를 계속해서 주고 받을 수 없으므로 중요한 상태정보는 파일 시스템, 데이터그리드, DB등에 저장 되기도 한다. 또는 제약이 있기는 하지만 HTTP 세션과 같은 서블릿 컨테이너가 제공하는 저장공간을 활용하기도 한다.
  • 이렇게 상태를 저장, 유지하는데 어떤 방식을 사용할지 결정하는 일은 매우 중요하다. 스프링은 기본적으로 상태가 유지되지 않는 빈과 오브젝트를 사용하는 것을 권장한다. 웹의 생리에 가장 잘 들어맞고 개발하기 쉽기 때문이다. 또, 서버를 여러 대로 확장하기가 매우 쉽다.
  • 반면에 웹 클라이언트에 폼 정보를 출력하고 이를 수정하는 등의 작업을 위해서는 HTTP 세션을 적극 활용하기도 한다.

서드파티 프레임워크, 라이브러리 적용

스프링이 지원하는 기술이란 무슨 의미일까?
1. 해당 기술을 스프링의 DI 패턴을 따라 사용할 수 있다.
2. 스프링의 서비스 추상화가 적용됐다.
3. 스프링이 지지하는 프로그래밍 모델을 적용했다.
4. 템플릿/콜백이 지원된다.

스프링이 어떤 기술을 지원한다는 건, 결국 스프링이 지지하는 개발철학과 프로그래밍 모델을 따르면서 해당 기술을 사용할 수 있다는 의미다.

0개의 댓글