2. 스프링 프레임워크 소개

스프링 프레임워크는 자바 애플리케이션 개발을 위해 포괄적인 인프라 구조를 제공하는 자바 플랫폼이다. 스프링은 당신이 애플리케이션에 집중할 수 있도록 인프라 구조를 담당한다.

스프링을 사용하면 "plain old Java objects"(POJOs)로 애플리케이션을 만들고 엔터프라이즈 서비스를 비침투적으로 POJO에 적용할 수 있다. 이 능력은 Java SE 프로그래밍에 적용되고 전제 혹은 부분적으로 Java EE에 적용된다.

당신이 애플리케이션 개발자라고 가정할 때, 당신이 어떻게 스프링 플랫폼의 이점을 이용할 수 있는가에 대한 예제는 다음과 같다.

2.1 의존성 주입(DI)과 제어 역전(IoC)

제약을 가진 임베디드 애플리케이션부터 n-티어의 서버사이드 엔터프라이즈 애플리케이션까지 전반적인 느슨하게 포함한 자바 애플리케이션은 보통 애플리케이션에 적합한 형식으로 협력하는 객체로 이루어져 있다. 그래서 애플리케이션의 객체들은 서로 의존성을 가지고 있다.

자바 플랫폼이 애플리케이션 개발에 관련된 풍부한 기능을 제공하더라도, 기본적인 구성 요소들을 서로 밀착된 하나의 큰 구조로 만드는 기능은 부족하다. 따라서 아키텍트와 개발자들이 이 작업을 해야 한다. 비록, 애플리케이션을 개발시에 다양한 클래스들과 객체 인스턴스들을 구성하기 위해 팩토리, 추상 팩토리, 빌더, 데코레이터, 서비스 로케이터와 같은 디자인 패턴을 사용할 수 있을 수는 있다. 하지만 이러한 패턴들은 이름이 부여된 좋은 사례들로 이런 패턴이 무슨 일을 하고, 어디에 적용할 수 있고, 어떤 문제에 대한 것인지 등을 단순히 설명한 것에 불과하다. 패턴들은 여러분의 애플리케이션에서 여러분이 스스로 구현해야 할 공식화된 좋은 사례들인 것이다.

스프링 프레임워크의 제어의 역전 (IoC) 컴포넌트는 이러한 관심사에 접근한다. 즉, 서로 다른 컴포넌트들을 사용할 준비가 된 완전히 동작하는 애플리케이션을 구성하는 형식적인 의미를 제공한다. 스프링 프레임워크는 자신의 애플리케이션으로 통합할 수 있는 일급 클래스 객체들을 형식화된 디자인 패턴으로 만들었다. 수많은 단체와 기관들은 신뢰할 수 있고 유지보수 가능한 애플리케이션을 만들려고 이러한 방법으로 스프링 프레임워크를 사용한다.

2.2 모듈

스프링 프레임워크는 약 20개의 모듈로 구조화된 특징으로 이루어져 있다. 이러한 모듈들은 다음 다이어그램에 보이듯 핵심 컨테이너, 데이터 접근/통합, 웹, AOP(관점지향 프로그래밍), 인스트루멘테이션(Instrumentation), 메세징, 테스트로 그룹을 나눌 수 있다.

그림 2.1. 스프링 프로그램의 개요

spring overview

다음 섹션에서는 각각 기능별로 사용할 수 있는 모듈들의 아티팩트(artifact)명과 그것들이 다루는 주제들에 대해 나열한다. 아티팩트명들은 의존성 관리 도구들에서 사용하는 아티팩트 ID들에 대응한다.

2.2.1 핵심 컨테이너

핵심 컨테이너spring-core, spring-beans, spring-context, spring-context-support, spring-expression(스프링 표현 언어) 모듈들로 구성되어 있다.

spring-corespring-beans 모듈은 IoC와 의존성 주입 기능들을 포함하는, 프레임워크의 근본 기능이라 할 수 있는 부분을 제공한다. BeanFactory는 팩토리 패턴의 정교한 구현체이다. 그것은 싱글톤을 구현할 수고를 덜어주며, 실제 동작하는 프로그램 로직에서 설정과 명세를 분리하게 해준다.

컨텍스트 (spring-context) 모듈은 핵심 및 빈즈 모듈에서 제공하는 단단한 기반위에 구성되었다. 그 말은 JNDI 레지스트리와 유사한 프레임워크-스타일로 객체에 접근한다는 의미다. 컨텐스트 모듈은 빈즈 모듈의 특징들을 상속받고 국제화(예. 리소스 번들의 사용), 이벤트 전파, 리소스 로딩, 컨텍스트의 투명한 생성(예. 서블릿 컨테이너) 같은 지원을 부가한다. 또한 컨텍스트 모듈은 EJB, JMX, 기본 리모팅 같은 Java EE의 기능을 지원한다. ApplicationContext 인터페이스는 컨텍스트 모듈의 중심점을 담당한다. spring-context-support는 캐싱(EhCache, Guava, JCache), 메일링(JavaMail), 스케쥴링(CommonJ, Quartz), 템플릿 엔진(FreeMarker, JasperReports, Velocity)과 같은 공통 서드-파티 라이브러리들을 스프링 애플리케이션 컨텍스트로의 통합을 지원을 제공한다.

spring-expression 모듈은 런타임에서 객체 그래프를 조회하고 조작하는 강력한 표현 언어를 제공한다. 그것은 JSP 2.1 명세에 명시된 것처럼 통합된 표현언어(unified EL)의 확장이다. 그 언어는 프로퍼티를 설정하고 가져오는 기능과 프로퍼티 할당, 메서드 호출, 배열, 컬렉션, 인덱서의 컨텍스트 접근, 논리적 및 산술적 연산들, 이름있는 변수, 스프링의 IoC 컨테이너에서 이름으로 객체를 획득하는 기능을 지원한다. 또한 공통의 리스트 집합 뿐 아니라 리스트의 투영과 선택도 지원한다.

2.2.2 AOP와 인스트루멘테이션(Instrumentation)

spring-aop 모듈은 AOP Alliance를 따르는 관점지향 프로그래밍의 구현체다. 예를 들어 기능적으로 분리되어야 하는 코드를 깔끔하게 분리하는 메서드-인터셉터와 포인트컷을 정의할 수 있다. 소스레벨의 메타데이터 기능을 사용하면 .NET 어트리뷰트와 유사한 방법으로 행동에 관한 정보를 코드로 구현할 수 있다.

분리된 spring-aspects 모듈은 AspectJ와의 통합을 제공한다.

spring-instrument 모듈은 클래스 인스트루멘테이션 지원을 제공하는 특정 애플리케이션 서버들에서 사용되는 클래스 로드 구현체들이다.

2.2.3 메세징

스프링 프레임워크 4는 메세징-기반의 애플리케이션들을 위한 토대를 제공하기 위해 Message, MessageChannel, MessageHandler 등과 같은 스프링 통합(Spring Integration) 프로젝트의 핵심을 추상화시킨 spring-messaging 모듈을 포함한다. 이 모듈은 프로그래밍 모델에 기반한 스프링 MVC 애노테이션과 유사한 메소드들에게 메시지들을 매핑하기 위한 애노테이션들의 집합도 포함한다.

2.2.4 데이터 액세스/통합

데이터 액세스/통합 계층은 JDBC, ORM, OXM, JMC, 트랜잭션 모듈로 이루어졌다.

spring-jdbc 모듈은 JDBC-추상 계층을 제공한다. 그래서 지루한 JDBC 코딩과 데이터베이스 벤더에 따라 다른 오류코드를 파싱할 필요가 없다.

spring-tx 모듈은 특수한 인터페이스들을 구현하기 위한 클래스들과 모든 당신의 POJO(Plain Old Java Objects)들 을 대상으로 하는 프로그램적이고 선언적인 트랜잭션 관리를 지원한다.

spring-orm 모듈은 JPA, JDO, and Hibernate를 포함하는 인기 있는 객체-관계 매핑 API에 대한 통합 계층을 제공한다. ORM 패키지를 사용하면 이러한 O/R매핑 프레임워크들을 앞에서 언급했던 선언적 트랜젝션 관리 같은 스프링의 다른 기능들과 함께 사용할 수 있다. spring-orm 모듈을 사용하면 이전에 언급했던 선언적 트랜잭션 관리 기능과 같은 스프링이 제공하는 다른 기능들 모두와 조합해서 이러한 O/R 매핑 프레임워크들 모두를 사용할 수 있다.

spring-oxm모듈은 JAXB, Castor, XMLBeans, JiBX and XStream와 같은 객체/XML 매핑 구현체들을 지원하는 추상 계층을 제공한다.

spring-jms 모듈(자바 메세징 서비스)은 메세지들을 생성하고 사용하는 기능들을 포함한다. 스프링 프레임워크 4.1부터 spring-messaging 모듈과의 통합을 제공한다.

2.2.5 웹

계층은 spring-web, spring-webmvc, spring-websocket, spring-webmvc-portlet로 이루어졌다.

spring-web 모듈은 서블릿 리스너와 웹 지향적인 애플리케이션 컨텍스트를 사용하여 멀티파트 파일 업로드 기능과 IoC 컨테이너의 초기화와 같은 기본적인 웹-지향적인 통합기능을 제공한다. 또한, HTTP 클라이언트와 스프링의 리모팅 지원의 웹-관련 부분들을 포함한다.

spring-webmvc 모듈(또한 Web-Servlet 모듈로 알려져 있는)은 웹 애플리케이션을 위한 스프링의 모델-뷰-컨트롤러(MVC)와 REST 웹 서비스들의 구현을 포함한다. 스프링의 MVC 프레임워크는 도메인 모델 코드와 웹폼 그리고 스프링 프레임워크의 다른 기능들의 모든 통합들 간의 깔끔한 분리를 제공한다.

spring-webmvc-portlet 모듈(Web-Portlet 모듈로 알려져 있는)은 Portlet 환경과 spring-webmvc 모듈의 기능을 사용할 수 있는 MVC 구현체을 제공한다.

2.2.6 테스트

spring-test 모듈은 JUnit 또는 TestNG로 스프링 컴포넌트인 단위 테스트통합 테스트를 지원한다. 이 모듈은 스프링 ApplicationContext의 안정된 로딩과 이러한 컨텍스트의 캐싱을 제공한다. 또한, 코드를 격리된 상태로 테스트하기 위해 사용할 수 있는 모의 객체들을 제공한다.

2.3 사용 시나리오들

'제한된 리소스의 장치에서 돌아가는 임베디트 애플리케이션'부터 '스프링의 트랜잭션 관리 기능 및 웹 프레임워크 통합을 사용하는 일정 규모를 갖춘 엔터프라이즈 에플리케이션'까지 많은 시나리오 상황이 있을 수 있다. 이번 절에서는 이러한 많은 시나리오 상황에서 스프링의 논리적인 선택에 미리 도움이 되는 내용을 다룬다.

그림 2.2. 완전한 형태의 전형적인 스프링 웹 애플리케이션

overview full

스프링의 선언적 트랜잭션 관리 기능은 여러분이 EJB 컨테이너가 관리해주는 트랜잭션을 사용하는 것 처럼, 웹 애플리케이션이 완전한 트랙잭션 기능을 가지게 해준다. 모든 커스텀 비즈니스 로직은 간단한 POJO로 구현할 수 있고 스프링의 IoC 컨테이너로 관리할 수 있다. 또한, 부가 서비스들을 위한 지원을 포함하는데, 이메일을 보내거나 특정 지점의 유효성 확인 규칙들을 당신이 웹 계층에 독립적으로 선택하게 할 수 있는 유효성 검증 같은 서비스를 말한다. 스프링의 ORM 지원은 JPA, Hibernate, JDO, iBatis와 통합되었다. 가령 당신이 하이버네이트를 사용한다면, 이미 존재하는 매핑 파일과 표준 하이버네이트 SessionFactory 설정을 계속 사용할 수 있다. 폼(Form) 컨트롤러는 도메인 모델에 대한 값으로 HTTP 파라미터를 전달하는 ActionForms이나 다른 클래스에 대한 필요성을 제거함으로써 도메인 모델과 웹 계층을 자연스럽게 통합한다.

그림 2.3. 서드파티 웹 프레임워크(예. Struts, Tapestry)를 사용하는 스프링 미들-티어

overview thirdparty web

때로는 다른 프레임워크로 완전히 전환하는 것이 어려울 수 있다. 스프링 프레임워크는 스프링 프레임워크안에서만 모든 일을 하도록 강제하지 않는다. 즉, 양자택일을 하는 솔루션이 아니다. 이미 만들어진 프레임워크가 WebWork, Struts, Tapestry나 다른 UI 프레임워크로 만들어졌더라도 스프링의 트랜잭션 기능을 사용하는 스프링 기반의 미들-티어와 통합할 수 있다. Struts나 Tapestry, JSF, 다른 UI 프레임워크들로 프론트-엔트가 존재할 경우 스프링의 트랜잭션 기능을 사용하는 스프링 기반의 미들-티어와 통합할 수 있다. 간단하게 ApplicationContext를 사용하는 비즈니스 로직을 연결하고 웹 계층과 통합하기 위해 WebApplicationContext를 사용하면 된다.

그림 2.4. 원격 사용 시나리오

overview remoting

이미 존재하는 코드에 웹서비스로 접근할 필요가 있다면, 스프링의 Hessian-, Burlap-, Rmi-, JaxRpcProxyFactory 클래스를 사용할 수 있다. 존재하는 애플리케이션으로의 원격접근을 허용하는 것은 어렵지 않다.

그림 2.5. EJB - 존재하는 POJO들의 랩핑(Wrapping)

overview ejb

또한, 스프링 프레임워크는 엔터프라이즈 자바빈즈(Enterprise JavaBeans)를 위한 액세스 및 추상화 계층을 제공한다. 이러한 EJB는 기존의 POJO를 재사용 가능하게 하고, 무상태 세션빈 안에 그런 POJO들을 래핑을 하여, 선언적인 보안이 요구되는, 확장성 있고 안전장치를 갖춘 웹 애플리케이션이 된다.

2.3.1 의존성 관리와 네이밍 컨벤션들

의존성 관리과 의존성 주입은 다르다. 의존성 주입 같은 스프링의 멋진 기능들을 애플리케이션에서 사용하려면 런타임시에 필요한 모든 jar 라이브러리들을 클래스 패스에 그것들을 위치해놓아야 할 것이며, 또는 컴파일 시점에 두어야 할 필요도 있다. 이런 의존성들은 주입되는 가상 컴포넌트가 아니라 (전통적으로) 파일시스템에 존재하는 물리적 리소스이다. 의존성 관리의 프로세스는 이러한 리소스를 필요한 위치에 두고 저장하고 클래스 패스에 추가하는 작업이 포함한다. 의존성은 직접적(예. 런타임시에 스프링에 의존하는 애플리케이션)일 수도 있고 간접적(예. commons-pool기반의 commons-dbcp에 의존하는 애플리케이션)일 수도 있다. 간접적인 의존성은 보통 과도기적(transitive)이라고 부르기도 하는데, 이러한 의존성은 구별하고 관리하기가 몹시 어렵다.

스프링을 사용할 계획이라면 필요한 스프링 일부를 포함하는 jar 라이브러리의 복사본이 필요하다 스프링은 의존성이 최대한 분리되도록 모듈이 패키징되어 있으므로 이 작업을 쉽게 할 수 있다. 그래서 예를 들어 웹 애플리케이션을 작성할 것이 아니라면 스프링-웹 모듈은 필요 없다. 이 가이드의 스프링 라이브러리 모듈을 참조하기 위해 spring-*spring-*.jar,같은 짧은 작명 컨벤션을 사용한다. *은 모듈의 약칭을 의미한다 (예. spring-core, spring-webmvc, spring-jms 등등). 실제 jar 파일명은 보통 파일명에 버전번호가 포함되어 있다 (예. spring-core-4.1.5.RELEASE.jar).

각 스프링 프레임워크의 배포판은 다음 위치에 아티팩트들이 릴리즈 된다.

  • 메이븐 센트럴(Maven Central). Maven Central은 Maven의 기본 저장소이므로 사용하기 위해서 특별한 설정을 할 필요는 없다. 스프링이 의존하는 공통 라이브러리 중 다수는 메이슨 센트럴에서 이용할 수 있고 스프링 커뮤니티의 많은 부분은 의존성 관리에 Maven을 사용하므로 편리하다. Maven Central의 jar 파일명은 spring-*-<version>.jar의 형식을 따르고 Maven의 groupId는 org.springframework이다.
  • 퍼블릭 Maven 저장소에서 스프링은 특별하게 호스트된다. 이 저장소는 마지막 GA 릴리즈들 이외에, 개발 스냅샷들과 마일스톤들 역시 호스트된다. jar 파일명은 Maven Central에서는 동일한 형식을 가지기 때문에, 이곳은 Maven Central에 배포된 다른 라이브러리들을 사용하기 위해 스프링의 개발 버전들을 얻기 위한 유용한 장소이다. 이 저장소는 한번에 쉽게 다운로드를 편하게 할 수 있도록, 모든 스프링의 jar파일들의 번들을 포함하는 배포용 zip 파일 형식의 번들을 포함한다.

따라서 우선 여러분의 의존성들을 어떻게 관리할지를 결정해야 한다. 우리는 보편적으로 Maven, Gradle, Ivy 같은 자동화된 시스템의 사용을 권장한다. 하지만 본인 스스로 모든 jar파일들을 다운로드 하여 수동으로 관리하는 것 역시 가능하다.

아래 스프링 아티팩트들의 목록이 있다. 각각의 모듈들의 세부 설명은 섹션 2.2, “모듈”에서 찾을 수 있다.

표 2.1. 스프링 프레임워크의 아티팩트들(Artifacts)

GroupIdArtifactIdDescription

org.springframework

spring-aop

프록시-기반의 AOP 지원

org.springframework

spring-aspects

AspectJ 기반의 애스팩트들

org.springframework

spring-beans

그루비를 포함한 빈 지원

org.springframework

spring-context

스케쥴링과 원격 추상화들을 포함하는 애플리케이션 컨텍스트 런타임

org.springframework

spring-context-support

스프링 애플리케이션 컨텍스트로의 통합을 위한 공통 서드-파티 라이브러리의 클래스들의 지원

org.springframework

spring-core

다른 스프링 모듈들이 사용하는 핵심 유틸리티들

org.springframework

spring-expression

스프링 표현식 언어 (SpEL)

org.springframework

spring-instrument

JVM 부트스트래핑을 위한 인스트루멘테이션 에이전트

org.springframework

spring-instrument-tomcat

톰캣을 위한 인스트루멘테이션 에이전트

org.springframework

spring-jdbc

데이터소스(DataSource) 설정과 JDBC 액세스 지원을 포함하는 JDBC 지원 패키지

org.springframework

spring-jms

JMS 메세지들을 송수신을 위한 헬퍼 클래스를 포함하는 JMS 지원 패키지

org.springframework

spring-messaging

메세징 아키텍쳐들과 프로토콜들을 위한 지원

org.springframework

spring-orm

JPA와 하이버네이트 지원을 포함하는 객체/관계 매핑

org.springframework

spring-oxm

객체/XML 매핑

org.springframework

spring-test

단위 테스트와 통합 테스트를 지원을 위한 스프링 컴포넌트들

org.springframework

spring-tx

DAO 지원과 JCA 통합을 포함하는 트랜잭션 인프라구조

org.springframework

spring-web

클라이언트와 웹 리모팅을 포함하는 웹 지원 패키지들

org.springframework

spring-webmvc

REST 웹 서비스들과 웹 애플리케이션들을 위한 모델-뷰-컨트롤러 구현

org.springframework

spring-webmvc-portlet

Portlet 환경에서의 사용을 위한 MVC 구현

org.springframework

spring-websocket

STOMP 지원을 포함하는 웹소켓(WebSocket)과 SockJS 구현들


스프링 의존성들과 스프링에 대한 의존

비록 스프링이 엔터프라이즈와 다른 외부 도구들에 대한 광범위한 통합과 지원을 제공하더라도 의도적으로 의존성을 최소한으로 유지해야 한다. 간단한 사용을 위해서 (자동으로 한다고 하더라도) 많은 jar 라이브러리를 다운받아 설치하지 말아야 한다. 기본적인 의존성 주입을 사용하려면 오직 하나의 외부 의존성만 필요하고 이는 로깅을 위한 것이다(로깅 옵션에 대한 더 자세한 내용을 아래를 봐라).

다음은 스프링에 의존하는 애플리케이션 설정하는 필요한 기본적인 과정을 설명할 차례이다. 먼저 Maven을 살펴보고, Gradle, 그리고 Ivy를 살펴볼 것이다. 모든 경우에, 불명확한 것이 있다면, 여러분의 의존성 관리 시스템의 문서를 참고하거나, 예제코드를 보아라. 스프링은 개발과정에서 의존성 관리를 위해 그래들를 사용하고, 샘플들은 대부분 그래들이나 Maven을 사용한다.

Maven 의존성 관리

Maven으로 의존성을 관리한다면 로깅 의존성을 명시할 필요도 없다. 예를 들어 애플리케이션 컨텍스트을 생성하고 애플리케이션 설정에 의존성 주입을 사용하려면 Maven 의존성은 다음과 같을 것이다.

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.1.5.RELEASE</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>

이것이 전부다. 스프링 API를 참조하여 컴파일할 필요가 없다면 scope를 runtime으로 선언할 수 있다. 이는 기본적인 의존성 주입의 유즈케이스다.

위의 예제에서는 Maven Central의 저장소로 동작한다. 스프링 Maven 저장소를 사용하기 위해서는 (예. 마일스톤이나 개발 스냅샷을 위해), 여러분의 Maven 설정에 저장소 경로를 지정할 필요가 있다. 전체 릴리즈를 사용하려면 다음과 같이 작성한다.

<repositories>
    <repository>
        <id>io.spring.repo.maven.release</id>
        <url>http://repo.spring.io/release/</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>

마일스톤을 사용하려면 다음과 같이 작성한다.

<repositories>
    <repository>
        <id>io.spring.repo.maven.milestone</id>
        <url>http://repo.spring.io/milestone/</url>
        <snapshots><enabled>false</enabled></snapshots>
    </repository>
</repositories>

개발 스냅샷을 사용하려면 다음과 같이 작성한다.

<repositories>
    <repository>
        <id>io.spring.repo.maven.snapshot</id>
        <url>http://repo.spring.io/snapshot/</url>
        <snapshots><enabled>true</enabled></snapshots>
    </repository>
</repositories>

Maven의 "제품 구성 사양(bill of materials, BOM)" 의존성

우연찮게 Maven을 사용하여 스프링 JAR들의 여러 버전들을 섞어서 사용할 경우가 있을 것이다. 예를 들어, 서드-파티 라이브러리나 다른 스프링 프로젝트를 찾았는데 그것들이 옛날 릴리즈를 이행적(transitive)으로 의존을 하고 있다고 하자. 혹시 의존성을 당신 스스로 명시적으로 선언하는 것을 잊어버렸다면, 예상하지 못한 이슈들이 발생할 수 있다.

그러한 문제들을 해결하기 위해 Maven은 "제품 구성 사항" (BOM)의 개념을 제공한다. 모든 스프링 의존성들이 동일한 버전에 있는 것을 보장(직접 혹은 이행적인 것 모두)하기 위해서 당신의 dependencyManagement 섹션에 spring-framework-bom를 추가할 수 있다.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>4.1.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

BOM을 사용하게 되면 스프링 프레임워크의 아티팩트들의 의존성을 지정시에 더이상 특정 <version> 속성을 지정할 필요가 없어지게 되어 부가적으로 편해진다.

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
<dependencies>

Gradle 의존성 관리

Gradle 빌드 시스템으로 스프링 저장소를 사용하기 위해서는, repositories 섹션에 적절한 URL을 포함시킨다.

repositories {
    mavenCentral()
    // 그리고 추가적으로...
    maven { url "http://repo.spring.io/release" }
}

repositories URL 대신에 적절히 /release/milestone이나 /snapshot 로 바꿀 수 있다. 일단 저장소가 설정이 되면, 보통의 Gradle 방식으로 의존성들을 선언할 수 있다.

dependencies {
    compile("org.springframework:spring-context:4.1.5.RELEASE")
    testCompile("org.springframework:spring-test:4.1.5.RELEASE")
}

Ivy 의존성 관리

Ivy를 사용하여 의존성 관리를 하길 좋아하더라도 라이브러리 이름과 설정옵션은 비슷하다.

아이비를 설정하려면 여러분의 ivysettings.xml에 스프링 저장소를 가리킬 수 있게 다음의 resolver를 추가한다.

<resolvers>
    <ibiblio name="io.spring.repo.maven.release"
            m2compatible="true"
            root="http://repo.spring.io/release/"/>
</resolvers>

root URL을 적절히 /release/ 대신에 /milestone/이나 /snapshot/로 바꿀 수 있다.

일단 설정을 하고나면, 보통 하는 방법대로 의존성들을 추가할 수 있다. 다음은 하나의 예이다(ivy.xml).

<dependency org="org.springframework"
    name="spring-core" rev="4.1.5.RELEASE" conf="compile->runtime"/>

Zip 파일들의 배포

비록 추천하는 스프링 프레임워크를 획득하는 방법이 의존성 관리의 지원을 받는 빌드 시스템을 사용하는 것이지만, 여전히 배포되는 zip 파일을 다운로드하는 것이 가능하다.

배포판 zip파일들은 스프링 Maven 저장소에 공개된다. (Maven 저장소를 사용하는 것은 단지 편의를 위한 것이지, 그것들을 다운로드 받기 위해 Maven이나 기타 빌드 시스템이 필요하지는 않다).

패포판 zip을 다운로드 받기위해서 웹 브라우저를 http://repo.spring.io/release/org/springframework/spring로 이동하고, 적절한 원하는 버전의 서브폴더를 선택한다. 배포 파일들은 -dist.zip로 끝난다. 예를 들면, spring-framework-4.1.5.RELEASE-RELEASE-dist.zip와 같다. 배포판들은 milestonessnapshots버전도 있다.

2.3.2 로깅

스프링에서 로깅은 아주 중요한 의존성이다. 그 이유는 a) 로깅은 유일하게 스프링에 존재하는 강제적인 외부 의존성이며, b) 대부분의 개발자는 자신이 사용하는 도구에서 발생하는 출력을 보기 원하고, c) 스프링은 로깅 의존성의 선택이 있는 다른 많은 도구와 통합되기 때문이다. 애플리케이션 개발자의 목표 중 하나는 외부 컴포넌트를 포함한 전체 애플리케이션의 중심에서 통일된 로깅을 설정하는 것이다. 이미 로깅 프레임워크가 시중에 많이 존재하기 때문에 하나로 통합하는 것이 쉽지 않다.

스프링의 강제적인 로깅 의존성은 Jakarta Commons Logging API (JCL)이다. JCL을 컴파일하고 스프링 프레임워크를 상속받은 클래스에서 사용할 수 있는 Log객체로 만든다. 이것은 같은 로깅 라이브러리를 사용하는 모든 버전의 스프링 사용자에게 중요하다. 스프링은 스프링을 상속받은 애플리케이션에서조차 하위호환성이 유지되므로 마이그레이션이 쉽다. 마이그레이션을 하는 방법은 스프링의 모듈 중 하나를 명시적으로 commons-logging (JCL의 표준 구현체)에 의존하게 하고 다른 모듈은 컴파일 시에 의존하게 하는 것이다. Maven을 예를 들면, commons-logging에 대한 의존성의 지점은, 스프링에서 센트럴 모듈에서 spring-core에 있다.

commons-logging의 좋은 점은 당신이 애플리케이션을 동작하게 하는 것에만 신경 쓰면 된다는 것이다. 거기에는 잘 알려진 클래스패스나 적절한 위치(혹은 필요하다면 알려줄 수도 있다)에서 다른 로깅 프레임워크를 찾는 런타임 탐색 알고리즘이 있다. 이용할 수 있는 로깅 프레임워크가 없다면 JDK(java.util.logging 혹은 줄여서 JUL)로 꽤 괜찮은 로그를 남긴다. 대부분 상황에서 콘솔 출력 박스에 로그가 잘 나오는 것을 확인한다면 스프링이 잘 동작하고 있다는 것을 알 수 있다. 이는 중요하다.

Commons Logging 사용하지 않기

안타깝게도 commons-logging의 런타임 탐색 알고리즘은 엔드유저에게는 편리하지만, 문제의 소지가 있다. 시간을 거꾸로 돌릴 수 있거나 스프링을 새로운 프로젝트로 다시 시작한다면 런타임 디스커버리 알고리즘은 다른 로깅 의존성을 사용할 것이다. 첫 번째 선택은 아마 Simple Logging Facade for Java (SLF4J)일 것이다. 이것은 이미 그들의 애플리케이션에서 스프링과 함께 많이 사용하는 도구이다.

commons-logging을 사용하지 않는 방법은 크게 두 가지가 있다.

  1. commons-logging에 대해 유일하게 명시적으로 의존을 가지는 모듈인 spring-core 모듈에서 의존성을 제외시킨다.
  2. 특정 commons-logging에 대한 의존성을 빈 jar 라이브러리로 대체한다. (자세한 것은 SLF4J FAQ에서 찾을 수 있다.)

commons-logging을 제외하기 위해서, dependencyManagement에 다음 내용을 추가한다.

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.5.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

이제 이 애플리케이션은 클래스 패스상에 JCL API의 구현이 없기 때문에 빌드가 깨질 것이다. 따라서 새로운 구현체를 추가하면 이 문제를 해결할 수 있다. 다음 섹션에서 예제를 통해 SLF4J를 사용하는 JCL의 대체할 구현체를 제공하는 방법을 살펴볼 것이다.

SLF4J 사용하기

SLF4J는 commons-logging에 비해 의존성이 깔끔하며, 다른 로깅 프레임워크들이 런타임에서 탐색을 수행하는 대신에 SLF4J는 컴파일시 바인딩하기 때문에 런타임이 더 효율적이다. 달리 말하면 SLF4J는 런타임에서 벌어질 일을 더 명시적으로 지정해야 한다는 의미이다. 따라서 의존성을 선언하고 적절하게 설정해야 한다. SLF4J는 대부분의 로깅 프레임워크에 대한 바인딩을 지원하므로 기존에 사용하던 로깅 프레임워크를 계속 사용할 수 있다. 사용하는 로깅 프레임워크를 설정과 관리에 바인딩하면 된다.

SLF4J는 JCL을 포함한 많은 로깅 프레임워크에 대한 바인딩을 지원한다. 반대로 다른 로깅 프레임워크가 SLF4J로 바인딩할 수도 있다. 즉, 다른 로깅프레임워크와 SLF4J사이에 브릿지로 사용할 수도 있다. 그래서 스프링과 SLF4J를 함께 사용하려면 commons-logging의존성을 SLF4J-JCL 브릿지로 바꿔야 한다. 일단 설정을 바꾸었으면 스프링에서 호출된 로깅은 SLF4J API를 호출하는 로깅으로 전환된다. 따라서 애플리케이션의 다른 라이브러리가 같은 API의 로깅을 사용한다면 한 곳에서 로깅을 설정하고 관리할 수 있다.

스프링에서 브릿지를 통해 SLF4J로 연결하고, 다시 명시적으로 SLF4J를 Log4J로 바인딩하는 것이 가장 일반적이다. 브릿지, SLF4J API, Log4J로의 바인딩, Log4J구현체의 4가지 의존성을 추가해야 한다 (기존에 존재하던 commons-logging는 제외 처리). Maven이라면 다음과 같이 추가한다.

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.5.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.5.8</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.14</version>
    </dependency>
</dependencies>

단순히 로깅을 사용하기 위해 너무 많은 의존성을 추가한 것처럼 보일 수 있다. 하지만 이는 선택사항이고 평범한 commons-logging보다 나은 동작을 한다. 특히 commons-logging는 OSGi 플랫폼 같은 엄격한 컨테이너를 사용한다면 클래스로더 이슈도 고려해야 한다. 또한, 바인딩이 런타임이 아닌 컴파일 시점에 이뤄지기 때문에 성능적인 이점도 있다고 알려져 있다.

SLF4J 사용자들 사이에는 더욱 보변적인 선택이 있는데 Logback으로 직접적으로 바인딩하는 방법이다. 이는 더 적은 단계들과 의존성들의 발생을 줄여주는 방법이다. Logback은 SLF4J를 직접적으로 구현하고 있기 때문에 추가 바인딩 단계를 줄일 수 있다. 따라서 4개가 아닌 2개( jcl-over-slf4jlogback)의 라이브러리에만 의존할 수 있다. 클래스패스 상에 오직 한가지 버전의 API만 존재하기를 바라기 때문에, 스프링이 아닌 별도의 외부 의존성들로 부터 slf4j-api에 대한 의존성을 제외할 필요가 있다.

Log4J 사용하기

많은 사람들이 설정과 관리 때문에 로깅 프레임워크로 Log4j를 사용한다. Log4j는 사용하기 편하고 잘 구성되어 있다. 사실 스프링을 만들고 테스트할 때 런타임에서 Log4j를 사용했다. 스프링은 Log4j의 설정과 초기화를 위한 몇 가지 유틸리티도 제공한다. 그래서 스프링의 몇몇 모듈에는 컴파일 시점에 Log4j에 대한 선택적인 의존성이 존재한다.

기본 JCL 의존성(commons-logging)과 함께 Log4j가 동작하게 하려면 클래스패스에 Log4j를 두고 클래스패스의 루트에 설정파일 (log4j.propertieslog4j.xml)을 두면 된다. 메이븐 사용자라면 의존성 선언에 추가한다.

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.14</version>
    </dependency>
</dependencies>

다음은 콘솔에 로그를 출력하는 log4j.properties의 예제이다.

log4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n

log4j.category.org.springframework.beans.factory=DEBUG
네이티브 JCL 런타임 컨테이너 사용하기

많은 사람들이 JCL의 구현체를 제공하는 컨테이너에서 스프링 애플리케이션을 구동한다. IBM 웹스피어(Websphere) 애플리케이션 서버(WAS)가 대표적이다. 웹스피어는 종종 문제를 일으키는데 불행히도 거기에는 은총알(완벽한 해결책)은 없다. 대부분의 상황에서 단순히 애플리케이션에서 commons-logging를 제외처리하는 것으로는 해결되지 않는다.

이 문제를 좀 더 명확히 보자. 알려진 문제점은 JCL이나 commons-logging자체의 문제는 아니다. 더 정확히 말하면 commons-logging를 다른 프레임워크(대개 Log4J)로 바인딩해서 발생한 문제다. 바인딩이 실패하는 이유는 몇몇 컨테이너가 사용하는 commons-logging의 오래된 버전(1.0)과 대부분이 사용하는 현재 버전(1.1)사이에서 런타임 탐색 방법이 달라졌기 때문이다. 스프링은 JCL API에서 특이한 API는 전혀 사용하지 않으므로 문제가 발생하지 않는다. 하지만 스프링이나 애플리케이션이 로깅을 시도하자마자 Log4J로의 바인딩이 동작하지 않는 것을 발견할 수 있을 것이다.

WAS에서 이러한 상황을 겪을 가장 쉬운 해결책은 컨테이너 대신에 애플리케이션이 JCL 의존성을 제어하도록 클래스 로더의 계층을 거꾸로 하는 것이다(IBM은 이를 "parent last"라고 부른다). 이 옵션은 항상 열려 있는 것은 아니지만, 대안적인 방법을 위한 공개된 영역에서 다수의 다른 제안이 존재한다. 아마 컨테이너의 정확한 버전과 기능에 따라 방법이 다를 것이다.