Skip to content

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

ksw6169 edited this page Mar 28, 2021 · 4 revisions

개요

  • 2021-03-07 ~ 03-13 // 12주차
  • 8장(753 ~ 781p) // 12주차

자바 엔터프라이즈 플랫폼과 스프링 애플리케이션

  • 스프링으로 만들 수 있는 애플리케이션의 종류에는 제한이 없다. 자바 언어를 사용하는 모든 종류의 프로젝트라면 어디든 사용할 수 있다. (웹을 사용하는 자바 엔터프라이즈 시스템, 스윙으로 만드는 독립형 프로그램, 애플릿 등등)
  • 그러나 서버에서 동작하는 엔터프라이즈 애플리케이션을 제외한 다른 형태의 애플리케이션에 스프링을 제대로 적용하기 위해서는 추가적인 스프링 지원 기술(Spring RCP, Spring ME 등) 이 필요하므로 자바 엔터프라이즈 환경에서 동작하는 애플리케이션 개발에 주로 사용된다.

애플리케이션 서버

  • 스프링으로 만든 애플리케이션을 자바 서버 환경에 배포하려면 JavaEE(또는 J2EE) 서버가 필요하다.
  • JavaEE 표준을 따르는 애플리케이션 서버는 크게 두 가지로 구분할 수 있다.
    • (1) JavaEE의 대부분의 표준 기술을 지원하고 다양한 형태로 모듈 배포가 가능한 완전한 형태의 WAS
    • (2) 웹 모듈의 배포만 가능한 경량급 WAS 또는 서블릿/JSP Container

경량급 WAS/서블릿 컨테이너

  • 스프링은 기본적으로 톰캣이나 제티 같이 가벼운 서블릿 컨테이너만 있어도 충분하다.
  • EJB나 리소스 커넥터, WAS가 제공하는 분산 서비스 등이 굳이 필요하지 않다면 서블릿 컨테이너로도 엔터프라이즈 애플리케이션에 필요한 핵심 기능을 모두 이용할 수 있다.

WAS

  • 고가의 WAS를 사용하면 그만큼 장점이 있다.
  • 성능 면에서는 대단히 낫지 않더라도 미션 크리티컬한 시스템에서 요구되는 고도의 안정성이나 고성능 시스템에서 필수적인 안정적인 리소스 관리 등을 수행할 수 있다.
  • 또한, 레거시 시스템의 연동이나 기존 EJB로 개발된 모듈을 함께 사용하는 등의 필요가 있다면 상용 또는 오픈소스 WAS를 이용할 수 있다.
  • 그리고 WAS는 상대적으로관리 기능이나 모니터링 기능이 뛰어나서 여러 대의 서버를 동시에 운영할 때 유리한 점이 많다.

스프링 애플리케이션의 배포 단위

  • 스프링으로 만든 애플리케이션은 다음의 세 가지 단위로 배포할 수 있다.

1. 독립 웹 모듈

  • 스프링은 보통 war 로 패키징된 독립 웹 모듈로 배포된다. 톰캣 같은 서블릿 컨테이너를 쓴다면 독립 웹 모듈이 유일한 방법이다.

2. 엔터프라이즈 애플리케이션

  • 경우에 따라선 확장자가 ear 인 엔터프라이즈 애플리케이션으로 패키징해서 배포할 수도 있다.
  • 스프링 애플리케이션에서 EJB 모듈을 긴밀하게 사용하거나 반대로 EJB 모듈에서 스프링으로 만든 애플리케이션을 이용해야 한다면, EJB와 스프링 웹 모듈을 엔터프라이즈 애플리케이션으로 통합해야 한다.

3. 백그라운드 서비스 모듈

  • rar 패키징 방법도 있다. 리소스 커넥터를 만들어 배포할 때 사용하는 방식인데, 만약 스프링으로 만든 애플리케이션이 UI를 가질 필요는 없고 서버 내에서 백그라운드 서비스처럼 동작할 필요가 있을 때 rar 모듈로 만들어 배포할 수 있다.

라이브러리 관리와 빌드 툴

  • 라이브러리 관리에는 어려움이 따른다. 애플리케이션에서 필요한 기술에 따라 라이브러리를 선택해야 하고, 선택한 기술에 따라 라이브러리의가 정확히 어떤 버전을 사용해야 하는지도 선택해야 한다. 또한 각 라이브러리는 의존적으로 사용하고 있는 라이브러리 들이 있어 이들의 호환성도 맞춰줘야 하는 어려움이 있다.
  • Maven과 ANT는 자바의 대표적인 빌드 툴이다. 빌드 툴은 의존 라이브러리 관리 기능을 지원하며, 환경에 독립적이다.
  • Maven은 단순 빌드 툴을 넘어서 개발 과정에서 필요한 빌드, 테스트, 배치, 문서화, 리포팅 등의 다양한 작업을 지원하는 종합 프로젝트 관리 툴의 성격을 띠고 있다. Maven의 특징은 POM이라고 불리는 프로젝트 모델 정보를 이용한다는 점이다. 그래서 절차적인 스크립트와 구조가 비슷한 ANT와 달리 Maven은 선언적이다.
  • Maven은 프로젝트의 주요한 구조와 특징, 필요한 정보를 POM의 프로젝트 모델 정보로 만들어두면, 이를 참조해서 Maven에 미리 정해진 절차에 따라 빌드 또는 프로젝트 관리 작업을 진행할 수 있다.
  • Maven POM이 가진 독특한 특징 중의 하나는 애플리케이션이 필요로 하는 의존 라이브러리를 POM 안에 선언해두기만 하면 원격 서버에서 이를 자동으로 다운로드 받아서 사용할 수 있게 해주는 것이다.
<!--
    Maven POM의 의존 라이브러리 선언
-->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>${spring-boot-version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
        <version>${spring-boot-version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <version>${spring-boot-version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>
  • 또한, Maven의 의존 라이브러리 관리 기능이 제공하는 더 흥미로운 기능은 전이적(transitive) 의존 라이브러리 추적 기능이다. POM의 의존정보에 하나의 라이브러리를 지정하면, 지정된 라이브러리가 동작하는 데 필요한 여타 라이브러리까지 함께 다운로드 해주는 기능이다.
  • 애플리케이션의 POM에는 직접 지정해준 적은 없어도 전이적인 의존 추적 기능을 통해 자동으로 추가된다. 이런 과정은 모든 라이브러리에 재귀적으로 적용된다.
  • 참고로, 스프링의 모든 모듈은 POM 정보를 갖고 있다.

스프링 웹 애플리케이션 아키텍처

  • 책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것을 아키텍처 차원에서는 계층형 아키텍처(layered architecture) 또는 멀티 티어 아키텍처(multi tier architecture) 라고 부른다. 보통 웹 기반의 엔터프라이즈 애플리케이션은 일반적으로 세 개의 계층을 갖는다고 해서 3계층(3-tier, 3-layer) 애플리케이션이라고도 한다.

3계층 아키텍처와 수직 계층

  • 3계층 아키텍처는 프레젠테이션 계층, 서비스 계층, 데이터 액세스 계층으로 구분한다.
    • 프레젠테이션 계층 : 웹 기반의 UI를 만들어내고 그 흐름을 관리
    • 서비스 계층 : 비즈니스 로직을 담음
    • 데이터 액세스 계층 : 백엔드의 DB나 레거시 시스템과 연동하는 인터페이스 역할

데이터 액세스 계층(DAO 계층)

  • DB 외에도 ERP, 레거시 시스템, 메인프레임 등에 접근하는 역할을 하기 때문에 EIS(Enterprise Information System) 계층이라고도 한다.
  • 데이터 액세스 계층은 사용 기술에 따라서 다시 세분화된 계층으로 구분될 수 있다. 계층 안에서 다시 세분화하는 경우는 추상화 수준에 따른 구분이기 때문에 수직적인 계층이라고 부르기도 한다. 기본 3계층은 기술 계층보다는 역할에 따라 구분한 것이므로 보통 그림으로 나타낼 때 가로로 배열한다. 반면에 같은 책임을 가졌지만 추상화 레벨에 따라서 구분하는 경우는 세로로 배열해서 표현한다.
  • 추상화 계층은 필요하다면 얼마든지 추가할 수 있다. 하지만, 새로운 계층을 추가하면 개발자의 애플리케이션 코드에 지대한 영향을 줄 수 있으므로 매우 신중하게 결정해야 한다. 또한, 한번 새로운 계층과 API를 만들어 적용하면 이를 최대한 유지할 수 있도록 하위 계층의 변화에 대응해야 하는 책임도 갖게 된다.
  • 만약 추상 게층을 새로 추가하는 것이 부담스럽고 경우에 따라서 유연하게 하위 계층의 API를 활용할 필요가 있다면, 공통 기능을 분리해서 유틸리티나 헬퍼 메소드 또는 오브젝트로 제공해주는 것도 좋은 방법이다.

서비스 계층

  • 서비스 계층은 DAO 계층을 호출하고 이를 활용해서 만들어진다. 때론 데이터 액세스를 위한 기능 외에 서버나 시스템 레벨에서 제공하는 기반 서비스를 활용할 필요도 있다.
  • 서비스 계층은 특별한 경우가 아니라면 추상화 수직 계층 구조를 가질 필요가 없다. 단순히 비즈니스 로직을 모델링하다가 상속구조를 만들 수 있을진 몰라도 기술 API를 직접 다루는 코드가 아니기 때문에 기술에 일관되게 접근하게 하거나, 편하게 사용하도록 해주는 추상화는 필요 없기 때문이다.

프레젠테이션 계층

  • 프레젠테이션 계층은 클라이언트의 종류와 상관없이 HTTP 프로토콜을 사용하는 서블릿이 바탕이 된다.

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

  • 오브젝트와 그 관계에 적용했던 대부분의 객체지향 설계의 원칙은 아키텍처 레벨의 계층과 관계해도 동일하게 적용할 수 있다. 각 계층은 응집도가 높으면서 다른 계층과는 낮은 결합도를 유지할 수 있어야 한다.
  • 각 계층은 자신의 계층의 책임에만 충실해야 한다. 예를 들어, 데이터 액세스 계층은 데이터 액세스에 관한 모든 것을 스스로 처리해야 한다. 데이터 액세스 계층에 비즈니스 로직을 담거나 웹 파라미터를 파싱하는 코드 등이 들어가서는 안된다.
  • 계층의 경계를 넘어갈 때는 반드시 특정 계층에 종속되지 않는 오브젝트의 형태로 변환해줘야 한다. (예외, Object 등)
  • 계층간 낮은 결합도를 위해 인터페이스를 사용했다면 public 메소드를 설계할 때 신중해야 한다. 한 번 다른 계층에서 사용하기 시작한 인터페이스의 메소드는 변경이 매우 까다롭고 비용이 많이 들기 때문이다. 반드시 다른 계층에서 꼭 필요한 메소드만 노출해야 한다.

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

  • 애플리케이션을 사이에 두고 흘러다니는 정보를 어떤 식으로 다룰지 결정하는 일도 아키텍처를 결정할 때 중요한 기준이 된다. 엔터프라이즈 애플리케이션에 존재하는 정보를 단순히 데이터로 다루는 경우와 오브젝트로 다루는 경우, 두 가지로 구분해볼 수 있다.
  • 데이터 중심 아키텍처는 애플리케이션에 흘러다니는 정보를 단순히 값이나 값을 담기 위한 목적의 오브젝트 형태로 취급하는 구조다. DB나 백엔드 시스템에서 가져온 정보를 값으로 다루고 그 값을 취급하는 코드를 만들어 로직을 구현하고 값을 그대로 프레젠테이션 계층의 뷰, 즉 사용자가 보는 화면과 연결해주는 것이다.
  • 데이터 중심 아키텍처의 특징은 비즈니스 로직이 DB 내부의 저장 프로시저나 SQL에 담겨 있는 경우가 많은데 이 아키텍처는 단점이 많다.
    • 모든 계층의 코드는 하나의 업무 단위를 위해서만 존재하도록 만들어지므로 재사용이 어렵다.
    • 대응되는 작업 단위에 코드가 1:1로 매핑되므로 비슷한 기능에 대한 코드는 중복되기 쉽다.
    • 개발하기 쉽다는 장점이 있으나, 이런 방식의 개발은 자바 코드를 단지 DB와 웹 화면을 연결해주는 단순한 인터페이스 도구로 전락시킨다.
    • 비즈니스 로직은 항상 SQL과 그 결과에 종속되기 때문에 SQL의 변화가 일어나면 같이 변경되어야 한다. 겉으로 보기에는 각 계층이 독립적으로 보이지만, 그 사이를 이동하는 데이터가 일종의 접착제 역할을 해서 강한 결합을 만들게 된다. 이 구조에서는 객체지향의 장점이 별로 활용되지 않고, 각 계층의 코드가 긴밀하게 연결되는 구조로 만들어진다.
    • 복잡한 SQL을 처리하기 위해 제한된 자원인 DB에 큰 부담을 주게 된다.
  • 상대적으로 애플리케이션 서버와 그 안에 담긴 오브젝트는 비용이 적게 들기 때문에 로직을 DB에서 처리하기 보다는 애플리케이션으로 가져오는 편이 유리한 점이 많다. 따라서 익숙하고 편리하다는 이유로 DB 중심의 아키텍처를 선택하는 것은 좋지 않다. 이 방식은 스프링의 장점을 제대로 누릴 수 없기 때문이다.

거대한 서비스 계층(fat service layer) 방식

  • 데이터 중심 아키텍처이긴 하지만 DB에 많은 로직을 두는 개발 방법의 단점을 피하면서 애플리케이션 코드의 비중을 높이는 방법이 있다. DB에는 부하가 걸리지 않도록 복잡한 SQL을 피하면서 주요 로직은 서비스 계층의 코드에서 처리하도록 만드는 것이다.
  • 이 방식은 업무 트랜잭션 단위로 서비스 계층의 메소드가 만들어질 가능성이 높은데, 이런 접근 방식은 결국 거대한 서비스 계층을 만들게 된다.
  • 서비스 계층의 코드는 여전히 업무 트랜잭션 단위로 집중되서 만들어지기 때문에 DAO를 공유할 수 있는 것을 제외하면 코드의 중복도 적지 않게 발생한다.
  • 이 방식은 애플리케이션의 코드에 비즈니스 로직이 담겨 있기 때문에 자바의 장점을 활용해 로직 구현이 가능하고, 테스트하기도 수월하다. 또한 DAO 코드는 여러 비즈니스 로직에서 공유해서 사용할 수 있다.
  • 하지만, 데이터 액세스 계층의 SQL은 서비스 계층의 비즈니스 로직의 필요에 따라 만들어지기 쉽다. 그래서 계층 간의 결합도가 여전히 크다.
  • 또한 서비스 계층의 메소드는 크기가 큰 업무 트랜잭션 단위로 만들어지기 때문에 비슷한 기능의 코드가 여러 메소드에서 중복되서 나타나기 쉽다. 자주 사용되는 세부 로직을 추출해 공통 기능으로 뽑아내는 일도 불가능하지는 않지만 일반화하기는 힘들다. 그 이유는 DAO가 제공해주는 값의 포맷에 따라 이를 취급하는 방법이 달라지기 때문이다.
  • 반면에 각 단위 업무별로 독립적인 개발이 가능하므로 초기 개발 속도가 빠르고, 개발자 사이에 간섭없이 독립적인 개발이 가능하다는 장점이 있다.
  • 하지만 업무 트랜잭션 단위로 코드가 모이기 때문에 처음에는 개발하기 편하지만 중복이 많아지기 쉽고, 장기적으로 코드를 관리하고 발전시키기 힘들다는 단점이 있다.

오브젝트 중심 아키텍처

  • 이 아키텍처가 데이터 중심 아키텍처와 다른 가장 큰 특징은 도메인 모델을 반영하는 오브젝트 구조를 만들어두고 그것을 각 계층 사이에서 정보를 전송하는 데 사용한다는 것이다. 그래서 오브젝트 중심 아키텍처는 객체지향 분석과 모델링의 결과로 나오는 도메인 모델을 오브젝트 모델로 활용한다.
  • 대개 도메인 모델은 DB의 엔티티 설계에도 반영되기 대문에 관계형 DB의 엔티티 구조와도 유사한 형태일 가능성이 높다.
  • 이렇게 오브젝트를 만들어두고 오브젝트 구조 안에 정보를 담아서 각 계층 사이에 전달하게 만드는 것이 오브젝트 중심 아키텍처다.
  • 오브젝트 방식에서는 애플리케이션에서 사용되는 정보가 도메인 모델의 구조를 반영해서 만들어진 오브젝트 안에 담긴다. 도메인 모델은 애플리케이션 전 계층에서 동일한 의미를 갖는다. 따라서 도메인 모델이 반영된 도메인 오브젝트도 전 계층에서 일관된 구조를 유지한 채로 사용될 수 있다.

도메인 오브젝트를 사용하는 코드

public int calcTotalOfProductPrice(Category cate) {
	int sum = 0;
	for (Product prd : cate.getProducts()) {
		sum += prd.getPrice();
	}
	return sum;
}

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

  • 최적화된 SQL을 매번 만들어 사용하는 경우에 비해 성능 면에서 조금은 손해를 감수해야 할 수도 있다. DAO는 비즈니스 로직의 사용 방식을 알지 못하므로 도메인 오브젝트의 모든 필드 값을 다 채워서 전달하는 경우가 대부분인데, 그중에는 드물게 사용되는 필드도 있을 수 있다. 이 경우 모든 값을 채워서 전달하는 것은 낭비일 수 있다. 비즈니스 로직에 따라서 필요한 정보가 달라질 수 있기 때문에 발생하는 문제다.
  • 오브젝트 관계에 따른 문제점
    • Product 정보만 필요한 비즈니스 로직에서 Product 오브젝트와 관계를 맺고 있는 Category 정보까지 같이 가지고 오는 경우 상당한 낭비
  • 이런 문제를 해결하는 접근 방법은 여러가지가 있다.
    1. 지연된 로딩(lazy loading) 기법을 이용한다.
      • 일단 최소한의 오브젝트 정보만 읽어두고 관계하고 있는 오브젝트가 필요한 경우에만 다이내믹하게 DB에서 다시 읽어올 수 있다.
      • 이 경우, 도메인 오브젝트를 사용하는 코드는 이런 사실을 전혀 의식하지 않고 처음부터 모든 오브젝트의 정보가 다 제공된다고 생각하고 작성하면 된다.
    2. 필드가 너무 많은 테이블이 있다면 그중에서 자주 사용되는 것을 골라내서 별도의 오브젝트로 정의해두고 필요에 따라 구분해서 사용하게 한다.
      • 이 경우, 추가된 오브젝트에 따라 DAO 메소드가 추가해야 되고, 어느 DAO를 사용할지를 서비스 계층에서 알고 있어야 하기 때문에 약하긴 하지만, 계층 사이의 결합이 발생한다.
  • 이를 실현하기 위해 가장 이상적인 방법은 ORM(오브젝트 - RDB 매핑) 기술을 사용하는 것이다. 이는 기본적으로 지연 로딩 기법 등을 제공해주기 때문에 별도의 코드를 만들지 않고도 도메인 오브젝트의 생성을 최적화할 수 있다.
  • ORM을 사용하지 않고 JDBC를 이용하는 경우라면 지연 로딩 기법을 제공하는 코드를 추가해주거나, 사용되는 필드의 종류와 사용되는 관련 오브젝트의 범위에 따라서 여러 개의 DAO 메소드를 만들어 사용해야 할 수도 있다.
  • 오브젝트 중심 아키텍처는 오브젝트의 활용 방법을 기준으로 다시 구분할 수 있다.

빈약한 도메인 오브젝트 방식(anemic object)

  • 데이터를 저장하는 것 외에는 아무런 기능이 없는 오브젝트를 빈약한 오브젝트(anemic object) 라고 한다.
  • 이 방식은 도메인 오브젝트를 사용하지만 단순히 정보를 저장하고 조회하는 용도로만 활용하기 때문에 핵심 비즈니스 로직은 서비스 계층에 모두 담겨 있다.
  • 따라서 이 방식의 한계는 거대 서비스 계층 방식과 유사하다. 도메인 오브젝트라는 일관된 오브젝트를 활용하기 때문에 SQL에 의존적인 데이터 방식보다는 훨씬 유연하고 간결하지만, 여전히 서비스 계층의 메소드에 대부분의 비즈니스 로직이 들어있기 때문에 로직의 재사용성이 떨어지고 중복의 문제가 발생하기 쉽다.
  • 하지만 비즈니스 로직이 복잡하지 않다면 가장 만들기 쉽고 3계층 구조의 특징을 잘 살려서 개발할 수 있는 유용한 아키텍처다.

풍성한 도메인 오브젝트 방식(rich domain object)

  • 이 방식은 빈약한 도메인 오브젝트의 단점을 극복하고 도메인 오브젝트의 객체지향적인 특징을 잘 사용할 수 있도록 개선한 것이다.
  • 어떤 비즈니스 로직은 특정 도메인 오브젝트나 그 관련 오브젝트가 가진 정보와와 깊은 관계가 있는데 이런 로직을 서비스 계층의 코드가 아니라 도메인 오브젝트에 넣어주고, 서비스 계층의 비즈니스 로직에서 재사용하게 만드는 것이다.
  • 도메인 오브젝트 안에 로직을 담아두면 이 로직을 서비스 계층의 메소드에 따로 만드는 경우보다 응집도가 높다. 데이터와 그것을 사용하는 기능이 한곳에 모여있기 때문이다.
  • 이 방식은 빈약한 오브젝트 방식보다 서비스 계층의 코드가 간결하며, 비즈니스 로직을 이해하기도 쉽다. 따라서 빈약한 오브젝트 방식을 피하고, 이 방식을 선택하는 것이 바람직하다.

도메인 계층 방식

  • 도메인 계층의 역할과 비중을 극대화시키기 위해 도메인 오브젝트가 기존 3계층과 같은 레벨로 격상되어 하나의 계층을 이루도록 하는 것이 도메인 계층 방식이다.
  • 개념은 도메인 오브젝트들이 하나의 독립적인 계층을 이뤄서 서비스 계층과 데이터 액세스 계층 사이에 존재하도록 하는 것이다.
  • 이 방식에서는 도메인에 종속적인 비즈니스 로직의 처리는 서비스 계층이 아니라 도메인 계층의 오브젝트 안에서 진행된다. 또한, 도메인 오브젝트가 기존 데이터 액세스 계층이나 기반 계층의 기능을 직접 활용할 수 있다.
  • 도메인 오브젝트를 독립적인 계층으로 만들려고할 때 고려해야할 중요한 사항이 있다.

계층형 아키텍처

  • 3계층 구조는 스프링을 사용하는 엔터프라이즈 애플리케이션에서 가장 많이 사용되는 구조로, 스프링의 주요 모듈과 기술을 살펴보면 3계층 구조에 적합하도록 설계되어 있다.
  • 단, 3계층이라는 것은 논리적이고 개념적인 구분이지 꼭 오브젝트 단위로 딱 끊어져서 만들어지는 게 아님을 염두에 둬야 한다. 때로는 하나의 계층이 다시 수평으로 세분화될 수도 있고, 반대로 3계층에서 두 개의 계층이 통합되서 하나의 오브젝트에 담기는 일도 얼마든지 가능하다.
  • 예를 들어 서비스 계층을 굳이 도입하지 않아도 될 만큼 비즈니스 로직이 단순한 애플리케이션이라면 서비스 계층과 데이터 액세스 계층을 통합할 수도 있다.

참고 자료

  • 토비의 스프링 3.1