오상우
오상우

Categories

  • java
  • spring

스프링 어플리케이션 아키텍쳐

스프링으로 완전한 어플리케이션을 만드는데 있어, 아키텍쳐 개념을 살펴보는 장이다.


3 계층 어플리케이션 아키텍쳐

지금까지 우리는 스프링 개발에 있어, 오브젝트를 기준으로 역할을 나누고 분리하였다. 이 개념을 확장하면 여러 오브젝트들을 역할을 기준으로 나눌 수 있는데, 이를 어플리케이션의 계층이라 부른다. 일반적으로 스프링 어플리케이션의 계층 구조는 3 계층 아키텍쳐를 가장 많이 사용한다.

image

A. 데이터 엑세스 계층

데이터 엑세스 계층은 DAO 계층이라고도 불린다. 실제 퍼시스턴트 계층(DB 등)에 접근하는 인터페이스의 역할을 담당하는 계층으로, 가장 아랫단으로 가면 결국 구체적인 DB 기술에 의존적인 코드가 존재한다. 따라서 PSA를 활용한 추상화가 필수적으로 발생하는 계층이기도 하다.

image

B. 서비스 계층

서비스 계층은 구조로 보자면 가장 단순하다. 잘 만들어진 스프링 어플리케이션에서 서비스 계층은 POJO로 작성되어야 한다. DAO 계층을 호출해서 데이터를 가져와, 이 데이터를 활용해 실제 비즈니스 로직을 구현하는 계층이다. 애초에 구체적 기술과는 상관 없는 계층이기 때문에 PSA가 등장하지 않는 것이 일반적이다.

서비스 계층에 부가 기능이 필요한 경우 AOP를 활용하면 된다.

비즈니스 어플리케이션에서 가장 핵심이 되는 계층인 만큼, 데이터 엑세스 계층과 프레젠테이션 계층의 구현이 바뀌어도 영향을 받으면 안되는 계층이다.

C. 프레젠테이션 계층

프레젠테이션 계층은 가장 복잡한 계층이다. 기술/프레임워크 스텍이 매우 다양하고, 급변하는 계층이기도 하다. 웹만 해도 리액트, 뷰 등 SPA 부터 고전적인 JSP와 같은 서버사이드 렌더링 방식에, 모바일 어플리케이션과 다른 서비스를 위한 API 엔드포인트까지 다양하다.

스프링에서 HTTP 요청을 처리하는 프레젠테이션 계층의 기본 엔진은 서블릿과 서블릿 컨테이너(WAS 톰캣 등)을 기반으로 한다.



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

대부분의 엔터프라이즈 시스템은 본질적으로 데이터, 즉 정보를 다루는 역할을 한다. 사용자가 제공한 정보를 알맞을 로직에 맞겨 처리하고, 해당 정보를 다시 데이터베이스에 저장하거나 다시 불러와서 사용자에게 돌려준다.

따라서 어플리케이션 아키텍쳐를 설계할 때, 계층 사이를 흘러다니는 정보를 어떤 식으로 관리할지 결정 하는 일은 아키텍처를 이루는 중요한 부분이다.

정보를 다루는 방식으로 아키텍쳐를 구분할때, 우리는 크게 데이터를 단순한 값으로 바라보며 어플리케이션은 해당 값을 처리하기 위해 존재하는 데이터 중심 아키텍쳐와, 데이터를 하나의 객체로 바라보는 오브젝트 중심 아키텍쳐로 구분할 수 있다.

A. 데이터 중심 아키텍쳐

데이터 중심 아키텍쳐는 핵심 비즈니스 로직을 어디에 두느냐에 따라 다시 DB에 무게를 두는 구조와 서비스 계층에 무게를 두는 구조로 나눌 수 있다.

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

대부분의 주요 비즈니스 로직이 SQL문으로 존재한다. 따라서 전체 어플리케이션이 마치 DB의 인터페이스처럼 동작한다. 이 경우 서비스 계층은 하나 또는 여러개의 SQL을 호출하며 트랜젝션을 관리해주는 정도의 역할로, DB조회 결과를 프레젠테이션 계층에 전달하는 역할만을 수행한다. 하나의 업무 트랜젝션에 어플리케이션 전체 코드들이 종속되며, 따라서 코드 재사용성이 매우 낮다. 데이터도 DB에서 나온 데이터를 그냥 로우 데이터 혹은 키밸류 맵으로 다루기에 SQL이 조금만 달라져도 전체 로직이 바뀌게 된다.

이런 방식의 아키텍쳐는 변화에 몹시 취약하다. 데이터의 구조가 달라지게 되므로 로직 전체가 바뀌는 일이 다반사이고, 거의 비슷한 일을 하는 중복된 많은 코드들을 양산한다. 거기다 대부분의 부하가 DB로 가게 되는데, DB는 가격도 비싸고 테스트도 어렵고 확장도 어려운 고급자원이다.

  • 거대한 서비스 계층 방식

여전히 데이터는 DB에서 가져온 로우데이터를 사용하지만, 비즈니스 로직이 서비스 계층으로 옮겨진 아키텍쳐이다. 그만큼 서비스 레이어에서 객체 지향적 프로그래밍을 할 기회가 많아졌다. DAO는 상대적으로 단순한 조회 작업만을 수행하게 되고, DAO의 메소드들을 서비스 레이어에서 재활용할 수 있는 기회가 늘어난다.

하지만 여전히 DAO가 돌려주는 데이터가 로우 데이터이고, 서비스 계층의 요구에 따라 다른 구조를 갖게 될 확률이 높다. 따라서 비슷한 역할을 하는 비즈니스 로직도 공통 기능으로 만들기가 어렵다.


결국 데이터 중심 아키텍쳐는 처음에는 업무 트랜젝션 단위로 계층간 로직과 데이터구조까지 결합되어 개발이 쉽지만, 이후 코드 중복이 많아지고 수정과 유지보수가 어려워진다.


B. 오브젝트 중심 아키텍쳐

오브젝트 중심 아키텍쳐가 데이터 중심 아키텍쳐와 가장 다른 점은, 계층간을 이동하는 정보가 도메인 모델을 반영하는 객체 형태로 다뤄진다는 점이다.

데이터 중심 아키텍쳐는 하나의 데이터 모델이 없이 매 SQL마다 돌려주는 데이터 구조가 달랐지만, 오브젝트 중심 아크텍쳐는 처음 도메인 분석을 잘 해서 오브젝트를 잘 설계하면 어떤 SQL도 하나의 오브젝트로 변환해 사용할 수 있다.

단지 도메인 오브젝트를 사용하는 것 만으로, 계층간 결합도는 낮아지고, 서비스 계층 로직들은 재사용성이 높아진다. 하지만 자바 오브젝트는 데이터 뿐 아니라 행위도 담을 수 있다.

그래서 오브젝트 중심 아키텍쳐 역시 오브젝트의 역할을 기준으로 다시 두가지로 나눌 수 있다.

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

자바 오브젝트에 정보만 담겨 있고, 어떤 기능도 없다면 완전한 오브젝트라 보기 어렵다. 물론 이렇게 활용하더라도 데이터 중식 아키텍쳐 방식 보다는 훨씬 낫다. 실제로 많이 활용하는 방식이기도 하다.

이 방식의 문제는 결국 거대 서비스 계층 방식과 비슷하다. 도메인 오브젝트라는 공통의 인터페이스를 사용하기 때문에 SQL에 의존적인 데이터 방식보다는 낫지만, 여전히 서비스 계층의 메소드에 대부분의 비즈니스 로직이 의존하고 있고, 재사용성에 한계가 존재한다.

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

image

풍성한 / 영리한 도메인 오브젝트 방식은 빈약한 도메인 오브젝트 방식의 단점을 극복하고, 도메인 오브젝트를 보다 객체지향적으로 활용할 수 있도록 개선한 것이다. 어떤 비즈니스 로직은 특정 도메인 오브젝트와 깊은 관계가 있다. 이런 로직을 서비스 계층이 아니라 해당 도메인 오브젝트에 넣어주고 서비스 계층에서는 필요할때 해당 도메인 오브젝트에게 부탁해서 실행되게 하면, 보다 객체지향적인 어플리케이션을 만들 수 있다.

물론 여러 도메인 오브젝트가 협력해야 하는 로직 등을 처리하기 위해 서비스 계층은 필요하다. 하지만 보다 간결하고 중복이 적은 모습으로 바뀔 것이다.

이런 방식에도 단점은 있다. 도메인 오브젝트는 다른 계층 오브젝트를 직접 이용(DAO 사용 등)할 수 없다. DI를 받기 위해서는 해당 객체가 스프링 컨테이너의 관리를 받는 빈으로 등록되어야 하기 때문이다.

  • 도메인 계층 방식

image

도메인 객체가 가진 한계를 극복하기 위해 등장한 아키텍쳐가 도메인 계층 방식 아키텍쳐다. 이는 도메인 객체를 격상시켜 아예 4번째 계층으로 만드는 것이다. 이렇게 되면 도메인 객체도 DI를 통해 다른 계층 빈을 주입받아 사용할 수 있고, 모든 비즈니스 로직을 자체적으로 처리할 수 있다.

다만 도메인 객체에 DI를 사용할 때는 주의할 점이 있다.

우선, 도메인 객체는 다른 빈들과는 달리 생성을 스프링 컨테이너가 아닌, 매 요청과 로직에 따라 다른 계층의 빈들이 담당한다. 따라서 빈으로 등록하지 않고, AOP를 사용해서 생성될 때 어노테이션을 읽어서 빈 저장소에 있는 빈들을 주입하는 방식을 사용해야 한다. 당연히 싱글톤으로 관리되어서도 안된다.

생성자를 조인포인트로 사용하기 위해서는 다이나믹 프록시 방식이 아닌, 바이트코드 조작 방식의 AOP를 사용해야 한다. AspectJ가 대표적이다.

또 다른 문제는 도메인 오브젝트에서 중요한 비즈니스 로직까지 처리하게 되면서, 도메인 오브젝트를 과연 어디까지 그대로 전달해 주어야 하는지에 대한 고민이 등장한다. 프레젠테이션 계층에서 도메인 오브젝트의 모든 기능을 사용할 수 있도록 하는 것은 문제가 있다. 따라서 도메인 객체의 기능을 제한하는 프록시 객체를 전달하거나, 아예 데이터 읽기만 가능한 DTO로 변환해서 내려주는 방법도 생각해 볼 수 있겠다.


항상 도메인 계층 아키텍트가 낫다는 것은 아니다. 물론 데이터 중심 아키텍쳐는 항상 지향해야 하지만, 오브젝트 중심 아키텍쳐에서 어떤 것을 택할지는 상황에 따라 다르다.


결론

  • 스프링 어플리케이션은 3계층으로 구분되고, 다시 기술의 추상도에 따라 세분되는 계층형 아키텍쳐를 사용하는 것이 좋다.

  • 아키텍쳐는 어플리케이션이 데이터를 다루는 방식에 따라 데이터 중심 / 오브젝트 중심으로 나눌 수 있다.

  • 물론 가능하면 오브젝트 중심 아키텍쳐를 사용하자.