-
Notifications
You must be signed in to change notification settings - Fork 0
Description
토비의 스프링 3.1 Vol.1 의 마지막 장인 9장에서는 스프링 애플리케이션 프로젝트를 처음 구성할 때 알아야 할 내용에 대해 알아본다. 추가로, 스프링을 애플리케이션에 적용할 수 있는 아키텍처의 종류와 특징에 대해서도 알아본다.
스프링은 매우 유연하게 설계된 범용 프레임워크이기 때문에, 아키텍처의 종류나 프로젝트 구성에 대한 자유도가 매우 높다. 하지만 그만큼 프로젝트 구성 방법이나 아키텍처를 선택할 때 주의해야 한다. 스프링이 유연하다고 해서 아무렇게나 갖다 쓰면 안된다.
9.1 자바 엔터프라이즈 플랫폼과 스프링 애플리케이션
자바를 쓰는 어떤 프로젝트든 스프링으로 애플리케이션을 만들 수 있다.웹을 이용하는 자바 엔터프라이즈 시스템 개발이나, 스윙이나 이클립스 RCP로 만드는 독립형 프로그램도 가능하다. 심지어는 스프링을 핵심 엔진으로 사용하는 엔터프라이즈 미들웨어 제품도 있다.
하지만 스프링은 주로 자바 엔터프라이즈 애플리케이션을 개발하는데 주로 쓰인다. 자바 엔터프라이즈 애플리케이션은 서버에서 동작하며 클라이언트를 상대로 서비스를 제공한다. 즉, 클라이언트의 요청을 받고 그에 대한 작업을 수행한 후 결과를 돌려주는 것이 기본 동작 방식이다.
엔터프라이즈 애플리케이션에서 가장 많이 사용되는 구조는 클라이언트가 웹 브라우저, 백엔드 시스템이 DB인 구성이다. 많은 시스템이 이러한 구성을 갖고 있기 때문에 스프링의 주요 기능은 웹 브라우저를 클라이언트로 하고, DB에 데이터를 저장/조회하는데 집중되어 있다. 하지만 꼭 클라이언트가 웹 브라우저이고, DB로 백엔드 시스템을 구성할 필요는 없다. 자바 서버가 받아들일 수 있는 방식으로 요청을 보내기만 하면 어떤 종류의 클라이언트든 상관없다. 백엔드 시스템도 메시징 서버, 메일 서버, 메인 프레임도 가능하다. 역시 자바가 제공하는 접속 방식을 지원하는 시스템이면 된다.
스프링으로 만든 애플리케이션을 자바 서버 환경에 배포하려면 JavaEE나 J2EE 서버가 필요하다. JavaEE 표준을 따르는 애플리케이션 서버는 크게 두 가지로 구분할 수 있는데, 하나는 JavaEE의 대부분의 표준 기술을 지원하고 다양한 형태의 모듈로 배포가 가능한 완전한 WAS이고, 다른 하나는 웹 모듈 배포만 가능한 경량급 WAS 또는 Servlet/JSP 컨테이너이다.
스프링은 기본적으로 Tomcat이나 Jetty 같은 가벼운 서블릿 컨테이너만 있어도 된다. 서블릿 컨테이너로도 엔터프라이즈 애플리케이션에 필요한 핵심 기능을 이용할 수 있다.
반면 고가의 WAS를 사용하면 성능 면에서 확실한 차이가 없더라도 미션 크리티컬한 시스템에서 요구하는 고도의 안정성이나 안정적인 리소스 관리 등의 기능을 이용할 수 있다.
스프링으로 만든 애플리케이션은 다음 세 가지 단위로 배포할 수 있다.
- 독립 웹 모듈 - 스프링은 보통 war로 패키징된 독립 웹 모듈로 배포한다. 톰캣같은 서블릿 컨테이너를 쓴다면 독립 웹 모듈이 유일한 방법이다.
- 엔터프라이즈 애플리케이션 - 경우에 따라서 확장자가 .ear 인 엔터프라이즈 애플리케이션으로 패키징해서 배포할 수도 있다.
- 백그라운드 서비스 모듈 - J2EE 1.4에서 등장한 .rar 패키징 방법이다.
9.2 개발도구와 환경
스프링 3.0은 JavaSE 5 버전에서 추가된 문법의 특징을 최대한 활용하고 있기 때문에 기본적으로 JDK 5.0 이상이 필요하다. 일부 기능은 JDK 6.0의 API를 사용하는 것도 있다.
IDE는 Eclipse부터 IntelliJ, STS 도 다 사용해봤는데 IntelliJ 가 짱이다.
9.3 애플리케이션 아키텍처
아키텍처란 용어의 가장 단순한 정의를 보자면 어떤 경계 안에 있는 내부 구성 요소들이 어떤 책임을 갖고 있고, 어떤 방식으로 서로 관계를 맺고 동작하는지를 규정하는 것이라고 할 수 있다. 아키텍처 자체는 단순히 정적인 구조를 나타내는 것으로만 생각하기 쉽지만, 실제로는 그 구조에서 일어나는 동적인 행위와 깊은 관계가 있다.
성격이 다른 것들끼리 분리함으로써 각 요소의 응집도는 높이고 서로의 결합도를 낮추는 것이 좋다. 성격이 다른 모듈이 강하게 결합되어 있으면 한 가지 이유로 변경이 일어날 때 그와 상관없는 요소도 함께 영향을받게 되기 때문에 불필요한 변경이 일어나고, 그로 인해 작업은 더뎌지고 오류가 발생할 가능성이 높아지기 때문이다. 따라서 인터페이스와 같은 유연한 경계를 만들어두고 분리하거나 모아주는 작업이 필요하다.
지금까지는 주로 오브젝트 레벨에서 이런 분리의 문제에 대해 생각해보았는데, 이러한 원리는 아키텍처 레벨에서 좀 더 큰 단위에 대해서도 동일하게 적용할 수 있다. 예를 들어 데이터 액세스 로직을 담당하는 DAO들과 비즈니스 로직을 구현한 비즈니스 서비스 오브젝트들을 하나의 단위로 생각하는 것이다. 이렇게 분리된 각 오브젝트는 독자적으로 개발과 테스트가 가능해서 개발과 변경 작업이 모두 빨라질 수 있다. 전체 흐름을 이해하기도 상대적으로 쉽다.
이러한 책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해 두는 것을 아키텍처 차원에서는 계층형 아키텍처라고 한다. 일반적으로 웹 기반의 엔터프라이즈 애플리케이션은 세 개의 계층을 갖는다고 해서 3-layer 애플리케이션이라고도 한다.
3-layer 아키텍처는 백엔드의 DB나 레거시 시스템과 연동하는 인터페이스 역할을 하는 데이터 액세스 계층, 비즈니스 로직을 담고 있는 서비스 계층, 주로 웹 기반의 UI를 만들고 그 흐름을 관리하는 프레젠테이션 계층으로 구분된다.
먼저 데이터 액세스 계층은 DAO 패턴을 보통 사용하기 때문에 DAO 계층이라고도 불린다. 이 계층은 사용하는 기술에 따라서 다시 세분화된 계층으로 구분할 수 있는데, 이 때 세분화하는 기준은 추상화 수준에 따른 구분이기 때문에 수직적인 계층이다. 참고로 기본적으로 역할에 따라 계층을 구분할 땐 가로로 배열하는데, 같은 책임을 가졌지만 추상화 레벨에 따라 구분하는 경우는 세로로 배열한다.
다음으로 서비스 계층 구조를 보자면 가장 단순하다. 잘 만들어진 스프링 애플리케이션의 서비스 계층 클래스는 이상적인 POJO로 작성된다. 위에서 말했듯이, POJO로 잘 만든다면 객체지향적인 설계 기법이 적용된 코드를 통해서 비즈니스 로직의 핵심을 잘 담아내고, 쉽게 테스트하고 유연한 확장이 가능하다. 서비스 계층은 특별한 경우가 아니라면 추상화 수직 계층 구조를 가질 필요가 없다. 이상적인 서비스 계층은 데이터 액세스 계층과 프레젠테이션 계층이 바뀌어도 그대로 유지될 수 있어야 한다.
프레젠테이션 계층은 가장 복잡한 계층이다. 웹과 프레젠테이션 기술은 굉장히 빠르게 발전해오고 있기 때문이다. 엔터프라이즈 애플리케이션의 프레젠테이션 계층은 클라이언트의 종류와 상관없이 HTTP 프로토콜을 사용하는 서블릿이 바탕이 된다.
오브젝트와 그 관계에 적용했던 대부분의 객체지향 설계 원칙은 아키텍처 레벨의 계층과 그 관계에서도 동일하게 적용할 수 있다. 각 계층은 응집도가 높으면서 다른 계층과의 결합도는 낮게 유지되어야 한다. 즉, 각 계층은 자신의 계층의 책임에만 충실해야 한다. 다음은 서비스 계층이 DAO를 호출할 때 높은 결합도를 가지는 경우이다.
public ResultSet findUserByName(String name) throws SQLException;위 메서드의 문제점은 데이터 액세스 계층의 기술과 그 역할이 서비스 계층에 노출된다는 점이다. ResultSet 객체를 바로 돌려주면 이를 사용하는 서비스 계층의 코드에서 데이터 액세스 계층에서 만들어진 오브젝트를 직접 다루어야 한다. 또한 SQLException 이라는 JDBC 기술에 종속적인 예외를 던지면 이를 사용하는 서비스 계층에서 SQLException 을 해석해서 예외 상황을 분석하고 이를 처리하는 코드를 만들어야 한다. 서비스 계층의 코드가 특정 데이터 액세스 계층의 구현에 종속되는 강한 결합이 만들어지게 되는 것이다. 따라서 앞의 DAO 인터페이스의 메서드는 다음과 같이 수정되어야 한다.
public List<User> findUserByName(String name) throws DataAccessException;특정 계층의 기술이나 구현에 종속되지 않는 User 란 사용자 정보를 담고 있는 단순한 오브젝트를 사용하고, DataAccessException 과 같은 런타임 예외를 전달해야 해서 대개 그 존재를 무시해도 되도록 만들어야 한다.
또 흔히 발생하는 실수 중 하나는 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층에 전달하는 것이다. 서블릿의 HttpServletRequest나 HttpSession 같은 타입을 서비스 계층 인터페이스 메서드의 파라미터 타입으로 사용하면 안 된다. 계층의 경계를 넘어갈 때는 반드시 특정 계층에 종속되지 않는 오브젝트 형태로 반환해주어야 한다. 만약 서비스 계층에 웹 프레젠테이션 계층의 기술이 노출되었다면, 웹 방식이 아닌 다른 방식의 클라이언트의 요청을 처리하는 경우에는 해당 코드를 재사용할 수 없다. 같은 로직이지만 클라이언트의 종류에 따라서 비즈니스 로직 코드가 달라지는 것이다.
