프로 스프링 3 : 스프링 프레임워크의 원리와 활용

168

Post on 22-Jul-2016

287 views

Category:

Documents


34 download

DESCRIPTION

클라렌스 호, 롭 해롭 지음 | 유윤선 옮김 | 오픈소스 & 웹 시리즈 _ 041 | ISBN: 9788998139032 | 45,000원 | 2012년 09월 28일 발행 | 1088쪽

TRANSCRIPT

Page 1: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용
Page 2: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용
Page 3: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

프로

스프링 3

Page 4: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

•차 례•

01장 스프링 소개 1

스프링이란? ........................................................................................................2

제어 역전 또는 의존성 주입 ..........................................................................2

의존성 주입의 발전 과정 ...............................................................................3

의존성 주입을 넘어 ........................................................................................6

스프링 프로젝트 .............................................................................................. 13

스프링의 기원 ...............................................................................................13

스프링 커뮤니티 ...........................................................................................14

스프링 .NET ................................................................................................14

스프링소스 툴 스위트/스프링 IDE.............................................................15

스프링 시큐리티 프로젝트 ..........................................................................15

스프링 배치 및 스프링 인티그레이션 ........................................................16

그 외 많은 프로젝트 .....................................................................................16

스프링의 대안 .................................................................................................. 16

JBoss Seam 프레임워크 ...............................................................................16

구글 주스 .......................................................................................................17

PicoContainer ..............................................................................................17

JEE 6 컨테이너 .............................................................................................17

정리 ................................................................................................................... 18

02장 스프링 시작하기 19

스프링 프레임워크 내려받기 ......................................................................... 20

표준 배포판 내려받기 ..................................................................................21

깃허브를 통한 스프링 체크아웃 .................................................................21

스프링 패키징의 이해 ..................................................................................... 21

Page 5: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

스프링 모듈 ...................................................................................................22

애플리케이션에 적합한 모듈 선택 .............................................................24

메이븐 저장소의 스프링 모듈 .....................................................................25

스프링 의존성 분석 ......................................................................................... 28

예제 애플리케이션 .......................................................................................... 31

스프링 예제 소스코드 내려받기 .................................................................31

Petclinic 애플리케이션 ................................................................................31

Petclinic 그루비 애플리케이션 ...................................................................32

jPetStore 애플리케이션 ...............................................................................32

스프링 설정 기본 애플리케이션 .................................................................33

스프링 작업과 스케줄링 애플리케이션 .....................................................33

스프링 MVC 쇼케이스 애플리케이션 .......................................................33

스프링 MVC 기본과 Ajax 애플리케이션 ..................................................34

스프링 Petcare 애플리케이션 .....................................................................34

스프링 웹플로우 애플리케이션 ..................................................................35

스프링 문서 ...................................................................................................... 35

스프링을 활용한 ‘Hello World!’ 출력 ........................................................... 35

예제 ‘Hello World!’ 애플리케이션 개발 ....................................................36

STS에서의 스프링 프로젝트 생성 ..............................................................41

정리 ................................................................................................................... 46

03장 예제 애플리케이션 47

SpringBlog 애플리케이션의 요구 조건 ......................................................... 48

보안과 인증 ...................................................................................................48

블로그 글 보기 ..............................................................................................50

Page 6: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

블로그 글 올리기 ..........................................................................................51

블로그 글에 댓글 달기 .................................................................................52

비속어 필터링하기 .......................................................................................52

블로그 글이나 댓글에 파일 첨부하기 ........................................................54

블로그 행동 오디팅하기 ..............................................................................55

RSS 피드 ........................................................................................................55

XML 파일로 블로그 업로드하기 ................................................................55

SpringBlog의 구현 .......................................................................................56

개발 툴과 의존성 관리 .................................................................................56

애플리케이션 설계 .......................................................................................57

애플리케이션 설정 관리 ..............................................................................57

SpringBlog의 계층화된 애플리케이션 아키텍처 ......................................58

영속성 레이어의 구현 ..................................................................................59

서비스 레이어의 구현 ..................................................................................60

AOP를 활용한 비속어 필터링 ....................................................................61

정리 ................................................................................................................... 64

04장 스프링의 IoC 및 DI 소개 65

제어 역전과 의존성 주입 ............................................................................... 66

제어 역전의 종류 ............................................................................................. 66

의존성 풀 .......................................................................................................67

컨텍스트화된 의존성 룩업 ..........................................................................68

생성자 의존성 주입 ......................................................................................69

세터 의존성 주입 ..........................................................................................70

주입 vs. 룩업 .................................................................................................70

세터 주입 vs. 생성자 주입 ...........................................................................72

스프링의 제어 역전 ......................................................................................... 75

스프링을 통한 의존성 주입 ............................................................................ 76

빈 및 빈팩터리 ..............................................................................................77

BeanFactory 구현체 ....................................................................................78

Page 7: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

ApplicationContext .....................................................................................80

ApplicationContext의 설정 ........................................................................... 80

스프링 설정 옵션(XML 및 자바 애노테이션) ...........................................80

스프링 컴포넌트의 선언 ..............................................................................83

세터 주입의 활용 ..........................................................................................87

생성자 주입의 활용 ......................................................................................89

주입 파라미터 ...............................................................................................95

메서드 주입 .................................................................................................114

빈 이름 지정 방식의 이해 ..........................................................................125

빈 인스턴스화 모드 ....................................................................................128

의존성 리졸브 ................................................................................................ 133

빈 자동 연결 ................................................................................................... 133

자동 연결 모드 ............................................................................................134

자동 연결을 사용할 시점 ...........................................................................137

빈 상속 ............................................................................................................ 137

정리 ................................................................................................................. 139

05장 스프링 설정 상세 141

스프링이 애플리케이션 이식성에 미치는 영향 ........................................ 142

빈 생명주기 관리 ........................................................................................... 143

빈 생성 시점에 대한 개입 ..........................................................................145

빈 소멸에 대한 개입 ...................................................................................154

빈의 스프링 인식 ........................................................................................... 163

BeanNameAware 인터페이스의 사용......................................................163

ApplicationContextAware 인터페이스의 사용 ......................................165

FactoryBean의 사용 ..................................................................................... 168

커스텀 FactoryBean 예제: MessageDigestFactoryBean ........................168

FactoryBean 직접 접근 ..............................................................................173

factory-bean 및 factory-method 속성의 사용 ........................................174

자바빈 프로퍼티에디터 ................................................................................ 176

Page 8: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

내장 프로퍼티에디터 .................................................................................177

커스텀 PropertyEditor의 생성 .................................................................182

스프링 ApplicationContext 설정 살펴보기 ............................................... 186

MessageSource를 활용한 국제화 .............................................................187

단독 실행형 애플리케이션에서의 MessageSource 사용 ........................192

MessageSourceResolvable 인터페이스....................................................192

애플리케이션 이벤트 .................................................................................192

리소스 접근 .................................................................................................196

자바 클래스를 활용한 설정 .......................................................................... 198

자바를 활용한 ApplicationContext 설정 ................................................198

자바 설정과 XML 설정 사이의 선택 .......................................................206

프로필 ............................................................................................................. 206

스프링 프로필을 사용한 예제 ..................................................................206

사용자 프로필에 대한 고려 사항 ..............................................................213

Environment와 PropertySource 추상화 .................................................... 214

JSR-330 애노테이션을 활용한 설정 ............................................................ 219

정리 ................................................................................................................. 224

06장 스프링 AOP 소개 225

AOP 개념 ....................................................................................................... 227

AOP의 종류 ................................................................................................... 228

정적 AOP ....................................................................................................228

동적 AOP ....................................................................................................229

AOP 방식의 선택 .......................................................................................229

스프링에서의 AOP ....................................................................................... 230

AOP 연합 ....................................................................................................230

AOP를 활용한 'Hello World!' ...................................................................230

스프링 AOP 아키텍처 ...............................................................................233

ProxyFactory 클래스에 대한 소개 ...........................................................235

스프링에서의 어드바이스 생성 ................................................................236

Page 9: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

스프링의 어드바이저와 포인트컷 ............................................................... 256

Pointcut 인터페이스 ..................................................................................257

프록시의 이해 ................................................................................................ 277

JDK 동적 프록시의 사용 ...........................................................................278

CGLIB 프록시의 사용 ...............................................................................278

프록시 성능 비교 ........................................................................................279

어떤 프록시를 사용할까? ..........................................................................284

정리 ................................................................................................................. 285

07장 스프링 AOP와 애노테이션 활용 287

포인트컷의 고급 활용 ................................................................................... 288

제어 흐름 포인트컷의 사용 .......................................................................288

ComposablePointcut의 활용 ....................................................................292

합성 및 Pointcut 인터페이스 ....................................................................296

포인트컷 정리 .............................................................................................296

인트로덕션의 시작 ........................................................................................ 297

인트로덕션 기본 .........................................................................................297

인트로덕션을 통한 객체 수정 감지 ..........................................................300

인트로덕션 정리 .........................................................................................307

AOP 프레임워크 서비스 .............................................................................. 307

선언적인 AOP 설정 ...................................................................................307

ProxyFactoryBean의 사용 ........................................................................308

aop 네임스페이스 사용..............................................................................314

@AspectJ 방식의 애노테이션 활용 ..........................................................321

선언적 스프링 AOP 설정에 대한 고려 사항 ............................................326

AspectJ 연동 .................................................................................................. 327

AspectJ 소개 ...............................................................................................327

싱글턴 애스펙트의 활용 ............................................................................328

예제 애플리케이션의 AOP .......................................................................... 333

SpringBlog의 비속어 필터링 ....................................................................334

정리 ................................................................................................................. 335

Page 10: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

08장 스프링 JDBC 지원 337

예제 코드의 예제 데이터 모델 ..................................................................... 339

JDBC 인프라스트럭처 살펴보기 ................................................................. 343

스프링 JDBC 인프라스트럭처 ..................................................................... 349

개요 및 사용 패키지 ...................................................................................349

데이터베이스 커넥션과 데이터소스 ........................................................350

임베디드 데이터베이스 지원 ....................................................................354

DAO 클래스의 데이터소스 활용 ................................................................. 355

예외 처리 ........................................................................................................ 357

JdbcTemplate 클래스 .................................................................................... 359

DAO 클래스에서의 JdbcTemplate 초기화 .............................................359

단일 값을 조회하는 JdbcTemplate 클래스의 사용 .................................360

NamedParameterJdbcTemplate를 통한 네임드 파라미터의 활용........362

RowMapper<T>를 활용한 도메인 객체 조회 .........................................363

ResultSetExtractor를 활용한 중첩 도메인 객체 조회 ............................365

JDBC 작업을 모델링하는 스프링 클래스 .................................................. 367

애노테이션을 활용한 JDBC DAO 설정 ...................................................368

MappingSqlQuery<T>를 활용한 데이터 쿼리 .......................................372

SqlUpdate를 활용한 데이터 업데이트 .....................................................377

데이터 삽입 및 생성 키 조회 .....................................................................380

BatchSqlUpdate를 활용한 배치 작업 .......................................................382

SqlFunction을 활용한 저장 함수 호출 .....................................................386

자바 설정의 활용 ........................................................................................... 391

스프링 데이터 프로젝트 - JDBC 확장 프로젝트 ....................................... 392

JDBC 사용에 대한 고려사항 ........................................................................ 393

정리 ................................................................................................................. 394

Page 11: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

09장 하이버네이트 활용 395

STS를 통한 하이버네이트 유틸리티 프로젝트 생성 .................................. 397

예제 코드에 사용할 예제 데이터 ................................................................. 399

하이버네이트 SessionFactory 설정 ............................................................. 402

하이버네이트 애노테이션을 활용한 ORM 매핑........................................ 404

간단한 매핑 .................................................................................................405

일대다 매핑 .................................................................................................410

다대다 매핑 .................................................................................................412

하이버네이트 Session 인터페이스 .............................................................. 413

하이버네이트를 활용한 데이터베이스 작업 .............................................. 414

하이버네이트 쿼리 언어를 활용한 데이터 쿼리 .....................................415

데이터 삽입 .................................................................................................421

데이터 업데이트 .........................................................................................423

데이터 삭제 .................................................................................................424

하이버네이트 사용에 대한 고려 사항 ........................................................ 425

정리 ................................................................................................................. 426

10장 JPA2를 활용한 데이터 접근 427

JPA 2 소개 ...................................................................................................... 429

STS에서의 스프링 JPA 유틸리티 프로젝트 생성 ...................................430

예제 코드의 예제 데이터 모델 ..................................................................432

JPA EntityManagerFactory의 설정 ..........................................................432

JPA 애노테이션을 활용한 ORM 매핑 ......................................................435

DAO 레이어의 제거 ...................................................................................435

서비스 레이어 클래스로의 EntityManager 주입 ....................................436

JPA를 활용한 데이터 작업 ........................................................................... 437

자바 영속성 쿼리 언어를 활용한 데이터 쿼리 .......................................438

데이터 삽입 .................................................................................................447

데이터 업데이트 .........................................................................................450

Page 12: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

데이터 삭제 .................................................................................................451

네이티브 쿼리 .............................................................................................452

JPA 2 기준 API를 활용한 기준 쿼리 ........................................................455

스프링 데이터 JPA 소개 ............................................................................... 461

스프링 데이터 JPA 라이브러리 의존성 추가 ...........................................462

스프링 데이터 JPA 저장소 추상화를 활용한 데이터베이스 작업 .........463

엔티티 클래스의 변화 추적 .......................................................................469

하이버네이트 엔버스를 활용한 엔티티 버전 관리..................................... 477

하이버네이트 엔버스 의존성 추가 ...........................................................478

엔티티 버전 관리 테이블의 추가 ..............................................................478

엔티티 버전 관리를 위한 EntityManagerFactory 설정 ..........................480

엔티티 버전 관리와 히스토리 조회에 필요한 코드 수정 .......................482

엔티티 버전 관리 테스트 ...........................................................................484

JPA 사용에 대한 고려 사항 .......................................................................... 485

예제 애플리케이션에선의 JPA 활용 ............................................................ 486

데이터베이스 백엔드 .................................................................................486

JPA를 영속성 레이어 구현체로 활용 ........................................................486

오디팅과 엔티티 버전 관리 .......................................................................487

정리 ................................................................................................................. 487

11장 마이바티스 활용 489

스프링에서 마이바티스 시작하기 ............................................................... 490

마이바티스 소개 .........................................................................................490

STS에서 마이바티스 지원 기능을 갖춘 유틸리티 프로젝트의 생성 .....491

예제 코드에서 사용할 예제 데이터 모델 .................................................493

MyBatis SqlSessionFactory와 MapperScannerCon�gurer 설정 .........495

마이바티스의 SQL 매핑 ............................................................................... 497

매퍼 인터페이스와 SQL 매핑 파일 ..........................................................498

SQL 매핑 XML 설정 ..................................................................................500

마이바티스를 활용한 데이터베이스 작업 .................................................. 501

Page 13: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

데이터 조회 .................................................................................................501

데이터 삽입 .................................................................................................520

데이터 업데이트 .........................................................................................525

데이터 삭제 .................................................................................................530

마이바티스 사용에 대한 고려사항 ............................................................. 533

예제 애플리케이션의 마이바티스 활용 ...................................................... 533

데이터베이스 백엔드 .................................................................................534

마이바티스를 활용한 영속성 레이어 구현체 ..........................................534

오디팅과 엔티티 버전 관리 .......................................................................534

정리 ................................................................................................................. 535

12장 스프링 기반 애플리케이션의 설계와 구현 537

인터페이스를 통한 설계 ............................................................................... 539

인터페이스로 설계해야 하는 이유 ...........................................................539

팩터리 패턴 .................................................................................................540

인터페이스 기반 설계에 미치는 스프링의 영향 .....................................543

도메인 객체 모델의 개발 .............................................................................. 544

스프링과 도메인 객체 모델 .......................................................................544

DOM은 값 객체와 다르다 ........................................................................545

도메인 객체 모델의 생성 이유 ..................................................................546

도메인 객체 모델링 ....................................................................................546

동작 캡슐화 여부에 대한 결정 ..................................................................549

SpringBlog 도메인 객체 모델 ...................................................................... 549

도메인 객체 모델 정리 ...............................................................................556

데이터 접근 레이어의 설계 및 개발 ............................................................ 556

현실적인 설계 고려 사항 ...........................................................................558

데이터 접근 레이어 정리 ...........................................................................559

서비스 레이어의 설계 ................................................................................... 560

서비스 레이어가 필요한 이유 ...................................................................560

비즈니스 인터페이스의 설계 ....................................................................561

Page 14: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

서비스 레이어 정리 ....................................................................................562

정리 ................................................................................................................. 563

13장 트랜잭션 관리 565

스프링 트랜잭션 추상 레이어 살펴보기 .................................................... 566

트랜잭션의 종류 .........................................................................................566

PlatformTransactionManager의 구현체 .................................................568

트랜잭션 속성의 분석 ................................................................................... 569

TransactionDe�nition 인터페이스 ..........................................................570

TransactionStatus 인터페이스..................................................................572

예제 데이터 모델과 예제 코드의 인프라스트럭처..................................... 573

의존성을 포함한 간단한 스프링 JPA 유틸리티 프로젝트 생성 .............573

예제 데이터 모델과 공통 클래스 ..............................................................575

스프링의 선언적 트랜잭션과 프로그래밍적 트랜잭션 .............................. 578

애노테이션 활용한 트랜잭션 관리 ...........................................................578

XML 설정을 활용한 트랜잭션 관리 .........................................................588

프로그래밍적 트랜잭션 .............................................................................592

트랜잭션 관리에 대한 고려 사항 ..............................................................595

스프링의 전역 트랜잭션 ............................................................................... 595

JTA 예제 구현에 필요한 인프라스트럭처 ...............................................595

JTA를 활용한 전역 트랜잭션 구현 ...........................................................597

JTA 트랜잭션 매니저 사용에 대한 고려 사항 .........................................604

정리 ................................................................................................................. 605

14장 타입 변환과 포매팅을 활용한 유효성 검증 607

STS를 통한 예제 프로젝트의 생성 .............................................................. 608

스프링 타입 변환 시스템 .............................................................................. 609

PropertyEditor를 활용한 스프링의 변환.................................................609

스프링 3 타입 변환 소개 ............................................................................613

Page 15: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

스프링 3의 필드 포매팅 ................................................................................ 620

커스텀 포매터의 구현 ................................................................................620

ConversionServiceFactoryBean의 설정..................................................622

스프링의 유효성 검증 ................................................................................... 623

스프링 Validator 인터페이스의 활용 .......................................................625

JSR-303 빈 유효성 검증의 활용 ................................................................627

어떤 유효성 검증 API를 써야 할까?.........................................................637

예제 애플리케이션의 타입 변환과 포매팅을 활용한 유효성 검증 ........... 637

정리 ................................................................................................................. 640

STS를 통한 예제 프로젝트 생성 ................................................................. 641

15장 작업 스케줄링 641

스프링의 작업 스케줄링 ............................................................................... 642

스프링 TaskScheduler 추상화 소개..........................................................643

간단한 작업 .................................................................................................644

task 네임스페이스를 활용한 작업 스케줄링 ..........................................650

애노테이션을 활용한 작업 스케줄링 .......................................................652

스프링의 비동기적 작업 실행 ...................................................................... 654

예제 애플리케이션의 작업 스케줄링 .......................................................... 658

정리 ................................................................................................................. 659

16장 스프링 리모팅 활용 661

STS를 통한 예제 프로젝트 생성 ................................................................. 662

예제에 사용할 서비스 레이어의 구현 ........................................................ 664

JPA 백엔드에 필요한 의존성 추가 ............................................................664

프로젝트의 확인 .........................................................................................665

예제의 데이터 모델 ....................................................................................668

ContactService의 구현 및 설정 ................................................................668

스프링 HTTP 호출자의 활용 ....................................................................... 674

Page 16: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

서비스의 노출 .............................................................................................674

서비스의 호출 .............................................................................................675

스프링의 JMS 활용 ....................................................................................... 678

ActiveMQ 설정 ..........................................................................................679

스프링에서의 JMS 리스너 구현 ................................................................681

스프링에서의 JMS 메시지 전송 ................................................................684

스프링에서의 RESTful-WS 활용 ................................................................. 687

RESTful 웹 서비스의 소개 ........................................................................687

예제에 필요한 의존성의 추가 ...................................................................688

연락처 RESTful 웹 서비스 ........................................................................689

스프링 MVC를 활용한 RESTful 웹 서비스 노출 ....................................690

curl을 활용한 RESTful-WS 테스트 ..........................................................698

RestTemplate을 활용한 RESTful-WS 접근 .............................................700

스프링 시큐리티를 활용한 RESTful-WS 보호 ........................................705

RESTful 웹 서비스에서의 JSR-303 활용 ..................................................709

예제 애플리케이션에서의 리모팅 ............................................................... 712

정리 ................................................................................................................. 712

17장 스프링 웹 애플리케이션 713

STS를 통한 예제 프로젝트 생성 ................................................................. 715

예제의 서비스 레이어 구현 ......................................................................715

예제에 사용할 데이터 모델 .......................................................................716

ContactService의 구현 및 설정 ................................................................717

MVC 및 스프링 MVC 소개 ......................................................................... 723

MVC 소개 ...................................................................................................723

스프링 MVC 소개 ......................................................................................725

스프링 MVC의 첫 번째 뷰 생성 ................................................................. 731

DispatcherServlet의 설정 .........................................................................732

ContactController의 구현 ........................................................................733

연락처 목록 뷰의 구현 ...............................................................................734

연락처 목록 뷰의 테스트 ...........................................................................735

Page 17: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

스프링 MVC 프로젝트 구조의 개요 .......................................................... 737

i18n(국제화) .................................................................................................. 739

DispatcherServlet 설정에서의 i18n 설정 ................................................739

연락처 목록 뷰에 i18n 지원 기능 추가 ....................................................741

테마와 템플릿 ................................................................................................ 743

테마 지원 .....................................................................................................743

아파치 타일즈를 활용한 뷰 템플릿 ..........................................................746

연락처 정보 뷰의 구현 ................................................................................. 753

URL의 뷰 매핑 ...........................................................................................754

연락처 표시 뷰의 구현 ...............................................................................754

연락처 편집 뷰의 구현 ...............................................................................758

연락처 추가 뷰의 구현 ...............................................................................763

JSR-303 빈 유효성 검증 활성화 ................................................................765

제이쿼리 및 제이쿼리 UI의 활용 ................................................................ 768

제이쿼리 및 제이쿼리 UI의 소개 .............................................................769

뷰에서의 제이쿼리 및 제이쿼리 UI 활성화 .............................................769

CKEditor를 통한 리치 텍스트 편집 .........................................................772

jqGrid를 활용한 페이지형 데이터 그리드 ...............................................774

파일 업로드 처리 ........................................................................................... 782

파입 업로드 지원의 설정 ...........................................................................783

파일 업로드 지원을 위한 뷰 수정 .............................................................784

파일 업로드 지원을 위한 컨트롤러 수정 .................................................786

스프링 시큐리티를 활용한 웹 애플리케이션 보호..................................... 788

스프링 시큐리티 설정 ................................................................................789

애플리케이션의 로그인 기능 추가 ...........................................................791

애노테이션을 활용한 컨트롤러 메서드 보호 .........................................795

서블릿 3의 코드 기반 설정 지원 ................................................................. 797

예제 애플리케이션에서의 스프링 MVC ................................................... 799

SpringBlog의 MVC 구현체 ......................................................................799

리치 사용자 인터페이스와 Ajax ...............................................................800

보안 지원 .....................................................................................................800

서블릿 3.0 지원 ...........................................................................................801

정리 ................................................................................................................. 801

Page 18: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

18장 스프링 웹 플로우와 JSF 803

예제 백엔드 프로젝트 ................................................................................... 804

예제 백엔드 서비스 레이어 .......................................................................804

STS에서의 예제 백엔드 임포트 ................................................................805

스프링 웹 플로우 소개 .................................................................................. 806

스프링 웹 플로우 모델 ...............................................................................806

스프링 웹 플로우의 기능 ...........................................................................808

JSF 소개 .......................................................................................................... 809

뷰 .........................................................................................................810

모델 상호작용 .............................................................................................810

내비게이션 ..................................................................................................811

애플리케이션 생명주기 .............................................................................811

스프링 웹 플로우 애플리케이션 예제 ......................................................... 813

예제 플로우의 설계 ....................................................................................813

프로젝트 구조 .............................................................................................814

스프링 웹 플로우 및 JSF 설정 ..................................................................... 815

필요한 의존성의 추가 ................................................................................815

JSF의 설정 ...................................................................................................816

웹 배포 디스크립터의 설정 .......................................................................816

스프링 웹 플로우 및 스프링 MVC 설정 ..................................................818

예제 플로우의 구현 ....................................................................................... 821

플로우 정의 .................................................................................................822

템플릿 페이지의 구현 ................................................................................825

커스텀 변환기의 구현 ................................................................................827

컨트롤러 및 지원 빈의 구현 ......................................................................828

연락처 보기 뷰의 구현 ...............................................................................835

연락처 추가 플로우의 구현 .......................................................................... 838

1단계: 기본 정보 입력 ................................................................................838

2단계: 취미 선택 .........................................................................................843

3단계: 정보 검토 .........................................................................................845

4단계: 연락처 추가 완료 ............................................................................847

정리 ................................................................................................................. 850

Page 19: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

19장 스프링 테스트 851

예제 웹 애플리케이션 프로젝트 .................................................................. 852

STS에서의 예제 백엔드 임포트 ................................................................853

엔터프라이즈 테스트 프레임워크 소개 ..................................................... 854

논리 단위 테스트의 구현 .............................................................................. 857

필요한 의존성의 추가 ................................................................................857

스프링 MVC 컨트롤러 단위 테스트 ........................................................858

통합 단위 테스트의 구현 .............................................................................. 864

필요한 의존성의 추가 ................................................................................865

서비스 레이어 테스트에 필요한 프로필 설정 ........................................865

인프라스트럭처 클래스의 구현 ................................................................867

서비스 레이어의 단위 테스트 ...................................................................873

프론트엔드 단위 테스트의 구현 .................................................................. 877

필요한 의존성의 추가 ................................................................................877

Selenium 소개 ............................................................................................878

프론트엔드 UI의 테스트 케이스 구현......................................................878

테스트 케이스 코드 커버리지의 확인 ......................................................... 882

정리 ................................................................................................................. 885

20장 스프링 프로젝트: 배치, 인티그레이션, 루 887

예제 프로젝트 ................................................................................................ 888

STS에서 예제 백엔드 불러오기 ................................................................888

스프링 배치 소개 ........................................................................................... 890

배치 작업 흐름과 처리 ...............................................................................890

스프링 배치 인프라스트럭처 컴포넌트 ...................................................891

스프링 배치 메타데이터 ............................................................................... 893

작업 실행 정책 ............................................................................................894

배치 작업의 구현 ........................................................................................... 894

필요한 의존성의 추가 ................................................................................895

Page 20: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

스프링 배치 인프라스트럭처 설정 ...........................................................895

연락처 불러오기 작업의 구현 ...................................................................897

스프링 인티그레이션과 연동한 스프링 배치 활용..................................... 907

스프링 인티그레이션의 소개 ....................................................................907

필요한 의존성의 추가 ................................................................................908

파일 폴링 메커니즘의 구현 .......................................................................909

스프링 루 소개 ............................................................................................... 914

STS에서의 스프링 루 설정 ........................................................................915

스프링 루 프로젝트의 생성 .......................................................................916

프레젠테이션 레이어와 엔티티 클래스의 설정 ......................................917

서비스 레이어의 설정 ................................................................................921

프레젠테이션 레이어의 설정 ....................................................................922

스프링 루 애드온 ........................................................................................926

스프링 루에 대한 결론 ...............................................................................927

예제 애플리케이션에서의 스프링 배치 및 스프링 인티그레이션 활용 ... 927

정리 ................................................................................................................. 928

21장 예제 애플리케이션 살펴보기 929

예제 애플리케이션의 설정 ........................................................................... 930

프로젝트 설정 .............................................................................................931

MySQL과 H2 데이터베이스 사이의 전환 ...............................................933

JPA와 마이바티스 구현체 사이의 전환 ....................................................936

애플리케이션 설계 ........................................................................................ 938

데이터 모델 .................................................................................................938

도메인 객체 모델 ........................................................................................939

UML 모델 ...................................................................................................941

설정 상세 정보 ............................................................................................... 948

구현체 상세 내용 ........................................................................................... 955

서비스 레이어 구현체 ................................................................................955

AOP를 활용한 비속어 필터 ......................................................................962

Page 21: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

오디팅 데이터를 정리하는 작업 스케줄링 ..............................................966

프레젠테이션 레이어 .................................................................................968

웹 리소스 파일 폴더 구조 ..........................................................................969

정리 ................................................................................................................. 975

22장 스프링의 스크립트 지원 977

예제 프로젝트 ................................................................................................ 978

간단한 스프링 유틸리티 프로젝트의 생성 ..............................................979

이클립스용 그루비 플러그인의 설치 .......................................................980

자바의 스크립트 지원 ................................................................................... 982

그루비의 소개 ................................................................................................ 984

동적 타입 지정 ............................................................................................984

단순한 구문 .................................................................................................986

클로저 .........................................................................................................987

그루비와 스프링의 활용 ............................................................................... 988

필요한 의존성의 추가 ................................................................................989

Contact 도메인 ...........................................................................................989

규칙 엔진의 구현 ........................................................................................990

규칙 팩터리를 갱신 가능 빈으로 구현 ....................................................994

나이 카테고리 규칙의 테스트 ...................................................................996

정리 ............................................................................................................... 1000

23장 스프링 애플리케이션 모니터링 1001

예제 프로젝트 .............................................................................................. 1002

스프링의 JMX 지원 ..................................................................................... 1004

스프링 빈의 JMX 익스포트 .....................................................................1005

JMX 모니터링을 위한 VisualVM 설정 ..................................................1007

로그인 사용자 모니터링 ..........................................................................1009

하이버네이트 통계 모니터링 ..................................................................1014

Page 22: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxii

스프링 배치 작업 모니터링 .....................................................................1016

스프링 인사이트를 활용한 애플리케이션 모니터링 ................................ 1020

스프링 인사이트 소개 ..............................................................................1021

스프링 인사이트의 설정 ..........................................................................1021

스프링 인사이트 활용 ..............................................................................1024

정리 ............................................................................................................... 1027

부록 스프링소스 툴 스위트 1029

STS 소개 ....................................................................................................... 1030

STS 설치 ....................................................................................................... 1030

STS 단독 실행 버전 설치 .........................................................................1030

기존 이클립스 환경 대상 STS 설치 ........................................................1035

프로젝트 설정 및 의존성 관리 .................................................................. 1040

간단한 스프링 유틸리티 프로젝트의 생성 ...........................................1041

프로젝트 의존성 관리 ..............................................................................1045

STS 활용법 ................................................................................................... 1046

STS 플러그인 설치 ...................................................................................1046

STS에서의 VMware tc 서버 설정 ...........................................................1048

정리 ............................................................................................................... 1050

찾아보기 ......................................................................... 1051

Page 23: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxiii

지원과 충고를 아끼지 않고 소중한 경험을 선사해주는

친구와 동료들에게 이 책을 바치고 싶다. 또 나의 가족,

특별히 나를 진심으로 사랑해주고 나의 모든 결정을 따뜻하게 격려해준

어머니께 이 책을 바친다. 그리고 이 책을 쓰는 데 큰 도움을 준

크리스, 마누엘, 브렌트 등 저술 팀에도 감사를 드린다.

— 클라렌스 호

Page 24: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxiv

•옮 긴 이 글•

이 책은 스프링에 대한 완벽 가이드이자 스프링답게 스프링 애플리케이션을 개발하는 법을 소

개하는 최고의 지침서다. 이 책에서는 스프링의 다양한 요소를 폭넓게 다루며 언제 어떤 식으로

스프링 애플리케이션 개발에 접근해야 하는지 자세히 설명하고 있다. 그래서 이 책은 스프링을

처음 접하는 독자는 물론, 나름 스프링을 조금 안다고 하는 독자들도 꼭 읽어야 하는 스프링의

바이블 같은 책이다.

이 책은 단순히 스프링의 방대한 기능이나 API를 설명하는 데 그치지 않는다. 이 책에서는 왜

스프링의 설계 방식이 중요하고, 스프링답게 애플리케이션을 설계하고 구현하는 게 이로운지 자

세히 설명한다. 또 스프링이 제공하는 다양한 옵션 사이에서(이를테면 XML 설정, 애노테이션

설정, 자바 설정 등) 언제 어떤 옵션을 택하는 게 가장 적합한지 다양한 실전 사례를 통해 설명한

다. 그래서 이 책을 읽다 보면 자연스럽게 스프링의 철학을 체득할 수 있고, 모범 개발 기법을 통

해 애플리케이션을 개발하는 혜안을 얻을 수 있다.

이 책은 최신 스프링 환경에 맞게 다양한 최신 기법을 소개하는 것도 빼먹지 않는다. 예컨대

스프링 3.1에서 새로 도입된 프로필 기능을 활용해 설정을 분리하는 기법이나 서블릿 3.0 컨테이

너의 멀티파트 지원 기능을 활용한 파일 업로드 기능을 활용한 예제가 이에 해당한다. 또한 저자

는 일반적으로 개발자들에게 익숙한 DAO 레이어를 그대로 사용하는 대신 JPA를 활용해 엔티

티매니저를 서비스 레이어로 직접 주입하는 방식을 통해 최신 개발 기법을 설명하는 등 스프링

애플리케이션에서 불필요한 요소를 제거하고 애플리케이션을 유지보수하기 쉽게 관리하는 다

양한 전략을 소개한다.

이 책에서는 각기 다른 스프링 관련 주제를 폭넓게 다루는 동시에 모든 기능을 한데 아우르

는 대규모 애플리케이션의 개발도 함께 진행한다. 이 책에서 계속해서 개발하는 SpringBlog 애

플리케이션은 스프링 코어, 스프링 MVC, 스프링 시큐리티, AOP를 통한 필터링, 스프링 뷰 기술

(Ajax, 제이쿼리), 아파치 타일즈, 스프링 배치, 스프링 인티그레이션 등 가히 스프링의 모든 기능

을 담았다고 해도 과언이 아닐 정도로 방대하다. 따라서 독자들은 각 장을 통해 스프링 관련 지

Page 25: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxv

식을 쌓을 수 있음은 물론 SpringBlog 예제를 통해 전체적인 맥락에서 스프링을 이해하고 각 요

소가 서로 어떻게 연동되는지 한눈에 볼 수 있다.

이 책은 역자가 지금까지 읽은 스프링 관련 서적 중 최고의 서적이다. 이 책에 담긴 수많은 혜안

은 모두 이 책을 열심히 읽는 독자들의 것이다. 이 책을 통해 재미있는 스프링 세계를 경험하기를

바란다.

대상 독자

이 책은 스프링을 처음 접하는 독자는 물론 기존에 스프링 사용 경험이 있는 개발자를 포함해

스프링이 필요한 모든 사람이 대상 독자다. 이 책에서는 기본적인 Hello World 예제부터 시작해

스프링 코어, 스프링 AOP 등의 주제를 다양한 예제를 통해 조금씩 설명하는 만큼 기존에 스프

링을 접한 적이 없더라도 충분히 책을 따라올 수 있다. 또 스프링 개발 경험이 있는 독자라면 이

책에서 소개하는 다양한 모범 개발 기법과 최신 기술을 통해 남들과는 차별화된 프로 스프링 개

발자로 발돋움할 수 있을 것이다.

감사의 글

이 책을 집필한 저자인 클라렌스 호, 롭 해롭에게 감사하다. 또 번역을 맡겨주신 위키북스에도

감사드린다. 그리고 항상 나와 함께 하시는 하나님과 가족들에게도 감사의 인사를 전한다.

- 유윤선

Page 26: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxvi

지은이 소개

클라렌스 호(Clarence Ho)는 홍콩에 위치한 소프트웨어 컨설팅

회사인 SkywideSo� Technology Limited(www.skywideso�.com)의

자바 아키텍트다. 클라렌스는 IT 분야에서 20년 이상 여러 인하우스

애플리케이션 개발 프로젝트에서 팀 리더를 맡았으며, 클라이언트에게

엔터프라이즈 컨설팅 서비스를 제공했다. 클라렌스는 2001년부터 자

바 프로그래밍을 시작했고, 2005년부터는 EJB, 스프링 프레임워크, 하

이버네이트, JMS, WS 같은 기술을 사용해 JEE 애플리케이션을 설계

하고 개발하는 업무를 집중적으로 담당했다. 클라렌스는 2005년부터 자바 엔터프라이즈 아키

텍트로 근무하고 있다.

현재 클라렌스는 국제 금융 기관의 컨설턴트로 근무하면서 자바 EE 아키텍처 설계, 교육, 기술 솔

루션에 대한 컨설팅, 애플리케이션 개발 모범 기법에 대한 교육 등 여러 분야에서 기여하고 있다.

여가 시간에 클라렌스는 운동(조깅, 수영, 축구, 하이킹)을 하거나, 독서, 영화 감상, 친구들과 돌

아다니기를 즐긴다.

롭 해롭(Rob Harrop)은 큰 성공을 거둔 스프링 프레임워크를 제공하는 소프트웨어 회사인

스프링소스의 공동 설립자다. 현재 롭은 First Banco의 CTO다. 스프링소스를 설립하기 전에는

영국 맨처스터에 있는 부티크 컨설팅 업체인 Cake Solutions의 공동 설립자이자 CTO였다. 롭은

대용량 대규모 엔터프라이즈 시스템 개발을 전문으로 한다.

롭은 5권 이상의 기술 서적을 쓰거나 함께 썼다. 트위터에서는 @robertharrop을 통해 롭을 팔로

우할 수 있다.

Page 27: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxvii

기술 감수자 소개

마누엘 조단 엘레라(Manuel Jordan Elera)는 자신만의 실험 또

는 새로운 형태의 연동을 위해 새 기술을 배우기를 좋아하는 독학

개발자다.

마누엘은 2010 스프링 어워드의 커뮤니티 챔피언 수상자다. 여가 시

간에는 성경을 읽고 기타로 음악을 작곡한다. 마누엘은 스프링 커뮤

니티 포럼의 시니어 회원이며 dr_pompeii라는 이름을 사용한다.

마누엘은 다음 책의 기술 감수를 담당했다(모두 Apress 출판).

▒ Pro SpringSource dm Server(2009)

▒ Spring Enterprise Recipes(2009)

▒ Spring Recipes(Second Edition)(2010)

▒ Pro Spring Integration(2011)

▒ Pro Spring Batch(2011)

▒ Pro Spring MVC: With Web Flow(2012)

마누엘의 글 또는 연락처는 http://manueljordan.wordpress.com/의 블로그에서 확인할 수 있으

며 @dr_pompeii 트위터 계정을 통해 팔로우할 수 있다.

Page 28: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

xxviii

Page 29: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

01스프링 소개

자바 개발자 커뮤니티를 떠올리면 1840년대 말, 금을 찾아 북미의 강을 헤집고 다니던 무리들이

생각난다. 자바 개발자들의 강에도 오픈소스 프로젝트로 가득 차 있지만, 금을 찾던 사람들만큼

이나 쓸만한 프로젝트를 찾는 데는 시간과 노력이 든다.

많은 오픈소스 자바 프로젝트에 대해 사람들이 털어놓는 불만은 이런 오픈소스가 가장 최근

에 사람들이 자주 얘기하는 기술이나 패턴 구현체의 부족한 부분을 메우는 목적으로만 고안됐

다는 점이다. 하지만 이런 불만과 상관없이 실전에서 애플리케이션을 개발할 때 접하는 실제 문

제를 해결해주는 고품질의 쓸만한 프로젝트가 많이 나와 있으며, 이 책에서는 이런 프로젝트 중

일부를 살펴볼 것이다. 특히 그 중에서도 스프링 프로젝트에 대해 자세히 다룬다.

이 책에서는 다양한 오픈소스 기술을 여러 가지로 활용하는 법을 살펴볼 텐데, 이런 오픈소

스 기술은 모두 스프링 프레임워크와 연동된다. 스프링을 사용하면 애플리케이션 개발자는 많은

코드를 작성하거나 애플리케이션과 특정 툴을 결합시키지 않고도 다양한 오픈소스 툴을 활용할

수 있다.

이 장에서는 제목에서 말하는 것처럼 구체적인 예제나 설명을 보는 대신 스프링 프레임워크에

대해 소개한다. 이미 스프링 프로젝트에 익숙한 독자라면 이 장을 건너뛰고 바로 2장을 살펴봐

도 된다.

Page 30: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2 l 프로 스프링 3

스프링이란?

스프링을 하나의 기술로 설명할 때 가장 어려운 점은 스프링의 범주를 정확히 구분하는 것이다.

보통 스프링은 자바 애플리케이션 개발을 위한 경량 프레임워크로 설명하는데, 이 말에는 두 가

지 재미있는 특징이 들어 있다. 먼저 스프링은 자바로 어떤 애플리케이션(단독 실행형, 웹, JEE

애플리케이션 등)도 개발할 수 있게 도와준다. 이는 아파치 스트러츠 같은 다른 여러 프레임워크

가 웹 애플리케이션에 국한되는 점과 다른 점이다. 두 번째로 이 설명에서 ‘경량’이란 부분은 클

래스 개수나 배포판의 크기를 말하기보다는 전체적인 스프링 철학을 가리킨다. 즉, 최소한의 영

향을 준다는 의미다. 스프링은 스프링 코어를 활용할 때 애플리케이션 코드를 거의 바꾸지 않아

도 된다는 점에서, 또 어느 순간 스프링을 더 이상 사용하지 않기로 했을 때도 아주 간단하게 스

프링을 걷어낼 수 있다는 점에서 경량 프레임워크다. 이 문장에서 스프링 코어만 언급한 것에 주

의하자. 그 외 많은 추가 스프링 컴포넌트, 이를테면 데이터 접근 컴포넌트 등은 스프링 프레임워

크와 밀접하게 결합된다. 하지만 이런 결합은 매우 분명하며 이 책에서는 애플리케이션에 대한

영향을 최소화하는 기법을 계속해서 소개한다.

제어 역전 또는 의존성 주입

스프링 프레임워크의 코어는 제어 역전(IoC) 원칙을 기반으로 한다. IoC는 컴포넌트 의존성의 생

성 및 관리를 외부화하는 기법이다. 예를 들어 클래스 Foo가 클래스 B의 인스턴스에 의존해 어

떤 작업을 수행한다고 가정하자. 전통적인 방법으로는 Foo가 new 연산자를 사용해 Bar 인스턴

스를 생성하거나 팩터리 클래스를 통해 인스턴스를 가져온다. 하지만 IoC 접근 방식을 사용하면

Bar(또는 하위 클래스) 인스턴스가 외부 프로세스를 통해 런타임 시점에 Foo에게 제공된다. 런

타임 시점에 의존성을 주입하는 이런 동작은 나중에 마틴 파울러가 의존성 주입(DI)이라는 훨

씬 더 서술적인 이름으로 고쳐 부른다. DI를 통해 관리되는 의존성의 성격은 4장에서 자세히 다

룬다.

알아두기

4장에서 보겠지만 제어 역전을 가리킬 때 의존성 주입이란 용어를 사용하는 게 항상 정확하지

는 않다. 하지만 스프링과 관련해서는 두 용어를 혼용해 사용하더라도 의미 차이가 없다.

Page 31: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 3

스프링의 DI 구현체는 두 가지 핵심 자바 개념을 근간으로 한다. 바로 자바빈과 인터페이스다.

스프링을 DI 제공자로 사용하면 애플리케이션 내에서 여러 가지 방식으로 의존성 설정을 유연하

게 정의할 수 있다(외부 XML 파일, 스프링 자바 설정 파일, 코드 내에서의 자바 애노테이션 등).

자바빈(또는 POJO)은 다양한 방식으로 설정할 수 있는 자바 리소스를 생성하는 표준 메커니즘

을 제공한다. 4장에서는 스프링이 자바빈 명세를 활용해 어떤 식으로 DI 설정 모델을 형성하는

지 살펴볼 것이다. 사실 스프링에서 관리하는 모든 리소스는 빈이라고 부른다. 자바빈에 익숙하

지 않은 독자라면 4장 초반에 나온 간단한 설명을 참고하자.

인터페이스와 DI는 서로 도움이 되는 기술이다. 이 책을 읽고 있는 독자라면 인터페이스를 사

용해 설계하고 코딩하면 유연한 애플리케이션을 만들 수 있다는 데 모두 동의할 것이다. 하지만

인터페이스를 통해 설계한 애플리케이션을 서로 연결하는 작업은 매우 복잡하며 개발자에게 추

가적인 코딩 부담을 안겨준다. DI를 사용하면 애플리케이션에서 인터페이스 기반의 설계를 사용

하는 데 필요한 코드 양을 거의 0으로 줄일 수 있다. 마찬가지로 인터페이스를 사용하면 빈이 아

무 인터페이스 구현체나 사용해 의존성을 충족할 수 있으므로 DI를 가장 잘 활용할 수 있다.

DI와 관련해 스프링은 프레임워크보다는 컨테이너 역할을 한다. 즉 애플리케이션의 클래스 인

스턴스에 필요한 의존성을 모두 제공하는 역할을 하는데, 스프링은 이 작업을 강압적이지 않은

방식으로 처리한다. DI에 스프링을 사용할 때는 클래스 내에서 자바빈 명명 관례(5장에서 보겠

지만 스프링의 메서드 주입 기능을 사용하면 따르지 않아도 된다)를 따르는 것 외에 아무것도 요

구하지 않는다. 즉 스프링 클래스를 상속하지 않아도 되고, 고유의 명명 스키마를 따르지 않아도

된다. 다만 DI를 사용하는 애플리케이션에서 수정할 부분이 있다면 런타임 시점에 더 많은 의존

성을 주입할 수 있게 자바빈에서 더 많은 프로퍼티를 노출하기만 하면 된다.

알아두기

스프링 프레임워크 버전 3.0(이상)에서는 XML 설정 파일 외에 자바 기반의 빈 메타 데이터도

지원한다.

의존성 주입의 발전 과정

지난 몇 년간 스프링 및 다른 DI 프레임워크의 인기 덕분에 DI는 자바 개발자 커뮤니티에서 폭넓

게 수용됐다. 동시에 개발자들은 애플리케이션을 개발할 때 DI를 사용하는 게 가장 좋은 방식이

라고 확신하게 됐고 DI를 사용할 때의 장점도 잘 이해할 수 있게 됐다.

Page 32: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4 l 프로 스프링 3

폭넓게 확산된 DI 적용 움직임은 썬 마이크로시스템즈(2009년 오라클에 인수)가 이끄는 자바

커뮤니티 프로세스(JCP)의 개발에도 영향을 줬다. 2009년 ‘자바를 위한 의존성 주입’은 공식 자

바 명세 요청 사항(JSR-330)이 됐고, 예상한 독자도 있겠지만 이 명세를 주도한 사람 중 한 명은

스프링 프레임워크의 창시자인 로드 존슨이었다.

자바 엔터프라이즈 에디션 버전 6(JEE 6)에서 JSR-330은 전체 기술 스택의 명세에 포함됐다. 그

와중에 엔터프라이즈 자바빈(EJB) 아키텍처도 (버전 3.0을 시작으로) 큰 변화를 맞이했다. EJB도

다양한 엔터프라이즈 자바빈 애플리케이션 개발을 쉽게 하기 위해 DI 모델을 도입한 것이다.

DI에 대한 자세한 설명은 4장부터 시작하지만 전통적인 접근 방식 대신 DI를 사용할 때의 장

점은 미리 살펴볼 만하다.

▒ 접착 코드를 줄임: DI의 가장 큰 장점 중 하나는 애플리케이션의 각기 다른 컴포넌트

를 서로 연결하려고 작성하는 코드의 양을 크게 줄일 수 있다는 점이다. 물론 이런 코드

는 대부분 간단하다(의존성 생성의 경우 새 인스턴스를 단순히 생성하는 정도). 하지만

JNDI 저장소에서 의존성을 찾거나 원격 리소스의 경우처럼 의존성을 직접 호출할 수 없

을 때는 접착 코드가 매우 복잡해질 수 있다. 이때 DI를 사용하면 JNDI 자동 조회 기능

과 원격 리소스의 자동 프록시 기능을 통해 접착 코드를 단순화할 수 있다.

▒ 애플리케이션 설정 단순화: DI를 도입하면 애플리케이션을 설정하는 과정이 매우 단순

해진다. 즉 애노테이션과 XML을 사용해 다른 클래스에 주입할 클래스를 설정할 수 있다.

또 같은 기법을 사용해 ‘주입기’가 올바른 빈 인스턴스나 속성을 주입할 수 있게 의존성 요

구조건을 표현할 수 있다. 더불어 DI는 한 의존성 구현체를 다른 구현체로 바꾸는 일도

훨씬 쉽게 할 수 있다. 예를 들어 PostgreSQL 데이터베이스를 사용해 데이터 작업을 수행

하는 데이터 접근 객체(DAO) 컴포넌트를 사용하다 오라클로 DB를 바꾼다고 가정하자.

DI를 사용하면 비즈니스 객체가 PostgreSQL 대신 오라클 구현체를 사용하도록 의존성

을 간단히 재설정할 수 있다.

▒ 단일 저장소를 통한 공통 의존성 관리: 전통적인 접근 방식으로 자주 사용하는 서비스

(이를테면 데이터 소스 연결, 트랜잭션, 원격 서비스 등)를 관리하려면 필요한 코드에서

의존성 인스턴스를 생성(또는 팩터리 클래스에서 검사)해야 한다. 이렇게 하면 의존성이

애플리케이션 내의 여러 클래스에 분포되므로 수정하기가 어렵다. 하지만 DI를 사용하면

공통 의존성에 대한 정보가 모두 단일 저장소(스프링을 사용할 때는 XML 파일이나 자

바 클래스 중에서 선택해 정보를 저장할 수 있다)에 저장되는 만큼 의존성을 관리하기가

훨씬 쉽고, 에러가 날 일도 그만큼 적다.

Page 33: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 5

▒ 손쉬운 테스트: 클래스를 DI에 맞게 설계하면 의존성을 쉽게 대체할 수 있다. 특히 애플

리케이션을 테스트할 때 편리하다. 예를 들어 복잡한 처리를 하는 비즈니스 객체가 있다

고 가정하자. 이 처리를 하면서 이 객체는 DAO를 사용해 관계형 데이터베이스에 저장된

데이터에 접근한다. DI를 사용하면 이 객체를 테스트할 때 DAO를 굳이 테스트하지 않아

도 된다. 대신 다양한 데이터셋을 사용해 비즈니스 객체만 테스트하면 된다. 전통적인 접

근 방식에서는 비즈니스 객체가 DAO 인스턴스 자체를 가져오는 책임까지 맡고 있으므로

이런 테스트를 하기가 쉽지 않다. DAO 구현체를 테스트 데이터셋을 반환하는 목(mock)

구현체로 손쉽게 바꿀 수 없기 때문이다. 따라서 전통적인 방법을 쓸 때는 테스트 데이

터베이스에 올바른 데이터를 집어넣고 테스트에 사용할 완전한 DAO 구현체를 사용해

야 한다. 하지만 DI를 사용하면 DAO 객체를 테스트 데이터셋을 반환하는 목 구현체로

손쉽게 바꾼 후, 이 객체를 비즈니스 객체를 테스트할 때 넘겨주면 된다. 이 메커니즘은

애플리케이션의 모든 티어를 테스트하는 데 활용할 수 있으며, HttpServletRequest와

HttpServletResponse의 목 구현체를 만들 수 있는 웹 컴포넌트를 테스트할 때 특히 유용

하다.

▒ 좋은 애플리케이션 설계를 촉진: DI에 맞게 설계한다는 말은 보통 인터페이스를 기준으

로 설계한다는 뜻이다. 의존성 주입을 지향하는 애플리케이션은 보통 주요 컴포넌트가

모두 인터페이스로 정의돼 있으며 이들 인터페이스의 구현체는 DI 컨테이너를 사용해 생

성하고 연결한다. 이런 설계는 DI나 스프링 같은 DI 기반 컨테이너가 나오기 전에도 자바

에서 할 수 있는 설계였다. 하지만 스프링을 사용하면서 수많은 DI 기능을 공짜로 얻게

됐고 이제 개발자는 DI를 지원하는 프레임워크가 아니라 애플리케이션 로직 자체를 개발

하는 데만 집중할 수 있게 됐다.

이 목록에서 볼 수 있듯이 DI는 애플리케이션 개발에 많은 장점이 있지만 단점이 없는 것은 아

니다. 특히 DI는 내부 코드에 익숙하지 않은 사람이 특정 의존성 구현체가 어떤 객체로 연결되는

지 이해하기 어렵게 만든다. 보통 이런 문제는 DI를 겪어보지 않은 개발자들에게서만 나타난다.

좋은 DI 코딩 방식 경험을 좀 더 쌓고 실제로 이런 코드를 작성하다 보면(예를 들어 각 애플리케

이션 계층에 주입할 클래스를 같은 패키지에 두는 등) 개발자들은 전체 그림을 쉽게 볼 수 있다.

대부분의 경우 DI의 장점이 이런 작은 단점을 상쇄하고도 남지만 애플리케이션을 설계할 때는

이런 단점도 고려해야 한다.

Page 34: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

6 l 프로 스프링 3

의존성 주입을 넘어

고급 DI 기능을 제공하는 스프링 코어는 그 자체만으로도 훌륭한 툴이지만 스프링이 정말 뛰어

난 이유는 수많은 부가 기능을 제공하면서, 이들 기능을 스프링의 DI 원칙을 사용해 우아하게

설계하고 개발했다는 점이다. 스프링은 데이터 접근 헬퍼 애플리케이션 프로그래밍 인터페이스

(API)부터 고급 모델 뷰 컨트롤러(MVC) 기능까지 애플리케이션의 모든 계층에 필요한 기능을

제공한다. 이들 기능을 스프링에서 쓸 때 좋은 점은 이들 기능을 스프링의 다른 툴과도 손쉽게

연동할 수 있다는 점이다(물론 종종 스프링만의 접근 방식을 제공하기도 하지만).

스프링을 활용한 관점 지향 프로그래밍

관점 지향 프로그래밍(AOP)은 자바 세계에서 ‘현재 아주 중요한 프로그래밍 모델’ 중 하나다.

AOP는 공통 로직(애플리케이션의 많은 영역에 적용되는 로직)을 한곳에서 구현하고 이 로직을

애플리케이션 전 영역에 자동으로 적용할 수 있게 해준다. AOP는 현재 가장 큰 주목을 받고 있

다. 보통 사람들이 이렇게 주목하는 곳에는 모든 자바 개발자가 익혀야 할 진정으로 유용한 기술

이 있기 마련이다.

스프링의 AOP 접근 방식은 타깃 객체에 대한 ‘동적 프록시’를 생성하고 설정된 어드바이스를

사용해 객체를 ‘위빙(weaving)’함으로써 공통 로직을 수행하는 것이다.

또 다른 인기 있는 AOP 라이브러리는 이클립스 AspectJ 프로젝트(www.eclipse.org/aspectj)다.

AspectJ 프로젝트는 객체 생성, 클래스 로딩, 더 강력한 공통 관심사 지원 등 더욱 강력한 기능을

제공한다.

스프링과 AOP 개발자에게 희소식은 버전 2.0부터 스프링이 AspectJ와 더욱 강력한 연동 기

능을 제공한다는 점이다. 이 중 핵심 연동 기능을 정리하면 다음과 같다.

▒ AspectJ 방식의 포인트컷 표현식 지원

▒ 스프링 AOP를 위빙에 사용하면서 동시에 @AspectJ 애노테이션 스타일을 지원

▒ AspectJ에 구현된 애스펙트를 통한 DI 지원

▒ 스프링 ApplicationContext 내에서 로드 시점 위빙 지원

두 AOP 모두 각자의 용도가 있고, 대부분의 경우 애플리케이션의 공통 관심사를 처리하는 데

는 스프링 AOP만으로도 충분하다. 하지만 좀 더 복잡한 요구 사항을 처리할 때는 AspectJ를 사

용할 수 있으며, 스프링 AOP와 AspectJ는 스프링 애플리케이션에서 혼용해 사용할 수 있다.

Page 35: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 7

AOP의 응용 범위는 다양하다. 많은 AOP 기본 예제에는 로깅을 수행하는 예제가 들어 있지

만, AOP는 단순히 애플리케이션 로깅 정보를 기록하는 것보다 더 폭넓게 활용할 수 있다. 실제

로 스프링 프레임워크 내에서도 AOP는 트랜잭션 관리 등 다양한 용도로 사용하고 있다. 스프링

AOP에 대한 자세한 설명은 6장과 7장에서 다룬다. 6장과 7장에서는 스프링 프레임워크와 애플

리케이션에서 AOP를 활용하는 법을 배우고, AOP의 성능, 및 전통적인 기법이 AOP보다 나은

분야를 살펴본다.

스프링 표현 언어 (SpEL)

표현 언어는 애플리케이션이 런타임 시점에 자바 객체를 활용할 수 있게 해주는 기술이다. 하지

만 표현 언어의 문제점은 각기 다른 기술마다 자체 표현 언어 구현체와 구문을 제공한다는 점이

다. 예를 들어 자바 서버 페이지(JSP)와 자바 서버 페이스(JSF)는 모두 자체 표현 언어를 갖고 있

으며 구문이 서로 다르다. 이 문제를 해결하기 위해 통합 표현 언어(EL)가 나왔다.

스프링 프레임워크는 빠르게 발전하고 있으므로 스프링 프레임워크 모듈과 다른 스프링 프레

임워크 사이에서 공유할 수 있는 표준 표현 언어가 필요했다. 이에 따라 버전 3.0부터 스프링은

스프링 표현 언어(SpEL)를 도입했다. SpEL은 런타임 시점에 표현식을 해석하고 자바 객체와 스

프링 빈에 접근하는 강력한 기능을 제공한다. 스프링 표현 언어의 결과는 애플리케이션에서 사

용하거나 다른 자바 빈에 주입할 수 있다.

이 책에서는 SpEL을 별도로 다루지 않는다. 대신 이 책 전반에서 적절히 SpEL을 사용하고 이

에 대해 상세한 설명을 첨부한다.

스프링의 유효성 검증

유효성 검증은 모든 애플리케이션에서 큰 주제다. 가장 이상적인 시나리오는 데이터 처리 요청이

프론트엔드에서 오든, 배치 작업에서 오든, 원격(웹 서비스, RESTful 웹 서비스, RPC 등)에서 오

든 상관없이 비즈니스 데이터를 담고 있는 자바빈 내의 프로퍼티에 대한 유효성 규칙을 일관되게

적용하는 것이다.

이런 필요에 따라 JCP는 빈 유효성 검증 API 명세(JSR-303)를 개발했다. 빈 유효성 검증 API

는 빈 유효성 검즘 규칙을 정의하는 표준 방식을 제공한다. 예를 들어 @NotNull 애노테이션을

빈의 프로퍼티에 추가하면 이 프로퍼티는 데이터베이스에 저장되기 전에 널 값을 포함하지 않

아야 한다.

Page 36: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

8 l 프로 스프링 3

버전 3.0부터 스프링은 JSR-303을 기본 지원한다. 이 API를 사용하려면 ValidatorFactoryBean

을 선언하고 Validator 인터페이스를 아무 스프링 관리 빈에나 주입하면 된다. 그러면 스프링이

내부 구현체를 모두 알아서 리졸브해준다. 기본적으로 스프링은 인기 있는 JSR-303 구현체인 하

이버네이트 밸리데이터(hibernate.org/subprojects/validator)를 찾는다. 스프링 MVC를 비롯한

많은 프론트엔드 기술(예를 들어 JSF 2, 구글 웹 툴킷)도 사용자 인터페이스에서 JSR-303 유효성

검증 기능을 지원한다. 이제 개발자가 같은 유효성 검증 로직을 사용자 인터페이스와 뒷단에 작

성해야 하는 시기는 지났다. 이와 관련한 상세 내용은 14장에서 다룬다.

스프링에서의 데이터 접근

데이터 접근과 영속성은 자바 세계에서 가장 많이 얘기하는 주제인 듯하다. 예를 들어 www.

theserverside.com 같은 커뮤니티 사이트를 방문하면 가장 최근에 나온, 가장 좋은 데이터 접근

툴에 대한 기사와 블로그 글로 게시판이 넘쳐나는 것을 항상 볼 수 있다. 스프링은 이런 데이터

접근 툴과의 훌륭한 연동 기능을 제공한다. 더불어 스프링은 표준 API를 감싼 단순한 API를 제

공함으로써 표준 JDBC를 활용해 많은 프로젝트를 수행할 수 있게 해준다.

버전 3.x에서 스프링의 데이터 접근 모듈은 JDBC, 하이버네이트, 마이바티스(과거 아이바티

스), 자바 데이터 객체(JDO) 자바 영속성 API(JPA)를 기본 지원한다.

하지만 지난 몇 년간 인터넷과 클라우드 컴퓨팅의 폭발적인 성장과 더불어 관계 데이터베이

스의 성장으로 많은 ‘특수 목적용’ 데이터베이스가 개발됐다. 일례로 키-값 쌍을 기반으로 방대

한 데이터 양을 처리하는 데이터베이스(보통 NoSQL이라고 부름), 그래프 데이터베이스, 도큐먼

트 데이터베이스 등이 새로 등장했다. 개발자들이 이런 데이터베이스를 지원할 수 있게 도우면

서 스프링의 데이터 접근 모듈을 더 복잡하게 만들지 않도록 스프링 데이터라는 별도 프로젝트

(www.springsource.org/spring-data)가 생겼다. 이 프로젝트는 특정 데이터베이스 접근 지원 기

능에 따라 다시 여러 카테고리로 나뉜다.

알아두기

이 책에서는 스프링의 비관계형 데이터베이스 지원 기능을 다루지 않는다. 이 주제에 관심이

있는 독자라면 앞서 언급한 스프링 데이터 프로젝트를 참고하자. 이 프로젝트 페이지에는 프

로젝트에서 지원하는 비관계형 데이터베이스와 이들 데이터베이스의 홈페이지 링크가 나와

있다.

Page 37: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 9

스프링의 JDBC 지원 기능 덕분에 JDBC를 사용해 복잡한 애플리케이션을 개발하는 것도 훨

씬 쉬워졌다. 하이버네이트, 마이바티스, JDO, JPA에 대한 지원 기능은 그렇지 않아도 단순한

API를 더 단순하게 만들었고 개발자의 부담을 덜어줬다. 스프링 API를 사용해 데이터를 접근하

면 어떤 툴을 쓰든 스프링의 훌륭한 트랜잭션 지원 기능을 활용할 수 있다. 이에 대한 자세한 내

용은 13장에서 살펴본다.

스프링의 멋진 기능 중 하나는 애플리케이션 내에서 데이터 접근 기술을 원하는 대로 조합할

수 있다는 점이다. 예를 들어 오라클을 사용하면서 대부분의 데이터 접근 로직에 하이버네이트

를 사용할 수 있다. 그러다 오라클의 특정 기능을 사용하고 싶을 때는 스프링의 JDBC API를 사

용해 데이터 접근 단에서 해당 부분을 간단히 구현할 수 있다.

스프링에서의 객체/XML 매핑(OXM)

대부분의 애플리케이션은 다른 애플리케이션과 연동하거나 다른 애플리케이션에 서비스를 제

공한다. 흔한 요구사항 중 하나는 다른 시스템과 (정기적으로든 실시간으로든) 데이터를 교환하

는 것이다. 데이터 형식 측면에서 XML은 가장 많이 사용하는 데이터 형식이다. 그 결과 자바빈

을 XML 형식으로 변형하거나 XML을 자바빈으로 변형하려는 공동의 요구가 대두됐다.

스프링은 각종 자바-XML 매핑 프레임워크를 지원하며, 항상 그렇듯 특정 구현체와 직접 연결

해야 하는 필요성을 제거해준다. 스프링은 마샬링(자바빈을 XML으로 변형)과 언마샬링(XML

을 자바 객체로 변형)을 위한 공통 인터페이스를 제공하고 임의의 스프링 빈에 DI로 주입할 수

있게 해준다. 스프링은 XML 바인딩을 위한 자바 API(JAXB), 캐스터, XStream, XMLBeans 같

은 자주 쓰는 라이브러리를 모두 지원한다. 16장에서 XML 형식을 사용해 원격으로 스프링 애

플리케이션에 접근해 비즈니스 데이터를 가져오는 법을 설명할 때 스프링의 객체-XML 매핑

(OXM) 지원 기능을 애플리케이션에 활용하는 법을 살펴보겠다.

트랜잭션 관리

스프링은 트랜잭션 관리를 할 수 있는 훌륭한 추상 레이어를 제공함으로써 개발자가 프로그래

밍적으로 또는 선언적으로 트랜잭션을 제어할 수 있게 해준다. 스프링의 트랜잭션 관리용 추상

레이어를 사용하면 내부 트랜잭션 프로토콜과 리소스 관리자를 쉽게 바꿀 수 있다. 또 코드를 전

혀 바꾸지 않고 간단한, 로컬, 특정 리소스 관련 트랜잭션부터 시작해 전역, 멀티리소스 트랜잭션

관리까지 적용할 수 있다. 트랜잭션은 13장에서 자세히 다룬다.

Page 38: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

10 l 프로 스프링 3

JEE 단순화 및 연동

앞에서 말한 것처럼 지난 몇 년간 스프링 같은 DI 프레임워크가 폭넓게 채택됐고 많은 개발자들

이 JEE의 EJB 접근 방식을 사용하면서 DI 프레임워크를 활용해 애플리케이션을 구성하는 방식

을 택했다. 그 결과 JCP 커뮤니티도 EJB의 복잡성을 인식해 EJB 명세의 버전 3.0과 3.1에서 API

를 단순화했으며, 이제는 DI의 많은 개념을 수용하고 있다.

하지만 EJB를 사용해 개발한 애플리케이션이나 JEE 컨테이너에서 스프링 기반의 애플리케이

션을 배포하고 애플리케이션 서버의 엔터프라이즈 서비스(예를 들어 JTA 트랜잭션 관리자, 데이

터 소스 커넥션 풀, JMS 커넥션 팩터리 등)를 사용해야 하는 애플리케이션의 경우 스프링은 이런

기술을 손쉽게 쓸 수 있는 지원 기능을 제공한다. 스프링은 EJB를 위해 JNDI 룩업을 수행하고

스프링 빈으로 주입할 수 있는 단순 선언 구문을 제공한다. 또 반대로 스프링 빈을 EJB에 주입할

수 있는 간단한 애노테이션도 제공한다.

JNDI 접근 가능한 위치에 저장된 모든 리소스에 대해 스프링은 복잡한 룩업 코드 없이 JNDI

관리 리소스를 런타임 시점에 다른 객체에 의존성으로 주입할 수 있게 해준다. 이로 인해 애플리

케이션은 JNDI와 결합하지 않아도 되며, 향후에 코드를 재사용할 수 있는 범위가 그만큼 늘어

난다.

웹 티어의 MVC

물론 스프링은 데스크톱부터 웹까지 거의 모든 설정에 사용할 수 있지만 특별히 스프링은 웹 기

반 애플리케이션 개발을 지원하는 풍부한 클래스군을 제공한다. 스프링을 사용하면 웹 프론트

엔드를 구현할 때 최고의 유연성을 발휘할 수 있다.

웹 애플리케이션을 개발할 때는 MVC 패턴을 가장 많이 사용한다. 최근 버전을 통해 스프링

은 간단한 웹 프레임워크에서 완전한 MVC 구현체로 점차 발전했다.

먼저 스프링 MVC의 뷰 지원 기능은 매우 방대하다. 스프링 태그 라이브러리의 강력한 지원을

받는 JSP 표준 지원 기능뿐 아니라 아파치 벨로시티, 프리마커, 아파치 타일즈, XSLT와의 연동을

완벽히 지원한다. 더불어 애플리케이션에서 엑셀과 PDF 결과를 쉽게 출력할 수 있는 기본 뷰 클

래스군도 볼 수 있다.

대부분은 스프링 MVC만으로 웹 애플리케이션을 개발하는 데 충분하다. 하지만 스프링은 스

트러츠, JSF, 구글 웹 툴킷(GWT) 같은 다른 인기 있는 웹 프레임워크와의 연동도 지원한다.

지난 몇 년간 웹 프레임워크 기술은 빠르게 발전했다. 사용자들은 더 즉각적으로 반응하고 인

Page 39: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 11

터랙티브한 사용자 경험을 원했고, 이로 인해 리치 인터넷 애플리케이션(RIA)을 개발하는 데

Ajax가 폭넓게 활용됐다. 다른 한편으로 사용자들은 스마트폰과 태블릿 같은 다른 기기에서도

애플리케이션에 접근하고 싶어 했다. 그 결과 HTML5, 자바스크립트, CSS3를 웹 프레임워크에

서 지원해야 할 필요성이 대두됐다. 17장에서는 스프링 MVC와 제이쿼리(Ajax 및 다른 많은 기

능을 지원하는 인기 자바스크립트 라이브러리) 및 JSP를 사용해 웹 애플리케이션을 개발하는

법을 살펴본다.

웹 애플리케이션 개발을 말할 때 빼놓지 말아야 할 또 다른 스프링 프로젝트는 바로 스프링 웹

플로우 프로젝트(www.springframework.org/web�ow)다. 이 프로젝트는 페이지 흐름을 제어하

고 여러 페이지에 걸쳐 유지해야 하는 웹 애플리케이션 기반의 데이터(대화형 스코프라고 부름)

를 상세하게 제어하기 위해 개발됐다. 18장에서는 스프링 웹 플로우 프로젝트를 살펴보고 인기

있는 JSF 프레임워크인 프라임페이스(www.primefaces.org)와 스프링 웹 플로우를 연동하는 법

을 배운다.

리모팅 지원

자바에서 원격 컴포넌트에 접근하거나 원격 컴포넌트를 노출하는 일을 손쉽게 할 수 있는 경우

는 한 번도 없었다. 하지만 스프링의 다양한 리모팅 지원 기능을 활용하면 원격 서비스에 손쉽게

접근하고 원격 서비스를 노출할 수 있다.

스프링은 자바 RMI, JAXRPC, Caucho Hessian, Caucho Burlap 등 다양한 리모트 접근 메

커니즘을 지원한다. 이들 리모팅 프로토콜 외에 스프링은 표준 자바 직렬화를 기반으로 자체

HTTP 기반 호출자를 제공한다. 스프링의 동적 프로시 기능을 적용하면 원격 리소스에 대한 프

록시를 클래스의 의존성에 주입할 수 있으므로 애플리케이션을 특정 리모팅 구현체와 결합하지

않아도 되며 애플리케이션을 작성하는 코드의 양도 그만큼 줄일 수 있다.

요즘 들어 폭넓게 사용되는 또 하나의 리모트 기술은 바로 RESTful 웹 서비스(RESTful-WS)

다. RESTful-WS는 HTTP를 중심으로 설계됐으며 서비스를 호출하고 결과를 얻는 메커니즘을

크게 단순화한다. 예를 들어 클라이언트가 http://somedomain.com/someapp/customer/123이

라는 URL로 HTTP GET 요청을 보내면 클라이언트가 ID(또는 고객 번호)가 123인 고객 정보를

요청한다는 의미다. 결과 값은 XML, 자바스크립트 객체 표기법(JSON), 또는 HTTP 요청 헤더

에 기술된 클라이언트 지원 형식이 될 수 있다. 3.0부터 스프링 MVC 모듈은 RESTful-WS를 폭

넓게 지원한다. 리모트 지원에 대해서는 16장에서 자세히 다룬다.

Page 40: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

12 l 프로 스프링 3

알아두기

JCP는 RESTful-WS 표준을 RESTful 웹 서비스를 위한 자바 API인 JAX-RS로 공식화했다

(JSR 311).

메일 지원

이메일을 보내는 일은 많은 애플리케이션에서 일반적으로 요구하는 기능이며, 스프링 프레임워

크가 가장 잘 처리해주는 기능 중 하나다. 스프링은 스프링 DI 기능과 잘 연동해 이메일 메시지

를 보낼 수 있는 간단한 API를 제공한다. 또 스프링은 표준 자바 메일 API를 지원한다.

스프링은 DI 컨테이너에서 프로토타입 메시지를 생성하고 이를 애플리케이션에서 보내는 모

든 메시지의 기반으로 사용할 수 있게 해준다. 이를 통해 메일 제목, 발신자 주소 같은 메일 매개

변수를 쉽게 커스터마이징할 수 있다. 더불어 메시지 본문을 커스터마이징할 때 스프링은 아파

치 벨로시티 같은 템플릿 엔진과 연동해 메일 내용을 자바 코드 외부에 둘 수 있게 해준다.

작업 스케줄링 지원

대부분의 업무용 애플리케이션은 어느 정도의 스케줄링 기능을 필요로 한다. 고객에게 최신 정

보를 보내는 것이든, 정리 작업을 수행하는 것이든 코드가 미리 정한 시점에 실행되게끔 예약하

는 기능은 개발자에게는 없어서는 안 될 기능이다.

스프링은 대부분의 일반적인 시나리오를 다룰 수 있을 만큼의 자체적인 스케줄링 지원 기능

을 제공한다. 작업은 고정된 주기로 예약하거나 유닉스 크론 표현식을 사용해 예약할 수 있다.

다른 한편으로 스프링은 다른 스케줄링 라이브러리와 연동해 작업 실행과 스케줄링을 할 수도

있다. 예를 들어 애플리케이션 서버 환경에서 스프링은 작업 실행을 많은 애플리케이션 서버에서

사용하는 CommonJ 라이브러리로 위임할 수 있다. 작업 스케줄링의 경우 스프링은 오픈소스 스

케줄링 라이브러리로 자주 사용하는 JDK Timer API와 쿼츠 같은 라이브러리를 지원한다.

스프링에서의 스케줄링 지원 기능은 15장에서 자세히 살펴본다.

동적 스크립팅 지원

JDK 6을 시작으로 자바는 동적 언어 지원 기능을 도입했고, 이를 통해 JVM 환경에서 다른 언어

로 작성한 스크립트를 실행할 수 있게 됐다. 이런 동적 언어 스크립트에는 그루비, 제이루비, 자바

Page 41: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 13

스크립트 등이 포함된다.

스프링도 스프링 애플리케이션에서 동적 스크립트 실행을 지원하며, 개발자는 동적 스크립팅

언어로 작성한 스프링 빈을 정의하고 다른 자바 빈에 주입할 수도 있다. 스프링이 지원하는 동적

스크립팅 언어에는 그루비, 제이루비, 빈셸이 있다. 22장에서는 스프링의 동적 스크립팅 언어 지

원을 자세히 들여다본다.

단순한 예외 처리

스프링을 활용해 반복되는 코드를 크게 줄일 수 있는 곳 중 하나는 에러 처리다. 예외 처리와 관

련해 스프링 철학의 핵심은 검사형 예외가 자바에서 남용되고 있으며 프레임워크에서는 개발자

가 복구할 수 없는 예외를 처리하도록 강요하지 말아야 한다는 것이다(이 점은 우리 모두 진심으

로 동의하는 바이다).

실제로 많은 프레임워크가 검사형 예외를 처리하는 코드를 작성해야 하는 부담을 줄이도록 설

계됐다. 하지만 이러한 프레임워크는 대부분 검사형 예외를 여전히 고수하고 인위적으로 예외 클

래스의 계층구조를 제한하는 방식을 쓰고 있다. 스프링에서는 비검사형 예외를 개발자가 쓸 수

있게 되면서 예외 계층구조를 매우 상세하게 지정할 수 있게 됐다.

이 책 전반에서는 스프링 예외 처리 메커니즘을 활용해 작성해야 하는 코드를 줄이는 동시에 애

플리케이션 내에서 에러를 식별, 분류, 진단하는 능력이 개선되는 예를 여러 차례 보게 될 것이다.

스프링 프로젝트

스프링 프로젝트의 가장 좋은 점 중 하나는 현재 커뮤니티에 참여하는 참여자의 활동 수준과

CGLIB, 아파치 제로니모, AspectJ 같은 다른 프로젝트와의 교류의 양이다. 오픈소스의 장점으

로 가장 많이 얘기하는 게 프로젝트가 내일 중단되더라도 코드는 남는다는 것이다. 하지만 좀 더

솔직하게 얘기하자면 스프링 정도의 코드 기반을 개인이 코드만 가지고 계속 지원하고 개선하기

란 어렵다. 이런 이유로 스프링 커뮤니티가 얼마나 안정화돼 있고 얼마나 활성화돼 있는지 알게

된다면 조금 안심이 될 것이다.

스프링의 기원

스프링의 기원은 로드 존슨이 쓴 Expert One-to-One J2EE Design and Development(Wrox,

2002)라는 책으로 거슬러 올라간다. 이 책에서 로드 존슨은 인터페이스 21(Interface 21) 프레임

Page 42: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

14 l 프로 스프링 3

워크라는 개인 프레임워크를 제시했다. 이 프레임워크는 그가 개인 애플리케이션에서 사용하려

고 개발한 프레임워크다. 오픈소스 세계에 공개된 후 이 프레임워크는 오늘날 우리가 알고 있는

스프링 프레임워크의 근간이 된다.

스프링은 빠르게 초기 베타와 RC 단계를 거쳐 2004년 3월 24일 첫 번째 공식 버전 1.0을 내놓

았다. 그 후 스프링은 엄청난 성장을 거듭했고, 이 책을 쓰고 있는 현 시점 기준으로 스프링 프레

임워크의 최신 버전은 3.1이다.

스프링 커뮤니티

스프링 커뮤니티는 우리가 본 오픈소스 프로젝트 중 최고의 프로젝트 중 하나다. 메일링 목록과

포럼에는 항상 내용이 가득 차 있으며 새로운 기능의 개발도 빠르게 이뤄진다. 개발 팀은 스프링

을 모든 자바 애플리케이션 프레임워크 중 가장 성공적인 프레임워크로 만들기 위해 진정으로

헌신하고 있으며, 이는 코드의 품질을 통해서도 드러난다. 현재 진행 중인 스프링 개발 중 상당

부분은 기존 코드를 더 빠르게, 더 작게, 더 깔끔하게, 또는 셋 모두를 하기 위한 재작업이다.

앞서 언급한 것처럼 스프링은 다른 오픈소스와도 잘 연동된다. 이는 전체 스프링 배포판에 포

함된 의존성의 양을 고려할 때 매우 큰 장점이다.

사용자 관점에서 볼 때 아마도 스프링의 가장 좋은 점은 훌륭한 문서 및 배포판과 함께 나오

는 테스트 스위트일 것이다. 스프링은 거의 모든 기능에 문서를 제공해 새 사용자도 스프링 프레

임워크를 쉽게 사용할 수 있게 했다. 또 스프링이 제공하는 테스트 스위트는 매우 방대하다(개발

팀이 모든 테스트를 작성한다). 개발 팀이 버그를 찾아내면 개발 팀은 먼저 버그를 가리키는 테

스트를 작성한 후 이 테스트가 통과되게끔 한다.

이런 사실은 어떤 의미를 지니고 있을까? 간단히 말해 스프링 프레임워크의 품질에 대해 그만

큼 확신을 가져도 되고 앞으로도 스프링 개발 팀은 이미 훌륭한 프레임워크를 한층 더 발전시키

기 위한 노력을 계속할 것이라는 의미다.

스프링 .NET

메인 스프링 프레임워크 프로젝트는 100퍼센트 자바 기반이다. 하지만 자바 버전이 성공하면

서 .NET 세계의 개발자들은 조금 소외되는 감을 느끼기 시작했다. 이에 마크 폴랙과 로드 존슨

은 스프링 .NET(Spring .NET) 프로젝트를 시작했다. 로드 존슨을 제외하면 두 프로젝트는 개

발 팀이 완전히 다르다. 따라서 .NET 프로젝트는 스프링 자바에는 거의 영향을 미치지 않는다.

Page 43: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 15

실제로 우리는 이 소식이 매우 좋은 소식이라고 생각한다. 자바 세계에서 흔히 믿는 것과 달리

.NET은 쓰레기가 아니며 우리 클라이언트에게 .NET 애플리케이션이 성공적으로 개발된 사례

도 있다. 이 프로젝트는 교류의 새 장을 열어줬다. 특히 .NET은 이미 소스 레벨 메타데이터 같은

특정 영역에서 앞서가고 있는 만큼 양 진영 모두에서 스프링을 더 좋게 만드는 데 기여할 수 있다.

이 프로젝트의 또 다른 부수 효과로는 이제 양 진영에서 모두 스프링을 사용함에 따라 개발자들

의 플랫폼 이전이 훨씬 쉬워졌다는 점이다. 이 사실은 하이버네이트와 마이바티스 같은 다른 프

로젝트도 이제 .NET 버전을 지원한다는 점을 고려하면 훨씬 더 그 중요성을 실감할 수 있다. 스

프링 .NET에 대한 자세한 정보는 www.springframework.net에서 볼 수 있다.

스프링소스 툴 스위트/스프링 IDE

이클립스(자바 애플리케이션 개발에 가장 많이 사용하는 IDE)에서 스프링 기반의 애플리케이

션 개발을 쉽게 할 수 있게 스프링은 스프링 IDE 프로젝트를 만들었다. 그로부터 얼마 지나지

않아 로드 존슨이 설립한 스프링 지원 회사인 스프링소스에서는 스프링소스 툴 스위트(STS)라

는 통합 툴을 개발했다. 이 툴은 한때 유료 제품이었지만 지금은 무료로 사용할 수 있다. 이 툴은

이클립스 IDE, 스프링 IDE, Mylyn(이클립스에서의 작업 기반 개발 환경), 이클립스용 메이븐,

AspectJ 개발 툴, 다른 여러 유용한 이클립스 플러그인을 한 패키지로 통합했다. 이 툴은 새 버전

이 나올 때마다 그루비 스크립팅 언어 지원, 스프링 루 지원, 스프링소스 tcServer(톰캣 서버 기

반 위에 개발된 애플리케이션 서버로, 스프링소스에서 유료로 지원하는 애플리케이션 서버) 지

원 등 새로운 기능이 더 많이 추가되고 있다. 이 책의 장별 소스코드와 예제 애플리케이션의 코

드는 STS에서 개발하므로 독자들은 STS를 내려받고 프로젝트를 불러와야 한다(STS는 부록 A

에서 자세히 설명한다). STS를 사용해 애플리케이션 개발을 바로 시작하는 법이 궁금하다면 부

록 A를 참고하자.

스프링 시큐리티 프로젝트

스프링 시큐리티 프로젝트(http://static.springsource.org/spring-security/site/index.html)는

과거 스프링을 위한 아시지 시큐리티 시스템이라고 불렀으며, 스프링 포트폴리오에 들어 있는 또

하나의 주요 프로젝트다. 스프링 시큐리티는 웹 애플리케이션 보안과 메서드 레벨 보안 모두를

강력하게 지원한다. 스프링 시큐리티는 스프링 프레임워크와 밀접하게 연동하며 HTTP 기본 인

증, 폼 기반 로그인, X.509 인증서, SSO 제품(예를 들어 사이트마인더) 등 자주 사용하는 인증 메

커니즘을 모두 지원한다. 스프링 시큐리티는 애플리케이션 리소스에 대한 역할 기반 접근 제어

Page 44: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

16 l 프로 스프링 3

기능을 제공하고 애플리케이션에서 좀 더 복잡한 보안 요구사항(예를 들어 데이터 분리), 접근

제어 목록(ACL)을 지원한다. 하지만 스프링 시큐리티는 주로 웹 애플리케이션 보안에만 사용하

며, 이 내용은 17장에서 자세히 다룬다.

스프링 배치 및 스프링 인티그레이션

말할 필요도 없이 배치 작업 실행과 시스템 연동은 애플리케이션에서 자주 필요한 기능이다. 개

발자들이 이런 작업을 쉽게 할 수 있게끔 스프링은 스프링 배치와 스프링 인티그레이션 프로젝

트를 개발했다. 스프링 배치는 배치 작업 구현을 위한 공통 프레임워크와 다양한 정책을 제공함

으로써 많은 반복 코드를 줄여준다. 또 스프링 인티그레이션은 엔티프라이즈 연동 패턴(EIP)을

구현함으로써 스프링 애플리케이션을 외부 시스템과 쉽게 연동할 수 있게 해준다. 이 주제는 20

장에서 자세히 살펴본다.

그 외 많은 프로젝트

지금까지 스프링의 코어 모듈과 스프링 포트폴리오의 주요 프로젝트를 다뤘지만 이들 프로젝트

외에 커뮤니티의 요구에 따라 각기 다른 요구사항을 해결하기 위한 많은 프로젝트가 더 나와 있

다. 이런 프로젝트에 해당하는 예로는 플렉스 연동을 위한 스프링 BlazeDS, 스프링 모바일, 스프

링 다이나믹 모듈, 스프링 소셜, 스프링 AMQP 등이 있다. 이들 프로젝트는 이 책에서 다루지 않

는다. 더 자세한 사항은 스프링소스 웹사이트(www.springsource.org/projects)를 참고하자.

스프링의 대안

앞에서 얘기한 오픈소스 프로젝트의 수를 감안하면 의존성 주입 기능을 제공하고 애플리케이

션 처음부터 끝까지 개발할 수 있는 솔루션을 제공하는 프로젝트가 스프링 프레임워크만 있는

게 아니라는 사실도 새삼스럽지 않게 느껴질 것이다. 사실 세상에는 언급할 만한 프로젝트가 수

없이 많다. 여기서는 열린 마음으로 이러한 프레임워크 중 몇 가지를 간단히 설명하지만, 우리는

이들 플랫폼 중 어떤 것도 스프링만큼 방대한 해결책을 제시하지는 못한다고 믿고 있다.

JBoss Seam 프레임워크

개빈 킹(하이버네이트 ORM 라이브러리의 창시자인)이 설립한 Seam 프레임워크(www.

seamframework.org)는 또 하나의 DI 기반 프레임워크다. 이 프레임워크는 웹 애플리케이션 프

Page 45: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

1장 스프링 소개 l 17

론트엔드(JSF), 비즈니스 로직 레이어(EJB 3), 영속성을 위한 JPA 레이어를 포함하고 있다. 여기

서 볼 수 있듯이 Seam과 스프링의 주된 차이점은 Seam 프레임워크는 전적으로 JEE 표준 위에

서 개발됐다는 점이다. JBoss도 Seam 프레임워크의 개념을 JCP에 반영해 JSR-299(‘자바 EE 플

랫폼을 위한 컨텍스트 및 의존성 주입’)로 발전한다.

구글 주스

또 다른 인기 있는 DI 프레임워크는 구글 주스(http://code.google.com/p/google-guice)다. 검색

엔진 업체인 구글이 주도하는 주스는 애플리케이션 설정 관리에 DI를 제공하는 데 초점을 맞춘

경량 프레임워크다. 이 프레임워크는 JSR-330 명세(‘자바를 위한 의존성 주입’)의 레퍼런스 구현

체이기도 하다.

PicoContainer

PicoContainer(www.picocontainer.org)는 매우 작은(약 300KB) DI 컨테이너로, PicoContainer

이외에 다른 의존성을 도입하지 않고도 애플리케이션에서 DI를 쓸 수 있게 해준다. Pico

Container는 DI 컨테이너 역할만 하므로 애플리케이션의 크기가 커지면 스프링 같은 다른 프

레임워크를 도입해야 할 필요성을 느끼기 쉽다(이럴 바엔 처음부터 스프링을 사용하는 게 더 낫

다). 물론 가벼운 DI 컨테이너만 필요하다면 PicoContainer도 좋은 선택이 될 수 있지만, 스프링

은 DI 컨테이너를 나머지 프레임워크와는 다른 패키지에 보관하므로 스프링도 PicoContainer

만큼이나 쉽게 사용할 수 있으며, 더불어 미래 유연성도 확보할 수 있다.

JEE 6 컨테이너

앞에서 설명한 것처럼 DI의 개념이 폭넓게 받아들여지면서 JCP에서도 이를 깨닫게 됐다. 그 결

과 JEE 6에서 기술 스택(EJB 3.1, JPA 2.1, JSR-299, JSR-330 등)에 DI를 도입하고 JEE 애플리케이

션 개발을 단순화하기 위한 큰 변화가 있었다. 따라서 JEE 6 호환 애플리케이션 서버용 애플리케

이션을 개발할 때는 모든 레이어에서 표준 DI 기법을 사용할 수 있게 됐다. 이 책을 쓰고 있는 현

시점 기준으로 인기 있는 JEE 6 호환 애플리케이션 서버에는 JBoss AS 7, 오라클 글래스피시 3.1,

웹스피어 8 등이 있다.

Page 46: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

18 l 프로 스프링 3

정리

이 장에서는 주요 기능을 통해 스프링을 개략적으로 살펴보고 이들 기능을 자세히 다루는 관련

장을 소개했다. 이 장을 읽고 난 후 독자들은 스프링이 무엇을 해줄 수 있는지 어렴풋이 감이 잡

힐 것이다. 이제 남은 일은 이를 어떻게 활용할지 배우는 것뿐이다. 그럼 이제 그 방법을 차례차

례 알아보자.

다음 장에서는 기본 스프링 애플리케이션을 설정하고 실행하는 데 필요한 정보를 모두 다룬

다. 그 과정에서 스프링 프레임워크를 내려받는 법을 보여주고, 패키지 옵션, 테스트 스위트, 문서

에 대해 설명한다. 또 2장에서는 의존성 주입 기반으로 ‘Hello World!’ 예제를 작성하는 법을 비

롯해 기본적인 스프링 코드 작성법을 소개한다.

Page 47: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

02스프링 시작하기

새로운 개발 도구를 익히는 게 가장 어려운 때는 어디서부터 시작할지 모를 때다. 보통 이 문제는

스프링처럼 툴에서 다양한 선택 사항을 제공할 때 더 어려워진다. 다행히 어디서부터 봐야 할지

만 알면 스프링을 시작하는 일은 그리 어렵지 않다. 이 장에서는 처음 스프링을 사용할 때 필요

한 기본 지식을 모두 살펴본다. 좀 더 구체적으로 다음과 같은 사항들을 살펴본다.

▒ 스프링 내려받기: 논리적으로 처음 시작하려면 스프링 JAR 파일을 내려받고 빌드하는 것

부터 시작해야 한다. 표준 스프링 배포판을 사용해 빠르게 개발을 시작하고 싶다면 스프

링 웹사이트인 www.springframework.org에서 최신 스프링 배포판을 그냥 내려받으면

된다. 하지만 최신 스프링 개발 기능을 모두 활용하고 싶다면 스프링의 깃허브 저장소에

서 최신 소스코드를 체크아웃하자. 애플리케이션 개발에 메이븐을 사용 중이라면 스프

링용 의존성을 프로젝트의 pom.xml(프로젝트 객체 모델 파일)에 추가하면 된다. 그럼 메

이븐이 필요한 JAR 파일을 모두 내려받는다. 이와 관련한 자세한 사항은 ‘메이븐 저장소

의 스프링 모듈’ 절을 참고하자.

▒ 스프링 패키징 옵션: 스프링 패키징은 모듈화돼 있다. 이로 인해 애플리케이션에서 필요

한 컴포넌트만 선택할 수 있고 배포하는 애플리케이션에도 필요한 컴포넌트만 포함시킬

수 있다.

▒ 스프링 의존성: 스프링 전체 배포판에는 방대한 의존성이 포함되지만 많은 경우 실제로

필요한 의존성은 일부에 지나지 않는다. 이 절에서는 어떤 스프링 기능이 어떤 의존성을

필요로 하는지 살펴본다. 이 정보는 애플리케이션의 크기를 최소한으로 줄이는 데 도움

이 된다.

Page 48: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

20 l 프로 스프링 3

▒ 스프링 예제: 스프링은 애플리케이션을 개발할 때 참조해 사용할 수 있는 예제 애플리케

이션을 폭넓게 제공한다. 이 절에서는 예제 애플리케이션의 코드를 들여다보면서 제공되

는 예제 코드에 대해 전반적으로 감을 잡는다. 이 책을 진행하는 동안 개발하는 예제 애

플리케이션과 스프링의 예제 애플리케이션을 익히면 자신만의 애플리케이션을 개발할

수 있는 코드 기반도 충분히 마련할 수 있을 것이다.

▒ 테스트 스위트와 문서: 스프링 커뮤니티의 구성원들이 가장 자랑스러워 하는 것 중 하나

는 방대한 테스트 스위트와 문서다. 테스트는 스프링 팀이 하는 일 중 큰 영역을 차지한

다. 스프링 팀은 클로버(www.atlassian.com/software/clover)를 사용해 테스트를 마친

비율을 항상 모니터링하며 이 비율을 계속해서 높이려고 노력하고 있다. 더불어 표준 배

포판과 함께 제공되는 문서 또한 매우 훌륭하다.

▒ 스프링으로 ‘Hello World!’ 만들기: 우리는 새 프로그래밍 툴을 시작하는 가장 좋은 방법

은 직접 코드를 작성하는 것이라고 생각한다. 여기서는 완전히 DI를 기반으로 누구나 좋

아하는 ‘Hello World!’ 예제를 만드는 것을 비롯해 몇 가지 예제를 살펴본다. 여기서 사용

하는 코드 예제를 바로 이해하지 못하더라도 걱정하지 않아도 된다. 자세한 설명은 이 책

에서 계속해서 보게 될 것이다.

이미 스프링 프레임워크의 기본 지식에 익숙하다면 바로 3장으로 넘어가 이 책에서 계속해서

개발할 예제 애플리케이션에 대한 설명을 봐도 된다. 하지만 스프링의 기본 지식에 익숙하더라도

특히 패키징이나 의존성에 대한 설명 등 이 장에서도 유익한 내용을 몇 가지 찾아볼 수 있을 것

이다.

스프링 프레임워크 내려받기

스프링 코딩을 시작하기 전에 먼저 스프링 코드를 받아야 한다. 스프링 코드를 내려받는 방법은

몇 가지가 있다. 스프링 웹사이트에서 패키지 배포판을 내려받을 수도 있고 스프링 깃허브 저장

소에서 코드를 체크아웃할 수도 있다. 또 다른 옵션으로 메이븐(Maven)이나 아이비(Ivy) 같은

애플리케이션 의존성 관리 툴을 사용해 설정 파일에 의존성을 선언하고 툴에서 필요한 라이브

러리를 가져오게 하는 방법도 있다.

Page 49: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 21

표준 배포판 내려받기

스프링은 www.springsource.org/download에 있는 스프링소스 다운로드 센터에서 개발 코드

를 호스팅한다. 이 페이지를 방문해 가장 최신 스프링 배포판(이 책을 쓰고 있는 현 시점 기준으

로 버전 3.1)을 내려받는다. 또 다운로드 센터에서는 과거 버전이나 미래에 출시될 배포판의 마일

스톤/나이트 스냅샷을 내려받을 수도 있다.

3.0 배포판부터 스프링 프레임워크는 두 가지 버전으로 배포된다. 바로 문서가 포함된 버전과

문서가 포함되지 않은 버전이다. 3.0 이전 스프링은 서드파티 라이브러리(commons-logging,

hibernate 등)를 모두 포함하는 또 다른 패키지를 제공했다. 하지만 이제 스프링은 메이븐이나

아이비 같은 의존성 관리 툴을 사용해 각 모듈에 대한 서드파티 라이브러리 의존성을 표현한다.

따라서 스프링 모듈(예를 들어 spring-context)에 의존하게끔 프로젝트를 선언하면 필요한 의존

성이 자동으로 포함된다. 이와 관련해서는 이 장에서 나중에 좀 더 자세히 설명한다.

깃허브를 통한 스프링 체크아웃

스프링 개발 팀에서 스냅샷에 반영하기 전에 새 기능을 써보고 싶다면 스프링소스의 깃허브 저

장소에서 소스코드를 직접 체크아웃할 수 있다.

최신 버전의 스프링 소스를 체크아웃하려면 먼저 http://git-scm.com/에서 깃허브를 내려받아

설치한 후 깃 배시 툴을 열고 다음 명령을 실행해야 한다.

git clone git://github.com/SpringSource/spring-framework.git

스프링 패키징의 이해

패키지를 모두 내려받고 압축을 풀면 dist 폴더 아래에서 각 스프링 모듈을 나타내는 JAR 파일

목록을 볼 수 있다. 각 모듈이 하는 일을 이해하고 나면 프로젝트에 필요한 모듈을 선택한 후 코

드에 모듈을 포함시키면 된다. 그림 2-1에는 스프링 프레임워크 패키지를 내려받고 압축을 푼 후

dist 폴더의 내용이 나와 있다.

Page 50: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

22 l 프로 스프링 3

그림 2-1 압축을 푼 스프링 프레임워크 라이브러리

스프링 모듈

스프링 버전 3.1에서 스프링은 20개의 JAR 파일로 패키징된 20개의 모듈을 제공한다. 표 2-1에는

이들 JAR 파일과 관련 모듈이 나와 있다. 실제 JAR 파일의 형식은 org.springframework.aop-

3.1.0.RELEASE.jar와 같은 형태지만 여기서는 간단히 표기하기 위해 특정 모듈 영역(예를 들어

aop)만을 표기했다.

표 2-1 스프링 모듈

JAR 파일 설명

aop 이 모듈에는 애플리케이션에서 스프링의 AOP 기능을 사용하는 데 필요한 클래스가

모두 들어 있다. 이 JAR 파일은 AOP를 사용하는 스프링의 다른 기능(이를테면 선언

적 트랜잭션 관리)을 사용할 때도 애플리케이션에 포함시켜야 한다. 더불어 AspectJ

와의 연동 기능을 지원하는 클래스들도 이 모듈에 패키징돼 있다.

asm ASM(asm.ow2.org)은 자바 바이트코드 조작 프레임워크다. 스프링은 이 라이브러

리에 의존해 스프링 빈의 바이트코드를 분석하고, 동적으로 이들 빈을 수정하며 런

타임 시점에 새로운 바이트코드를 생성한다.

Page 51: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 23

JAR 파일 설명

aspects 이 모듈에는 AspectJ AOP 라이브러리의 고급 연동에 필요한 클래스가 모두 들어 있

다. 예를 들어 스프링 설정에 자바 클래스를 사용하고 AspectJ 방식의 애노테이션을

통해 트랜잭션 관리를 해야 한다면 이 모듈이 필요하다.

beans 이 모듈에는 스프링 빈을 활용하는 지원 기능 관련 클래스가 모두 들어 있다. 이런 클

래스는 대부분 스프링의 빈 팩터리 구현체를 지원한다. 예를 들어 스프링의 XML 설

정 파일과 자바 애노테이션을 파싱하는 데 필요한 클래스는 이 모듈 패키지에 들어

있다.

context 이 모듈에는 스프링 코어를 확장한 많은 클래스가 들어 있다. 이 책에서 곧 보겠지만

모든 클래스는 EJB, JNDI(자바 명명 및 디렉터리 인터페이스), JMX(자바 관리 확장)

용 클래스와 연동하는 데 스프링의 ApplicationContext 기능(5장에서 다룸)을 사

용해야 한다. 또 이 모듈에는 스프링 리모팅 클래스, 동적 스크립팅 언어(예를 들어

제이루비, 그루비, 빈셸)와 연동하는 클래스, 빈 유효성 검증(JSR-303) API, 스케줄

링 및 작업 실행 등을 하는 클래스도 모두 들어 있다.

context.support 이 모듈에는 스프링 컨텍스트 모듈을 더욱 확장한 클래스가 들어 있다. 사용자 인터

페이스 측면에서는 메일 지원과 벨로시티, 프리마커, JasperReports 등과 같은 템플

릿 엔진과 연동하는 클래스가 들어 있다. 더불어 다양한 작업 실행 및 CommonJ와

Quartz 같은 라이브러리를 비롯한 스케줄링 라이브러리와 연동 기능을 담당하는 클

래스가 이 패키지에 포함돼 있다.

core 모든 스프링 애플리케이션에서 필요한 핵심 모듈이다. 이 JAR 파일에는 다른 스프링

모듈에서 공통으로 사용하는 모든 클래스(예를 들어 설정 파일에 접근하는 클래스)

가 들어 있다. 더불어 이 JAR에서는 스프링 코드 기반과 커스텀 애플리케이션에서 유

용하게 사용할 수 있는 유틸리티 클래스도 들어 있다.

expression 이 모듈에는 스프링 표현 언어(SpEL) 지원 클래스가 모두 들어 있다.

instrument 이 모듈에는 스프링의 자바 가상 머신(JVM) 부트스트랩을 위한 스프링의 인스트루

멘테이션 에이전트가 들어 있다. 이 JAR 파일은 스프링 애플리케이션에서 AspectJ

를 사용해 로드 시점 위빙을 사용할 때 필요하다.

instrument.tomcat 이 모듈에는 톰캣 서버에서 JVM을 부트스트랩 하는 데 필요한 인스트루멘테이션 에

이전트가 들어 있다.

jdbc 이 모듈에는 JDBC 지원 기능에 필요한 모든 클래스가 들어 있다. 이 모듈은 데이터베

이스에 접근해야 하는 애플리케이션에서는 항상 필요하다. 데이터 소스, JDBC 데이

터 타입, JDBC 템플릿, 네이티브 JDBC 연결 등을 지원하는 클래스가 이 모듈에 패키

징돼 있다.

jms 이 모듈에는 JMS 지원 기능을 담당하는 클래스가 모두 들어 있다.

orm 이 모듈은 스프링의 표준 JDBC 기능을 확장해 하이버네이트, 아이바티스(마이바티

스는 제외), JDO, JPA 같은 인기 있는 ORM 툴을 지원한다. 이 JAR에 포함된 많은

클래스는 spring-jdbc.jar에 포함된 클래스에 의존하는 만큼 애플리케이션에는 이

JAR 파일도 함께 포함시켜야 한다.

oxm 이 모듈은 OXM(객체-XML 매핑) 지원 기능을 제공한다. XML 추상화, 마샬링과 언

마샬링, 캐스터, JAXB, XMLBeans, XStream 같은 인기 있는 툴의 지원 기능 등이

이 모듈에 들어 있다.

web.struts 이 모듈에는 스프링과 스트러츠 웹 프레임워크의 연동을 담당하는 모든 클래스가 들

어 있다.

Page 52: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

24 l 프로 스프링 3

JAR 파일 설명

test 앞서 언급한 것처럼 스프링은 애플리케이션 테스트를 돕기 위해 많은 목(mock) 클래

스를 제공한다. 이들 목 클래스 중 대부분은 스프링 테스트 스위트 내에서 사용하며,

충분한 테스트를 거쳤으며 애플리케이션 테스트를 간단하게 해준다. 웹 애플리케이

션에서는 HttpServletRequest와 HttpServletResponse의 목 클래스를 활용해 단

위 테스트를 손쉽게 진행할 수 있다. 다른 한편으로 스프링은 JUnit 단위 테스트 프레

임워크와도 긴밀하게 연동하며, JUnit 테스트 케이스 개발을 지원하는 클래스 중 다

수를 이 모듈을 통해 제공한다. 예를 들어 SpringJUnit4ClassRunner는 단위 테스

트 환경에서 손쉽게 스프링 ApplicationContext를 부트스트랩할 수 있게 해준다.

transaction 이 모듈은 스프링의 트랜잭션 인프라를 지원하는 모든 클래스를 제공한다. 이 모듈에

서는 자바 트랜잭션 API(JTA) 지원을 위한 트랜잭션 추상 레이어에 속한 클래스와 주

요 벤더의 애플리케이션 서버와 연동하는 클래스를 볼 수 있다.

web 이 모듈에는 웹 애플리케이션에서 스프링을 사용하는 데 필요한 핵심 클래스가 들어

있다. 이들 클래스에는 ApplicationContext 기능을 자동으로 로드하는 클래스, 파

일 업로드 지원 클래스, 쿼리 문자열에서 int 값을 파싱하는 등 반복 작업을 수행하는

유용한 클래스가 포함돼 있다.

web.servlet 이 모듈에는 스프링의 자체 MVC 프레임워크에 사용하는 클래스가 모두 들어 있다.

애플리케이션에서 별도 MVC 프레임워크를 사용 중이라면 이 JAR 파일의 클래스는

전혀 필요 없다. 스프링 MVC는 17장과 18장에서 자세히 살펴본다.

web.portlet 이 모듈은 스프링 MVC를 사용해 포틀릿을 개발해 포탈 서버 환경에 배포할 수 있게

끔 지원 기능을 제공한다.

애플리케이션에 적합한 모듈 선택

이클립스 같은 IDE나 메이븐, 아이비 같은 의존성 관리 툴이 없으면 애플리케이션에서 사용할

모듈을 고르는 게 조금 까다롭다. 예를 들어 스프링의 빈 팩터리와 DI 지원 기능만 필요하다고

해도 여전히 spring-core, spring-beans, spring-context, spring-aop, spring-asm 같은 모듈이

필요하다. 또 스프링의 웹 애플리케이션 지원 기능이 필요하다면 spring-web도 추가해야 한다.

더불어 스트러츠와 연동하려면 spring-struts도 추가해야 하는 식이다.

하지만 IDE를 사용하면, 특히 스프링소스 툴 스위트(STS)를 사용하면(이 IDE는 이 책에서 기

본 IDE로 사용한다) 이런 의존성을 관리하고 시각화하기가 훨씬 쉽다. STS에서는 스프링 템플

릿 프로젝트를 생성하고 애플리케이션에 맞는 다양한 프로젝트 템플릿을 선택할 수 있는 옵션이

있다. 스프링 템플릿 프로젝트는 메이븐으로 의존성을 관리하며, STS는 메이븐 연동 이클립스

플러그인인 m2e를 기본으로 제공한다. 프로젝트 템플릿을 선택하면 STS는 여러분 대신 적절한

의존성을 선언해 프로젝트를 생성해준다. 메이븐의 의존성 지원 기능 덕분에 필요한 서드파티

라이브러리는 모두 자동으로 포함된다.

Page 53: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 25

그림 2-2에는 STS로 간단한 스프링 유틸리티 프로젝트를 만든 화면이 나와 있다. 이 화면은 프

로젝트의 pom.xml(메이븐 프로젝트 객체 모델) 파일과 m2e 플러그인의 의존성 계층구조 뷰어

에서 가져온 것으로, 프로젝트에 필요한 모든 의존성을 보여준다. 의존성 계층구조 다이어그램

에서는 이 프로젝트가 spring-context에 의존하는 모습을 볼 수 있는데, spring-context는 다시

spring-aop, spring-beans, spring-core, spring-expression, spring-asm을 필요로 한다. 더불어

spring-aop은 aopalliance에 의존하며, spring-core는 commons-logging에 의존한다. 기본적

으로 스프링 프로젝트는 로깅에 log4j를 사용한다. 끝으로 다른 테스트 관련 의존성(spring-test,

junit)도 볼 수 있다.

그림 2-2 스프링의 빈 팩터리와 DI 기능을 사용하는 간단한 스프링 유틸리티 프로젝트에서 필요한 스프링 의

존성 계층구조

메이븐 저장소의 스프링 모듈

인터넷에서 내려받는 방식 외에 아이비와 메이븐 같은 애플리케이션 의존성 관리 툴을 사용해

스프링 라이브러리를 관리할 수도 있다. 이 절에서는 메이븐 저장소의 스프링 모듈을 살펴본다.

아파치 소프트웨어 재단이 설립한 메이븐(http://maven.apache.org)은 오픈소스부터 엔터프

라이즈 환경에 이르기까지 자바 애플리케이션의 의존성을 관리하는 가장 인기 있는 툴 중 하나

가 됐다.

Page 54: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

26 l 프로 스프링 3

메이븐은 매우 강력한 빌드, 패키징, 의존성 관리 툴이다. 메이븐은 리소스 처리와 컴파일부터

테스트와 패키징까지 애플리케이션의 전체 빌드 주기를 관리해준다. 더불어 데이터베이스를 업

데이트하고 패키징된 애플리케이션을 특정 서버(톰캣, JBoss, 웹스피어 등)에 배포하는 등 다양

한 작업을 해주는 메이븐 플러그인이 존재한다.

거의 모든 오픈소스 프로젝트는 메이븐 저장소를 통한 라이브러리 배포를 지원한다. 가장 인

기 있는 저장소는 아파치에서 호스팅하는 메이븐 중앙 저장소로, 메이븐 중앙 웹 사이트(http://

search.maven.org)에서는 아티팩트(artifact)의 존재 여부를 검사하고 관련 정보를 찾아서 볼 수

있다. 메이븐을 개발 장비에 내려받아 설치하면 자동으로 메이븐 중앙 저장소에 접근할 수 있다.

다른 오픈소스 커뮤니티(JBoss, 스프링소스 등)도 사용자를 위해 자체 메이븐 저장소를 제공한

다. 하지만 이러한 저장소에 접근하려면 메이븐의 설정 파일에 저장소를 추가해야 한다.

메이븐에 대한 상세 설명은 이 책의 범위를 벗어나며, 온라인 문서나 메이븐 관련 서적을 보면

얼마든지 참고할 수 있다. 하지만 현재 메이븐을 폭넓게 사용하는 만큼 여기서도 메이븐 저장소

에서의 스프링 패키징 구조에 대해 언급하는 게 좋을 것 같다.

메이븐의 개별 아티팩트는 그룹 ID, 아티팩트 ID, 패키징 타입, 버전으로 구분한다. 예를 들어

log4j의 경우 그룹 ID는 log4j, 아티팩트 ID는 log4j, 패키징 타입은 jar다. 그리고 그 아래 다양한

버전이 정의돼 있다. 예를 들어 1.2.16 버전의 경우 아티팩트의 파일명은 해당 그룹 ID, 아티팩트

ID, 버전 폴더 아래 있는 log4j-1.2.16.jar가 된다.

다른 오픈소스 라이브러리와 마찬가지로 스프링의 메이븐 아티팩트도 아파치의 메이븐 중앙

에서 찾을 수 있다. 하지만 스프링소스도 자체 메이븐 저장소를 호스팅하고 있으며 OSGi와 호환

되는 엔터프라이즈 번들 저장소(EBR) 형태로 스프링 라이브러리를 제공한다. 스프링 EBR의 명

명 관례는 메이븐 중앙과는 다르다.

독자들의 혼동을 막기 위해 메이븐 중앙에 있는 스프링 아티팩트와 스프링소스 자체에서 운영

하는 메이븐 저장소의 명명 관례가 서로 다르다는 점을 언급해야 할 것 같다. 결국 여러분이 속한

개발 팀은 둘 중 하나를 표준으로 삼아야 한다. 일반적으로 메이븐 중앙의 아티팩트를 사용하는

것을 더 선호한다. 하지만 애플리케이션을 OSGi 컨테이너(예를 들어 스프링 다이나믹 모듈)에 배

포할 계획이 있다면 스프링 EBR을 사용하자. 표 2-2에는 메이븐 중앙과 스프링소스 EBR에 있는

스프링 아티팩트의 각기 다른 이름이 정리돼 있다. 두 저장소 모두 그룹 ID는 같고, 아티팩트 ID

만 다르다. 표 2-1과 마찬가지로 여기서도 JAR 파일에서 모듈 부분만 떼어내 표기했다.

Page 55: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 27

표 2-2 스프링 모듈

스프링 모듈

JAR 파일

그룹 ID 아티팩트 ID

(메이븐 중앙 저장소)

아티팩트 ID

(스프링 EBR)

aop org.springframework spring-aop org.springframework.aop

asm org.springframework spring-asm org.springframework.asm

aspects org.springframework spring-aspects org.springframework.

aspects

beans org.springframework spring-beans org.springframework.beans

context org.springframework spring-context org.springframework.

context

context.

support

org.springframework spring-context

-support

org.springframework.

context.support

core org.springframework spring-core org.springframework.core

expression org.springframework spring-expression org.springframework.

expression

instrument org.springframework spring-instrument org.springframework.

instrument

instrument.

tomcat

org.springframework spring-instrument

-tomcat

org.springframework.

instrument.tomcat

jdbc org.springframework spring-jdbc org.springframework.jdbc

jms org.springframework spring-jms org.springframework.jms

orm org.springframework spring-orm org.springframework.orm

oxm org.springframework spring-oxm org.springframework.oxm

web.struts org.springframework spring-struts org.springframework.struts

test org.springframework spring-test org.springframework.test

transaction org.springframework spring-tx org.springframework.

transaction

web org.springframework spring-web org.springframework.web

web.servlet org.springframework spring-webmvc org.springframework.web.

servlet

web.portlet org.springframework spring-webmvc

-portlet

org.springframework.web.

portlet

Page 56: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

28 l 프로 스프링 3

스프링 의존성 분석

스프링은 다양한 서드파티 라이브러리 의존성을 갖고 있다. 소스에서 스프링을 빌드할 때는 이

런 의존성이 모두 필요하다. 하지만 런타임 시점에는 주로 일부 의존성만 필요하며, 필요한 의존

성만 포함시키면 배포 결과물의 크기를 실제로 최소화할 수 있다.

의존성의 방대한 규모로 인해 스프링은 좀 더 쉽게 처리할 수 있게 의존성을 그룹으로 정리했

다. 표 2-3에는 이들 그룹이 나와 있다. 더불어 이 표에는 각 그룹에 속한 JAR 파일과 해당 의존

성을 사용하는 용도가 정리돼 있다.

표 2-3은 스프링 프레임워크의 모듈이 의존하는 기본적이고 공통적인 의존성만을 반영하고

있다. 애플리케이션에서는 이들 외에 보통 추가로 의존성이 필요하다. 예를 들어 스프링 XML 지

원 기능(oxm 모듈)에서 내부 객체-XML 매핑 라이브러리로 캐스터를 사용 중이라면 캐스터 라

이브러리가 추가로 필요하다. 또 XStream을 사용한다면 XStream 라이브러리가 필요한 식이다.

이 책에서 나중에 이들 각 주제에 대해 자세히 다룰 때는 추가로 사용되는 서드파티 라이브러리

에 대해서도 함께 언급할 것이다. 따라서 이 표의 내용은 완전한 레퍼런스보다는 전반적인 개요

로 이해하면 된다.

표 2-3 스프링 프레임워크의 서드파티 라이브러리 의존성

의존성 그룹 JAR 파일 설명

aopalliance aopalliance-1.0.jar AOP 연합(http://aopalliance.

sourceforge.net)은 자바에서 AOP를 위한

표준 인터페이스를 제공하기 위해 여러 프로

젝트와 연계해 오픈소스로 공동 작업한 결과

물이다. 스프링의 AOP 구현체는 표준 AOP

연합의 API를 기반으로 한다. 이 JAR 파일은

스프링의 AOP나 AOP 기반 기능을 사용할

계획이 있을 때만 필요하다.

aspectj aspectjweaver-1.6.8.jar 스프링은 강력한 AOP 기능을 제공하기 위해

AspectJ와 긴밀하게 연동한다. AspectJ를

사용해야 할 때는 이 라이브러리가 필요하다.

caucho com.springsource.com.

caucho-3.2.1.jar

스프링 리모팅은 Caucho의 Burlap과

Hessian을 비롯해 다양한 프로토콜을 지원

한다. 이 그룹에 속한 JAR 파일은 애플리케이

션에서 해당 프로토콜을 사용할 때만 필요하

다.

cglib cglib-2.2.jar CGLIB은 스프링의 AOP 모듈이 의존하는 코

드 생성 라이브러리다. CGLIB은 자바 클래스

와 인터페이스용 프록시를 생성할 수 있다.

Page 57: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 29

의존성 그룹 JAR 파일 설명

dom4j dom4j-1.6.1.jar dom4j는 하이버네이트를 사용할 때 꼭 필

요하다. 따라서 애플리케이션에서 ORM으

로 하이버네이트를 사용할 계획이 있다면 이

JAR를 포함시켜야 한다.

easymock easymock-2.5.1.jar EasyMock은 스프링 테스트 스위트에서 사

용하므로 이 JAR는 테스트 스위트를 빌드하

고 실행할 때만 필요하다. 이 JAR는 애플리케

이션을 배포할 때는 필요하지 않다.

freemarker freemarker-2.3.15.jar 스프링은 프리마커 템플릿 엔진을 감싼 래퍼

클래스를 제공하며, 프리마커 템플릿을 웹 애

플리케이션의 뷰로 사용할 수 있는 지원 기능

을 제공한다. 이 JAR는 프리마커를 사용할 때

마다 필요하다.

hibernate hibernate-core-3.6.10.Final.

jar, hibernate-commons-

annotations-3.2.0.Final.jar,

hibernate-

entitymanager-3.6.10.Final.

jar, hibernate-jpa-2.0-api-

1.0.1.Final.jar, hibernate-

validator-4.1.0.GA.jar

이들 JAR는 스프링의 하이버네이트 연동 기

능과 지원 클래스를 사용할 때 필요하다. 마

이바티스 같은 다른 ORM 툴을 사용한다면

애플리케이션에서 이들 JAR를 제거해도 된

다. 하이버네이트를 사용할 때는 애플리케이

션에 javassist.jar 파일도 꼭 포함시켜야 한

다. 또 JPA와 하이버네이트를 영속성 제공자

로 사용할 때는 엔티티 매니저와 JPA 라이브

러리도 필요하다. 더불어 JSR-303 빈 유효성

검증 API나 JPA 2.0이 필요하다면 하이버네

이트 밸리데이터도 필요하다.

javassist javassist-3.12.0.GA.jar 이 라이브러리는 바이트코드 조작 라이브러

리다.

mybatis mybatis-3.0.6.jar,

mybatis-spring-1.0.2.jar

이들 파일은 마이바티스를 사용할 때 필요하

다.

itext itextpdf-5.1.2.jar 스프링은 iText를 사용해 웹 단에서 PDF 지

원 기능을 제공한다. 이 JAR는 애플리케이션

에서 PDF 문서 결과를 생성해야 할 때만 필요

하다.

jee activation-1.1.0.jar,

connector-api-1.5.jar,

jaxws-api-2.2.6.jar,

ejb-api-3.0.jar,

jms-1.1.jar,

jstl-1.2.jar,

jta-1.1.jar,

mail-1.4.4.jar,

servlet-2.5.jar,

xml-apis-2.0.2.jar

보다시피 이 그룹에는 다양한 JEE 관련 JAR

파일이 들어 있다. activation.jar와 mail.jar

파일은 스프링 메일 지원 기능의 자바메일 구

현체를 사용할 때 필요하다. 또 connector-

api.jar는 하이버네이트에서 JCA 커넥터를

사용할 때, ejb.jar는 스프링의 EJB 지원 기

능을 사용할 때, jms.jar는 스프링의 JMS 지

원 기능을 사용할 때 각각 필요하다. 웹 애

플리케이션에서 스프링의 JSTL 지원 기능

을 사용하려면 servlet.jar와 jstl.jar가 필요

하다. jaxws-api.jar 파일은 스프링 리모팅에

서 JAX-WS 지원 기능에 필요하고, jta.jar는

JTA 트랜잭션 지원 기능에 필요하다.

Page 58: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

30 l 프로 스프링 3

의존성 그룹 JAR 파일 설명

apache-commons commons-attributes-api-

2.1.jar,

commons-attributes-

compiler-2.2.jar,

commons-beanutils-1.8.3.jar,

commons-collections-

3.2.0.jar,

commons-dbcp-1.4.jar,

commons-digester-2.1.jar,

commons-discovery-0.5.jar,

commons-fileupload-

1.2.0.jar,

commons-lang-2.5.jar,

commons-logging-1.1.1.jar,

commons-pool-1.5.4.jar,

httpclient-4.1.1.jar,

스프링에서는 아파치 커먼즈 프로젝트에서

많은 컴포넌트를 사용한다. 애플리케이션에

서 소스 레벨 메타데이터를 사용하고 싶다면

commons-attributes-api.jar와 애플리케이

션에서 속성을 컴파일해주는 컴파일러 JAR

파일이 필요하다. BeanUtils, Collections,

Digester, Discovery JAR 파일은 스트러츠

에서 사용하며, Hibernate는 Collections도

사용한다. DBCP는 DBCP 커넥션 풀을 생성

할 때 스프링의 JDBC 지원 기능에서 필요로

하며, 일부 예제 애플리케이션에서도 풀링이

필요하다. FileUpload는 웹 애플리케이션에

서 스프링 래퍼를 사용해 파일 업로드를 처리

하려고 할 때 필요하다. 끝으로 로깅은 스프링

전체에서 사용하므로 이 JAR 파일은 모든 스

프링 기반 애플리케이션에 포함시켜야 한다.

junit junit-4.9.jar JUnit은 런타임 시점에는 전혀 필요 없다.

JUnit은 테스트 스위트를 빌드하고 실행할 때

만 사용한다.

log4j log4j-1.2.14.jar 이 JAR는 스프링을 사용해 log4j 로깅을 설

정하려고 할 때 필요하다.

poi poi-3.6.jar 스프링의 MVC 프레임워크에 마이크로소프

트 엑셀 생성 기능을 지원한다.

quartz quartz-1.6.2.jar 스프링 쿼츠 기반의 스케줄링 지원 기능에 사

용된다.

struts struts-1.2.9.jar 스트러츠 JAR는 스트러츠를 스프링과 연계해

웹 애플리케이션을 개발할 때마다 필요하다.

velocity velocity-1.5.0.jar 스프링은 벨로시티를 감싼 래퍼를 제공해 이

를 DI로 주입할 수 있게 하고 애플리케이션에

서 벨로시티를 사용하기 위해 작성해야 할 코

드를 줄여준다. 더불어 스프링은 웹 단에서

벨로시티를 뷰 제공자로 사용할 수 있게 지원

클래스도 제공한다. 이들 기능 중 하나라도

사용한다면 배포판에 벨로시티 JAR 파일을

포함시켜야 한다.

이 표에서 볼 수 있듯이 스프링의 의존성은 매우 다양하며 대부분의 애플리케이션에서는 전

체 의존성 중 일부만 필요하다. 애플리케이션에 꼭 필요한 의존성만 정확히 골라 애플리케이션

에 추가할 수 있게 시간을 들여 고민하는 게 좋다. 이렇게 하면 애플리케이션의 크기를 그만큼 줄

일 수 있다. 특히 원격으로 자주 배포해야 한다면 꼭 필요한 의존성만 포함시키는 게 훨씬 편하

다. 웹을 통해 인터넷 회선이 느린 사람들을 대상으로 애플리케이션을 배포할 계획이 있다면 애

플리케이션의 크기는 가능한 한 줄이는 게 좋다.

Page 59: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 31

예제 애플리케이션

여러 오픈소스 프로젝트는 물론 상업적인 제품도 사람들이 쉽게 시작할 수 있게 충분한 문서가

뒷받침되는 예제 코드를 제공하는 일에는 서툴다. 다행히 스프링은 스프링의 다양한 기능을 보

여주는 풍부한 예제 애플리케이션을 제공한다. 예제 애플리케이션은 개발 팀에서 프레임워크의

최우선 순위를 부여받고 있으며 계속해서 개선 작업이 진행 중이다. 이런 이유로 새로운 기능을

배울 때는 보통 테스트 스위트에서 원하는 내용을 찾은 후 예제부터 살펴보는 게 좋다.

스프링 예제 소스코드 내려받기

모든 스프링 예제 소스코드(spring-mvc-showcase 프로젝트 제외)는 스프링소스의 SVN 저장

소에서 호스팅한다. 이 소스코드를 가져오려면 빈 디렉터리에서 다음 명령을 실행하면 된다.

svn co https://src.springframework.org/svn/spring-samples/

그림 2-3에는 이 링크에서 체크아웃한 예제 애플리케이션 폴더가 나와 있다.

그림 2-3 스프링 예제 애플리케이션

Petclinic 애플리케이션

Petclinic(petclinic 폴더 아래에 있는)은 스프링의 데이터 접근 지원 기능을 보여주기 위해 개발

된 재미있는 예제 애플리케이션이다. 이 안에서는 가상의 동물 병원 데이터베이스를 조회하고

Page 60: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

32 l 프로 스프링 3

업데이트하는 웹 기반 애플리케이션을 볼 수 있다. 이 애플리케이션의 장점은 스프링을 사용할

때 애플리케이션과 데이터 접근 로직 사이의 결합을 얼마나 쉽게 없앨 수 있는지 알 수 있게끔 여

러 DAO 구현체를 서로 교체해서 쓸 수 있다는 점이다.

하이버네이트 DAO 구현체는 한두 줄로 8개의 DAO 메서드를 각각 구현함으로써 스프링의 강

력한 하이버네이트 지원 기능을 잘 보여준다. JDBC 구현체도 하이버네이트만큼이나 흥미롭다.

먼저 이 구현체는 SimpleJdbcTemplate과 SimpleJdbcInsert를 사용해 일반적인 JDBC 작업을

할 때 반복 코드를 작성해야 하는 불편함을 없애준다. 다른 한편으로는 데이터 접근을 얼마나 객

체지향적인 방식으로 처리할 수 있는지 명확하게 보여준다. RowMapper 인터페이스와 이 인터

페이스의 다양한 구현체를 사용한 쿼리 결과는 값 객체로 바로 매핑되고 호출자에게 반환된다.

이 프로젝트에는 스프링의 MVC 지원 기능을 사용해 웹 애플리케이션을 개발하는 훌륭한 예

제가 들어 있는 만큼 스프링 MVC를 애플리케이션에 사용할 계획이라면 이 예제를 먼저 살펴

보자.

JDBC 지원 기능은 8장, 하이버네이트는 9장, 스프링 MVC는 17장에서 다룬다.

Petclinic 그루비 애플리케이션

Petclinic 그루비 예제(petclinic-groovy 폴더)는 같은 Petclinic 애플리케이션을 그루비와 그레

일즈를 사용해 구현한 예제다. 그루비는 JVM에서 실행되는 동적 스크립팅 언어이고, 그레일즈

는 그루비 기반의 웹 애플리케이션 개발을 빠르게 해주는 개발 프레임워크다. 이 예제에서 스프

링 MVC 레이어의 컨트롤러는 모두 그루비 언어로 개발됐다.

그레일즈에 대한 설명은 이 책의 범위를 벗어나지만 스프링과 그루비 연동은 22장에서 살펴

본다.

jPetStore 애플리케이션

jPetStore 예제(jpetstore 폴더)는 클린턴 비긴이 아이바티스용으로 만든 jPetStore 예제를 기반

으로 한다. 이 애플리케이션은 예제 애플리케이션치고는 규모가 매우 크다. 이 예제에는 스프링

과 아이바티스를 사용하고 오라클 구현체, 마이크로소프트 SQL 서버, MySQL, PostgreSQL 구

현체를 적용한 완전한 DAO 레이어가 들어 있다. 이 예제의 비즈니스 단은 모두 스프링으로 관리

하며, DAO 레이어와 결합해 스프링을 통해 트랜잭션을 관리하는 좋은 예제를 보여준다.

Page 61: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 33

또 이 애플리케이션에는 스프링 MVC와 스트러츠를 활용하는 훌륭한 예제도 들어 있다. 아울

러 JAXRPC를 사용해 스프링 리모팅을 활용하는 법까지도 다룬다.

스프링 MVC는 17장, 아이바티스(마이바티스)는 11장, 스프링 리모팅은 16장에서 다룬다.

스프링 설정 기본 애플리케이션

con�guration-basic 프로젝트(con�guration-basic 폴더)는 자바 클래스를 사용해 스프링 애플

리케이션의 설정을 정의하는 법을 보여준다(이 기능은 스프링 3.0에서 새로 생긴 기능이다). 이

프로젝트에는 XML 파일이 없다. 모든 스프링 관련 설정은 다양한 애노테이션을 사용해 자바 클

래스(AppCon�g.java) 내에서 선언한다.

자바 클래스를 활용한 스프링 설정은 5장에서 다룬다.

스프링 작업과 스케줄링 애플리케이션

task-basic 프로젝트(task-basic 폴더)는 스프링의 작업 실행자와 스케줄링 지원 기능을 활용하

는 법을 보여준다.

스프링의 스케줄링 지원 기능은 15장에서 설명한다.

스프링 MVC 쇼케이스 애플리케이션

스프링 MVC 쇼케이스 프로젝트(spring-mvc-showcase 폴더)는 여러 개의 간단한 예제를 통해

스프링 MVC 웹 프레임워크의 기능을 보여준다. 이 애플리케이션은 스프링 MVC 3으로 개발했

으며 스프링 MVC 3의 주요 기능은 다음과 같다.

▒ @Controller 애노테이션을 사용한 스프링 MVC 컨트롤러 선언

▒ 요청 매핑, 요청 데이터 가져오기, 응답 생성

▒ 메시지 변환기와 뷰 렌더링

▒ 폼, 타입 유효성 검증, 파일 업로드, 예외 처리

스프링 MVC는 17장에서 자세히 설명한다.

Page 62: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

34 l 프로 스프링 3

알아두기

다른 스프링 예제 프로젝트와 달리 spring-mvc-showcase 프로젝트는 깃허브(www.

github.com)에서 호스팅한다. 깃허브는 매우 인기 있는 소셜 코딩 웹사이트다. 깃허브에서

소스코드를 가져오려면 깃 툴(http://git-scm.com)을 먼저 내려받아야 한다. 깃을 설치하고

나면 데스크톱에 깃허브에서 받은 프로젝트 저장소를 복제하기 위해 다음 명령을 실행한다.

git clone https://github.com/SpringSource/spring-mvc-showcase

스프링 MVC 기본과 Ajax 애플리케이션

spring-mvc-showcase 프로젝트 외에 스프링은 두 개의 프로젝트를 추가로 제공해 스프링 MVC

3의 다양한 활용 사례를 보여준다. mvc-basic 프로젝트(mvc-basic 폴더)는 단일 컨트롤러로 이

뤄진 아주 간단한 스프링 MVC 프로젝트다. mvc-ajax 프로젝트(mvc-ajax 폴더)는 스프링 MVC

를 사용해 Ajax 지원 기능을 갖춘 웹 애플리케이션을 개발하는 법을 보여준다. 서버 사이드는 스

프링 MVC로 개발됐으며, 스프링 MVC의 내장 RESTful-WS 지원 기능을 사용해 요청을 매핑

하고 데이터는 JSON 형식으로 반환한다. 클라이언트 사이드는 Ajax 지원 기능을 제공하는 인기

있는 자바스크립트 라이브러리인 제이쿼리를 사용해 스프링 MVC와 연동함으로써 풍부한 사

용자 경험을 전달한다.

스프링 Petcare 애플리케이션

petcare 예제(petcare 폴더)는 또 다른 재미있는 프로젝트다. 이 프로젝트는 스프링 프레임워크

와 다른 스프링 프로젝트의 다양한 기능을 보여주는 완전한 형태의 웹 애플리케이션이다. 웹에

서는 스프링 MVC와 스프링 시큐리티를 함께 사용해 리소스를 보호한다. 뷰에서는 타일즈와 연

동해 템플릿 지원 기능을 사용한다. 더불어 메일링 지원 기능도 함께 살펴볼 수 있다.

이 프로젝트의 또 다른 재미있는 특징은 스프링 인티그레이션 프로젝트와 연동해 애플리케이

션 메시지를 브로드캐스팅한다는 점이다. 그뿐만 아니라 스프링 루(Spring Roo)라는 또 다른 프

로젝트를 사용해 백엔드 데이터베이스 스키마를 기반으로 자바빈을 생성한다.

스프링 시큐리티를 활용한 웹 애플리케이션 리소스 보호는 17장에서 다루고, 스프링 인티그레

이션, 스프링 루를 비롯한 다른 스프링 프로젝트와 스프링 프레임워크를 함께 사용하는 방법에

대해서는 20장에서 개략적으로 설명한다.

Page 63: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 35

스프링 웹플로우 애플리케이션

스프링과 스프링 웹플로우의 기능을 보여주는 예제 프로젝트가 몇 개 있다. travel 애플리케이션

(travel 폴더)은 스프링 프레임워크 3과 스프링 웹플로우 2.1을 함께 사용하는 레퍼런스 예제다.

스프링 웹플로우는 많은 뷰 기술과 연동된다. 이런 뷰 기술 중 하나로 자바 서버 페이스(JSF)

가 있다. 스프링 페이스는 이런 JSF와의 긴밀한 연동 기능을 제공하는 스프링 웹플로우 내의 모

듈이다. 이 기능을 보여주는 예제 애플리케이션은 두 개가 있다. web�ow-primefaces-showcase

예제(webflow-primefaces-showcase 폴더)는 프라임페이스와의 연동을 보여주고, webf low-

richfaces-showcase 예제(web�ow-richfaces-showcase 폴더)는 JBoss 리치페이스와의 연동을

보여준다. 프라임페이스와 JBoss 리치페이스는 모두 인기 있는 JSF 라이브러리이며, 최신 버전이

JSF 2.0 표준을 따른다.

프라임페이스를 예로 들어 스프링 웹 플로우를 활용한 웹 애플리케이션 개발은 18장에서 살

펴본다.

스프링 문서

실제 개발자들이 실제 애플리케이션을 만드는 데 스프링을 지금처럼 유용하게 쓸 수 있었던 이

유 중 하나는 스프링의 잘 작성된, 정확한 문서 덕분이다. 매번 출시할 때마다 스프링 프레임워크

의 문서 팀은 개발 팀에서 모든 문서 작업을 끝내고 문서 내용을 가다듬었는지 열심히 확인하고

있다. 이 말은 스프링의 모든 기능이 자바독으로 완전히 문서화될 뿐 아니라 각 배포판에 포함된

레퍼런스 매뉴얼에서도 모든 기능을 다루고 있다는 의미다. 아직 스프링 자바독과 레퍼런스 매뉴

얼에 익숙하지 않다면 지금 익숙해지자. 이 책의 목적은 이러한 참고자료를 대신하는 게 아니다.

오히려 이 책의 목적은 이런 레퍼런스를 보완하면서 기초부터 시작해 스프링 기반의 애플리케이

션을 개발하는 법을 보여주는 데 있다.

스프링을 활용한 ‘Hello World!’ 출력

이쯤에서 독자들은 스프링이 기초가 튼튼하고, 많은 지원을 받으며, 애플리케이션 개발에 활용

하기에 좋은 툴을 모두 갖춘 프로젝트라는 사실을 잘 알게 됐을 것이다. 하지만 아직 한 가지가

빠졌다. 아직까지 아무런 코드도 살펴보지 않은 것이다. 이제 독자들도 스프링을 실제로 사용하

는 법을 보고 싶을 테니 더 이상은 코드 없이 설명을 진행하기 힘든 만큼 바로 코드를 살펴보자.

Page 64: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

36 l 프로 스프링 3

이 절에서 소개하는 코드를 모두 이해하지 못한다고 해서 걱정할 필요는 없다. 여기서 설명한 주

제는 책의 내용을 진행하면서 훨씬 자세히 살펴볼 것이다.

예제 ‘Hello World!’ 애플리케이션 개발

독자들에게도 ‘Hello World!’ 예제는 익숙할 것이다. 하지만 독자들 중 지난 30년간 달에서 살다

온 사람이 있을지 몰라 간단히 예제 2-1을 통해 자바 버전의 ‘Hello World!’ 코드를 작성해 봤다.

예제 2-1 전형적인 'Hello World!' 예제

package com.apress.prospring3.ch2;

public class HelloWorld {

public static void main(String[] args) {

System.out.println("Hello World!");

}

}

이 예제는 예제치고는 매우 간단하다. 더불어 이 예제는 필요한 작업을 하고 있기는 하지만 확

장성은 높지 않다. 만일 메시지를 바꾸고 싶다면 어떻게 해야 할까? 또 표준 출력 대신 표준 에러

출력을 사용하거나 일반 텍스트 대신 HTML 태그를 쓰는 등 메시지 출력 방식을 바꾸려면 어떻

게 해야 할까?

여기서는 예제 애플리케이션의 요구 조건을 변경해 예제 애플리케이션이 메시지를 바꿀 수 있

는 단순하고 유연한 메커니즘을 지원해야 하고, 렌더링 동작을 간단히 바꿀 수 있어야 한다고 정

하기로 한다. 기본 ‘Hello World!’ 예제에서는 적절히 코드를 바꿔 앞의 두 가지 변경사항을 손쉽

게 바꿀 수 있다. 하지만 규모가 큰 애플리케이션에서는 다시 컴파일하는 데 시간이 걸리고 애플

리케이션을 완전히 재테스트해야 한다. 이보다 더 좋은 해결책은 예제 2-2에 나온 것처럼 메시지

내용을 외부화하고 명령행 인자를 통해 메시지 내용을 받는 등 런타임 시점에 읽어오는 것이다.

예제 2-2 명령행 인자를 사용한 'Hello World!'

package com.apress.prospring3.ch2;

public class HelloWorldWithCommandLine {

public static void main(String[] args) {

if(args.length > 0) {

System.out.println(args[0]);

} else {

Page 65: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 37

System.out.println("Hello World!");

}

}

}

이 예제는 우리가 원했던 일을 해준다. 이제 코드를 바꾸지 않더라도 메시지를 바꿀 수 있게 됐

다. 하지만 이 애플리케이션에는 아직 문제가 있다. 메시지를 렌더링하는 책임을 맡은 컴포넌트

가 메시지를 가져오는 책임도 함께 맡고 있기 때문이다. 따라서 메시지를 가져오는 법을 바꾸면

렌더러의 코드도 바뀌게 된다. 이뿐만 아니라 아직 렌더러를 쉽게 바꿀 수도 없다. 즉 렌더러를

바꾸려면 애플리케이션을 실행하는 클래스 자체를 바꿔야 한다.

이 애플리케이션을 기본 ‘Hello World!’ 애플리케이션에서 한 단계 더 발전시키려면 렌더링과

메시지 조회 로직을 별도 컴포넌트로 리팩터링하는 게 좋다. 더불어 애플리케이션을 정말 유연하

게 만들고 싶다면 이들 컴포넌트가 인터페이스를 구현하게 하고 이들 인터페이스를 사용하는 컴

포넌트와 실행 프로그램 사이의 상호의존성을 정의해야 한다.

메시지 조회 로직을 리팩터링하면 예제 2-3에 보이는 것처럼 getMessage() 메서드 하나로 구

성된 간단한 MessageProvider 인터페이스를 정의할 수 있다.

예제 2-3 MessageProvider 인터페이스

package com.apress.prospring3.ch2;

public interface MessageProvider {

public String getMessage();

}

예제 2-4에서 볼 수 있듯이 MessageRenderer 인터페이스는 메시지를 렌더링할 수 있는 모든

컴포넌트에서 구현한다.

예제 2-4 MessageRenderer 인터페이스

package com.apress.prospring3.ch2;

public interface MessageRenderer {

public void render();

public void setMessageProvider(MessageProvider provider);

public MessageProvider getMessageProvider();

}

Page 66: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

38 l 프로 스프링 3

보다시피 MessageRenderer 인터페이스는 render() 메서드를 갖고 있고 자바빈 스타일의 프

로퍼티인 MessageProvider도 갖고 있다. 모든 MessageRenderer 구현체는 메시지 조회 로직

과 분리돼 있으며 대신 제공되는 MessageProvider에게 메시지 조회 작업을 위임한다. 여기서

MessageProvider는 MessageRenderer의 의존성이다. 이들 인터페이스의 구현체는 간단히 작성

할 수 있다(예제 2-5 참고).

예제 2-5 HelloWorldMessageProvider 클래스

package com.apress.prospring3.ch2;

public class HelloWorldMessageProvider implements MessageProvider {

public String getMessage() {

return "Hello World!";

}

}

예제 2-5에서는 메시지로 항상 “Hello World!”를 반환하는 간단한 MessageProvider를 볼 수

있다. StandardOutMessageRenderer 클래스(예제 2-6)도 마찬가지로 간단하다.

예제 2-6 StandardOutMessageRenderer 클래스

package com.apress.prospring3.ch2;

public class StandardOutMessageRenderer implements MessageRenderer {

private MessageProvider messageProvider = null;

public void render() {

if (messageProvider == null) {

throw new RuntimeException(

"You must set the property messageProvider of class:"

+ StandardOutMessageRenderer.class.getName());

}

System.out.println(messageProvider.getMessage());

}

public void setMessageProvider(MessageProvider provider) {

this.messageProvider = provider;

}

public MessageProvider getMessageProvider() {

return this.messageProvider;

}

}

Page 67: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 39

이제 남은 일은 예제 2-7에 보이는 것처럼 프로그램을 시작하는 클래스의 main()을 재작성하

는 것뿐이다.

예제 2-7 리팩터링한 'Hello World!' 예제

package com.apress.prospring3.ch2;

public class HelloWorldDecoupled {

public static void main(String[] args) {

MessageRenderer mr = new StandardOutMessageRenderer();

MessageProvider mp = new HelloWorldMessageProvider();

mr.setMessageProvider(mp);

mr.render();

}

}

여기에 나온 코드도 아주 간단하다. 이 예제에서는 (물론 타입은 각각 MessageProvider와

MessageRenderer이지만) HelloWorldMessageProvider와 StandardOutMessageRenderer

인스턴스를 생성한다. 이렇게 하는 이유는 프로그래밍 로직에서는 인터페이스가 제공하는 메

서드만 사용해야 하며, 이들 인터페이스는 HelloWorldMessageProvider와 StandardOut

MessageRenderer에서 각각 구현하고 있기 때문이다. 그런 다음 MessageProvider를

MessageRenderer로 전달하고 MessageRenderer.render()를 호출한다. 그림 2-4에서는 이 예제

가 예상대로 실행됐음을 볼 수 있다.

그림 2-4 HelloWorldDecoupled의 결과

이제 예제가 원하는 형태에 좀 더 가까워졌지만 아직 작은 문제가 남아 있다. MessageRenderer

나 MessageProvider 인터페이스의 구현체를 바꾸려면 매번 코드를 바꿔야 하기 때문이다. 이 문

제를 해결하려면 프로퍼티 파일에서 구현체 클래스명을 읽고 애플리케이션을 대신해 인스턴스

를 생성해주는 간단한 팩터리 클래스를 만들어야 한다(예제 2-8 참고).

예제 2-8 MessageSupportFactory 클래스

package com.apress.prospring3.ch2;

import java.io.FileInputStream;

Page 68: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

40 l 프로 스프링 3

import java.util.Properties;

public class MessageSupportFactory {

private static MessageSupportFactory instance = null;

private Properties props = null;

private MessageRenderer renderer = null;

private MessageProvider provider = null;

private MessageSupportFactory() {

props = new Properties();

try {

props.load(new FileInputStream(“ch2/src/conf/msf.properties”));

// 구현체 클래스를 가져옴

String rendererClass = props.getProperty(“renderer.class”);

String providerClass = props.getProperty(“provider.class”);

renderer = (MessageRenderer) Class.forName(rendererClass).newInstance();

provider = (MessageProvider) Class.forName(providerClass).newInstance();

} catch (Exception ex) {

ex.printStackTrace();

}

}

static {

instance = new MessageSupportFactory();

}

public static MessageSupportFactory getInstance() {

return instance;

}

public MessageRenderer getMessageRenderer() {

return renderer;

}

public MessageProvider getMessageProvider() {

return provider;

}

}

이 구현체는 아주 간단하고 기초적이며, 에러 처리도 단순하고 설정 파일도 하드코딩돼 있지만

이미 코드의 양이 많으므로 이 부분은 건너뛰자. 이 클래스의 설정 파일은 아주 간단하다.

renderer.class=com.apress.prospring3.ch2.StandardOutMessageRenderer

provider.class=com.apress.prospring3.ch2.HelloWorldMessageProvider

Page 69: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 41

이제 main() 메서드를 (예제 2-9에 보이는 것처럼) 간단히 수정하면 다시 이전처럼 쓸 수 있다.

예제 2-9 MessageSupportFactory 사용하기

package com.apress.prospring3.ch2;

public class HelloWorldDecoupledWithFactory {

public static void main(String[] args) {

MessageRenderer mr = MessageSupportFactory.getInstance().getMessageRenderer();

MessageProvider mp = MessageSupportFactory.getInstance().getMessageProvider();

mr.setMessageProvider(mp);

mr.render();

}

}

이어서 스프링을 애플리케이션에 도입하기 전에 지금까지 한 일을 빠르게 정리해보자. 먼저

‘Hello World!’ 애플리케이션부터 시작해 애플리케이션이 충족해야 하는 두 가지 추가 조건

을 정의했다. 첫 번째 조건은 메시지 변경을 간단히 할 수 있어야 한다는 것이었고, 두 번째 조

건은 렌더링 메커니즘 또한 간단히 변경할 수 있어야 한다는 것이었다. 이러한 요구 조건을 충

족하기 위해 앞에서는 MessageProvider와 MessageRenderer라는 두 인터페이스를 도입했다.

MessageRenderer는 메시지를 조회해 렌더러에게 가져다줄 수 있는 MessageProvider 인터페이

스의 구현체에 의존한다. 끝으로 간단한 팩터리 클래스를 추가해 구현체 클래스의 이름을 읽어

인스턴스를 생성하게 했다.

STS에서의 스프링 프로젝트 생성

스프링을 사용해 ‘Hello World!’ 애플리케이션을 어떻게 리팩터링할지 설명하기 전에 STS를 사

용해 스프링 기반의 애플리케이션을 개발하는 법부터 살펴보자. STS는 여러 스프링 프로젝트를

개발할 때 많은 시간을 절약해준다(예를 들어 간단한 스프링 DI 애플리케이션, 웹 애플리케이

션, JPA 애플리케이션 등). 또 STS는 필요한 의존성을 모두 선언해 프로젝트를 만들어준다. 그럼

먼저 STS를 내려받고 설치한 후 ‘Hello World!’ 애플리케이션을 리팩터링할 수 있게 프로젝트를

만들어 보자.

먼저 스프링소스 웹사이트(www.springsource.com/downloads/sts)에서 STS를 내려받고 개

발 환경을 선택한 후 설치한다. 이 책을 쓰고 있는 현 시점 기준으로 버전은 최신 버전은 2.8.1이

다. 이 버전은 예제 애플리케이션뿐 아니라 이 책에서 소개한 예제 코드를 개발하는 데 사용한

버전과 같은 버전이다.

Page 70: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

42 l 프로 스프링 3

설치가 끝나면 STS를 시작하고 새로운 스프링 템플릿 프로젝트를 생성한다. 템플릿으로는

Simple Spring Utility Project(그림 2-5 참고)를 선택한다.

그림 2-5 STS에서 간단한 스프링 유틸리티 프로젝트를 생성한다.

그럼 STS는 프로젝트 이름, 기본 패키지 이름 같은 기본 프로젝트 정보를 묻는다(그림 2-6 참고).

그런 다음 STS는 메이븐 의존성 지원 기능을 활성화해 프로젝트를 생성한다. 그럼 필요한 의

존성(spring-context, spring-test, log4j 등)이 모두 선언되고 자동으로 포함된다. 즉 이 프로젝트

에는 스프링 라이브러리를 직접 포함시키지 않아도 된다.

이제 앞에서 본 ‘Hello World!’ 애플리케이션을 스프링으로 리팩터링할 준비가 모두 끝났다.

스프링을 통한 리팩터링

앞에서 본 마지막 예제는 예제 애플리케이션에 정의한 요구 조건을 충족했지만 여전히 몇 가

지 문제가 있다. 첫 번째 문제는 컴포넌트의 느슨한 연결 구조를 유지하려다 보니 애플리

Page 71: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 43

케이션의 각 부분을 연결하는 접착 코드를 많이 작성해야 한다는 점이다. 두 번째 문제는

MessageRenderer 구현체는 여전히 MessageProvider 인스턴스를 통해 직접 제공해야 한다는

점이다. 이 두 문제는 스프링을 사용해 해결할 수 있다.

그림 2-6 스프링 프로젝트의 기본 속성 지정

지나치게 많은 접착 코드 문제를 해결하려면 MessageSupportFactory 클래스를 애플리케이

션에서 완전히 제거하고 이를 스프링 인터페이스인 ApplicationContext로 대체하면 된다. 이 인

터페이스에 대해서는 아직 크게 걱정하지 않아도 된다. 지금은 스프링이 이 인터페이스를 사용

해 스프링에서 관리하는 애플리케이션에 대한 환경 정보를 모두 저장한다고만 알고 있으면 된다.

이 인터페이스는 또 다른 인터페이스인 ListableBeanFactory를 상속하는데, 이 인터페이스는 스

프링 관리 빈 인스턴스의 제공자 역할을 한다(예제 2-10 참고).

예제 2-10 스프링의 ApplicationContext 활용

package com.apress.prospring3.ch2;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloWorldSpringDI {

Page 72: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

44 l 프로 스프링 3

public static void main(String[] args) {

// 스프링 ApplicationContext 초기화

ApplicationContext ctx = new ClassPathXmlApplicationContext

("META-INF/spring/app-context.xml");

MessageRenderer mr = ctx.getBean("renderer", MessageRenderer.class);

mr.render();

}

}

예제 2-10에서는 main() 메서드가 ApplicationContext 타입의 ClassPathXmlApplication

Context 인스턴스를 가져오고 이를 토대로 ApplicationContext.getBean() 메서드를 호출해

MessageRenderer 인스턴스를 가져오는 것을 볼 수 있다(애플리케이션 설정 정보는 프로젝트 클

래스패스의 META-INF/spring/app-context.xml 파일에서 로드한다). 지금은 getBean() 메서

드에 대해 크게 걱정하지 않아도 된다. 다만 이 메서드가 애플리케이션 설정(여기서는 XML 파

일)을 읽고 스프링의 ApplicationContext 환경을 초기화하며, 설정 빈 인스턴스를 반환한다고

만 알아두자. 이 XML 파일(app-context.xml)은 앞서 사용한 MessageSupportFactory와 동일

한 역할을 한다(예제 2-11 참고).

예제 2-11 스프링 XML 애플리케이션 설정

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance

xmlns:p="http://www.springframework.org/schema/p"

xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<bean id="provider" class="com.apress.prospring3.ch2.HelloWorldMessageProvider"/>

<bean id="renderer" class="com.apress.prospring3.ch2.StandardOutMessageRenderer"

p:messageProvider-ref="provider"/>

</beans>

이 파일은 전형적인 스프링 ApplicationContext 설정 파일을 보여준다. 먼저 스프링의 네임스

페이스가 선언돼 있다. 기본 네임스페이스는 beans이고, 이 네임스페이스는 스프링에서 관리하

Page 73: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

2장 스프링 시작하기 l 45

는 빈과 의존성 조건(앞의 예제에서 renderer 빈의 messageProvider 프로퍼티는 provider 빈을

참조한다)을 선언해 스프링이 이들 의존성을 리졸브하고 주입하게 하는 데 사용된다.

그런 다음 ‘provider’ ID를 사용해 대응되는 구현체 클래스와 함께 빈을 선언한다. 스프링이

ApplicationContext를 초기화하는 과정에서 이런 빈 정의를 보면 스프링은 클래스 인스턴스를

생성하고 이를 지정된 ID를 사용해 저장한다.

그런 다음 ‘renderer’ 빈을 대응되는 구현체 클래스와 함께 선언한다. 이 빈은 Message

Provider 인터페이스에 의존해 메시지를 렌더러에게 전달한다는 점을 기억하자. 스프링에게 이

런 DI 정보를 알려줄 때는 p 네임스페이스 속성을 사용한다. p:messageProvider-ref="provider"

태그 속성은 messageProvider 빈 프로퍼티를 또 다른 빈과 함께 주입해야 한다는 사실을 스프

링에게 알려준다. 프로퍼티에 주입할 빈은 ‘provider’ ID를 사용해 빈을 참조해야 한다. 스프링이

이 정의를 보면 스프링은 클래스 인스턴스를 생성하고 빈의 프로퍼티에서 messageProvider라는

프로퍼티를 찾은 후 ID가 ‘provider’인 빈 인스턴스와 함께 인스턴스를 주입한다.

이와 같이 스프링의 ApplicationContext 초기화가 끝나면 main() 메서드는 타입 안전한

getBean() 메서드를 사용해 (ID와 예상되는 반환 타입인 MessageRenderer 인터페이스를 넘겨)

MessageRenderer 빈을 그냥 가져오고 render();를 호출한다. 이때 스프링은 MessageProvider

구현체를 생성한 후 이를 이미 MessageRenderer 구현체에 집어넣은 상태다. 이처럼 스프링을 사

용하면 서로 연결되는 클래스를 전혀 수정하지 않아도 된다는 사실을 알 수 있다. 사실 이들 클

래스는 스프링에 대한 참조도 갖고 있지 않으며 스프링에 대해 전혀 알고 있지도 않다. 하지만 항

상 이런 것은 아니다. 경우에 따라서는 커스텀 클래스가 스프링 관련 인터페이스를 구현해 DI 컨

테이너와 다양한 방식으로 상호작용할 수도 있다.

이제 스프링의 DI를 활용한 ‘Hello World!’ 애플리케이션을 실행해 보자. STS에서 HelloWorld

SpringDI 클래스를 대상으로 마우스 오른쪽 버튼을 클릭하고 Run As와 Java Application을 선

택한다. 그럼 실행 결과가 콘솔 탭에 나타난다(그림 2-7 참고).

그림 2-7 HelloWorldSpringDI의 실행 결과

Page 74: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

46 l 프로 스프링 3

정리

이 장에서는 스프링을 처음 시작할 때 알아야 할 배경 지식을 배웠다. 먼저 스프링 배포판을 내

려받는 법과 깃허브에서 직접 현재까지 개발된 소스코드를 내려받는 법을 배웠다. 또 스프링이

어떻게 패키징돼 있고 각 스프링 기능과 관련해 필요한 의존성을 살펴봤다. 이 정보를 활용해 독

자들은 애플리케이션에 필요한 스프링 JAR 파일을 현명하게 선택할 수 있으며 애플리케이션을

배포할 때 꼭 필요한 의존성만을 포함시킬 수 있게 됐다. 스프링의 문서, 예제 애플리케이션, 테

스트 스위트는 스프링 사용자가 이상적인 기반 스프링 개발을 시작할 수 있게 도와준다. 이 점을

고려해 이 장에서는 스프링 배포판에 들어 있는 내용도 자세히 살펴봤다. 끝으로 스프링 DI를 활

용해 전통적인 ‘Hello World!’ 애플리케이션을 리팩터링함으로써 결합도는 낮추고, 확장성은 높

인 메시지 렌더링 애플리케이션을 만드는 법을 살펴봤다.

중요한 점은 이 장에서는 스프링 DI를 개략적으로만 살펴봤다는 점이며, 아직 스프링을 제대

로 들여다보지 못했다는 점이다. 다음 장에서는 우리가 개발할 예제 애플리케이션을 자세히 들

여다본다. 아울러 특별히 스프링을 사용해 공통적인 설계 문제를 해결하는 법, 애플리케이션을

좀 더 단순하게 만드는 방법, 또 더 관리하기 쉽게 만드는 법에 주안점을 둔다.

Page 75: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

03예제 애플리케이션

이 책에 나온 각 장의 예제는 각 장의 주제에 맞춰져 있으며 이해하기 쉽게끔 각 기능을 구현하

도록 설계했다. 보통은 이들 예제만으로 각 장의 주제를 충분히 설명할 수 있다. 하지만 이와 별

개로 예제들은 독립적으로 제공되며 실제 시나리오를 기반으로 하지 않는다. 이로 인해 스프링

의 각기 다른 기능이 서로 어떻게 연동되는지 이해하는 게 조금 어려울 수 있다. 이런 문제를 해

결하기 위해 우리는 기본적인 블로그 애플리케이션인 SpringBlog를 개발해 책에서 설명한 대부

분의 주제를 강조하는 동시에 각기 다른 스프링의 기능이 어떻게 연동하는지 보여주려고 한다.

이 애플리케이션은 의도적으로 매우 단순하게 설계했으며, 사실 많은 기능이 스프링의 특정

기능을 강조하기 위해 고안했다는 점을 참고하자. 하지만 이런 단순함에도 불구하고 SpringBlog

애플리케이션은 스프링 기반의 애플리케이션이 어떻게 구성되고 어떻게 컴포넌트들이 서로 연

결되는지 잘 보여준다.

이 장에서는 완성된 SpringBlog 애플리케이션을 미리 살짝 들여다본다. 그런 다음 애플리케이

션의 각기 다른 영역을 구현하는 데 필요한 스프링 기능을 살펴본다. 또 이 장에서는 SpringBlog

를 설계하면서 내린 결정과 왜 이런 결정을 내렸는지도 함께 설명한다. 무엇보다도 이 장은 이 책

의 나머지 장에 대한 로드맵 역할을 하며, 독자들이 자신의 애플리케이션에서 중요한 요소를 쉽

게 알 수 있게 하고, 이 내용을 책의 어느 곳에서 다루고 있는지 보여준다.

이 장에서는 다음 두 주제를 다룬다.

Page 76: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

48 l 프로 스프링 3

▒ SpringBlog 애플리케이션의 요구 조건: 이 절에서는 SpringBlog 애플리케이션의 요구

조건을 다루고 이들 요구 조건을 충족한 결과물을 살짝 들여다본다. 또 예제 애플리케

이션을 만들면서 왜 특정 요구 조건을 포함시키고 왜 다른 요구 조건은 무시했는지도 설

명한다.

▒ SpringBlog 애플리케이션의 구현: 이 절에서는 앞 절에서 언급한 요구 조건을 스프링에

서 어떻게 구현해야 할지 개략적으로 살펴본다. 이 절에서는 스프링의 개별 기능을 상세

히 들여다보지 않는다. 대신 기능에 대해 전반적으로 설명하고 상세한 설명이 담겨 있는

장을 알려준다.

이미 스프링 애플리케이션 설계에 익숙하거나 자신의 애플리케이션에 어떤 주제가 가장 중요

한지 잘 알고 있다면 이 장을 건너뛰어도 된다. 스프링을 처음 접한다면 이 장을 통해 각기 다른

스프링 컴포넌트가 애플리케이션을 어떻게 구성하는지 명확히 이해할 수 있을 것이다.

SpringBlog 애플리케이션의 요구 조건

SpringBlog 애플리케이션의 요구 조건을 정의할 때 주된 목표는 완전한 애플리케이션을 통해 스

프링의 특정 프레임워크 기능을 강조하는 것이었다. 이런 이유로 이 예제에는 전통적인 블로그

애플리케이션에서는 잘 볼 수 없지만 있으면 유용하고, 스프링 프레임워크의 특정 기능을 잘 보

여줄 수 있는 오디팅과 비속어 필터링 같은 기능이 추가됐다.

이 절에서는 SpringBlog 애플리케이션에 포함된 전체 기능을 빠르게 살펴본다.

보안과 인증

대부분의 블로그 애플리케이션과 마찬가지로 SpringBlog 애플리케이션도 인증되지 않은 사용

자가 블로그 글을 쓰거나 편집할 수 없게 보안 제어 기능을 제공한다.

그림 3-1에서 볼 수 있듯 SpringBlog 애플리케이션은 사용자가 로그인해 애플리케이션의 실제

사용자로 인증될 수 있는 로그인 폼을 제공한다.

Page 77: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 49

그림 3-1 SpringBlog의 사용자 인증 기능

하지만 블로그 글을 볼 때는 로그인이 필요 없다. SpringBlog에서는 자동으로 익명 사용자 권

한을 부여한다. 로그인 기능을 사용하면 사용자의 상세 정보를 데이터베이스의 사용자 목록과

대조해 각기 다른 사용자 역할을 부여한다. 내부적으로 SpringBlog는 이 신원 정보를 보안 접근

제어에 사용하며, 오디팅 프로세스에서도 사용자 정보를 사용한다.

보안 제어 요구 사항은 다음과 같다.

▒ 익명 사용자는 블로그의 글을 볼 수만 있다.

▒ 애플리케이션에는 두 가지 역할만 있다. 바로 사용자(ROLE_USER)와 관리자(ROLE_ADMIN)다.

▒ 사용자 역할(ROLE_USER)을 갖고 있는 사용자는 다음 작업을 할 수 있다.

▒ 블로그 글을 올리거나 기존 블로그에 댓글 달기

▒ 블로그 글을 편집하거나 올린 댓글 수정하기

Page 78: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

50 l 프로 스프링 3

▒ 관리자 역할(ROLE_ADMIN)을 갖고 있는 사용자는 다음 작업을 할 수 있다.

▒ 오디팅 데이터 보기

▒ 사용자 관리 기능

블로그 글 보기

모든 블로그 시스템에 꼭 필요한 기능은 사용자에게 블로그 글을 보여주는 기능이다. 그림 3-2에

서 볼 수 있듯 SpringBlog 애플리케이션은 홈페이지에서 블로그의 모든 글을 시간 역순으로 보

여준다. 블로그의 글은 표 형태로 보여준다.

그림 3-2 최근 블로그 글 보기

사용자는 페이지당 보여주는 글 개수를 설정할 수 있다. 또 제목, 카테고리, 작성일 기준으로

블로그 글을 필터링할 수 있고, 블로그 글을 보여주는 순서도 조절할 수 있다.

Page 79: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 51

특정 블로그 글을 클릭하면 해당 글에 등록된 댓글 목록 및 첨부 파일과 함께 그림 3-3과 같이

블로그 글이 표시된다.

그림 3-3 블로그 글 보기

블로그 글 올리기

블로그 글을 올릴 수 있는 기능이 없다면 보여줄 내용도 없을 것이다. 유효한 사용자로 로그인하

고 나면 그림 3-4에 보이는 입력 양식을 사용해 새 블로그 글을 올릴 수 있다. 이 양식은 홈 페이

지에서 New Blog Posting 링크를 통해 접근할 수 있다.

블로그 글을 올리고 편집할 때는 세부 내용을 입력할 수 있고, 방문자가 보고 싶지 않은 글을

쉽게 필터링할 수 있게 카테고리와 하위 카테고리를 선택할 수 있다. 추가로 글을 올리는 동안 블

로그 글에 유효성 검증 규칙이 적용된다.

블로그 글을 작성했다면 글의 상세 페이지에 있는 Edit 링크를 클릭해 글을 편집할 수 있다(블

로그 글은 글을 작성한 사용자만 편집할 수 있다). 내부적으로 SpringBlog는 블로그 글을 쓸 때

와 편집할 때 모두 같은 HTML 폼을 사용하지만 각기 다른 스프링 컨트롤러 메서드를 사용해

각 동작을 처리한다.

Page 80: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

52 l 프로 스프링 3

그림 3-4 블로그 글 올리기

블로그 글에 댓글 달기

대부분의 블로그 애플리케이션과 마찬가지로 SpringBlog에서도 사용자가 댓글을 남겨 특정 블

로그 글에 대한 생각을 표현할 수 있다(댓글을 남길 때도 로그인이 필요하다). 사용자는 그림

3-5에 보이는 것처럼 글의 상세 페이지에 있는 ‘Post a comment’ 링크를 클릭해 댓글을 남길 수

있다.

글을 올리는 기능과 마찬가지로 댓글도 편집 기능을 제공한다.

비속어 필터링하기

SpringBlog에서 꼭 강조하고 싶은 스프링 프레임워크의 기능 중 하나는 바로 AOP였다. 하지만

우리는 전통적인 로깅 예제를 집어넣고 싶지 않았고, AOP 기반의 트랜잭션 관리는 이미 스프링

프레임워크에 들어 있는 만큼 적합하지 않았다. 물론 대부분의 블로그가 아무런 비속어 필터도

사용하지 않지만 이 블로그에서만큼은 이런 필터를 적용하기로 했다. 이 블로그를 설계할 때 우

리는 AOP를 사용해 애플리케이션 전반에 이 기능을 적용하는 게 가장 좋은 방법이라고 판단

했다.

Page 81: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 53

그림 3-5 댓글 남기기

이 기능을 적용한 후 그림 3-6에 보이는 것처럼 글을 입력하려고 하면 그림 3-7 같은 포스팅이

생긴다. 비속어 필터링을 수행할 때는 ROT13 알고리즘을 사용했고 이에 대해서는 21장에서 자

세히 설명한다.

그림 3-6 비속어를 올리려고 시도

Page 82: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

54 l 프로 스프링 3

그림 3-7 비속어 필터 적용 결과

블로그 글이나 댓글에 파일 첨부하기

월드 와이드 웹에서 사용되는 많은 블로그 애플리케이션과 달리 SpringBlog에서는 블로그 글과

댓글에 파일을 업로드할 수 있다. 사실 이 기능은 매우 큰 보안 위협 요소가 될 수 있지만 여기서

는 스프링 프레임워크의 훌륭한 파일 업로드 처리 방식을 보여주기 위해 이 기능을 도입했다. 그

림 3-8은 기존 블로그에 파일을 업로드하는 모습을 보여준다.

그림 3-8 SpringBlog에 파일 업로드하기

Page 83: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 55

블로그 행동 오디팅하기

순전히 특정 스프링 기능을 보여주기 위해 추가한 기능 하나는 바로 오디팅이다. 오디팅 기능을

위해 모든 블로그 작업에 로그 기능을 도입함에 따라 개별 블로그 작업에는 여러 개의 데이터베

이스 연산이 필요해졌다. 이로 인해 데이터베이스 트랜잭션을 사용해야 했는데, 이런 트랜잭션은

당연히 스프링의 트랜잭션 관리 기능을 통해 관리한다.

블로그 상세 페이지에서 View Audit History 링크를 클릭하면 그림 3-9에 보이는 것처럼 블로

그 글의 오디팅 히스토리를 볼 수 있다. 앞서 말한 것처럼 관리자 역할을 갖는 사용자면 이 링크

를 보고 오디팅 히스토리를 볼 수 있다.

그림 3-9 오디팅 데이터 보기

RSS 피드

사이트를 정기적으로 방문하지 않고 일부 사용자는 자신이 만든 웹 애플리케이션이나 개인화

된 포털 사이트(예를 들어 iGoogle)를 통해 RSS 피드를 받고 싶어 한다. SpringBlog는 RESTful-

WS를 통해 블로그 글을 제공한다. 이 서비스로 인해 컨수머는 블로그 글을 XML이나 JSON 형

식으로 조회할 수 있다.

XML 파일로 블로그 업로드하기

어떤 사용자는 오프라인에서 블로그 글을 쓴 다음 SpringBlog으로 글을 보내고 싶어 한다.

SpringBlog는 이 서비스도 제공한다. 사용자는 자신의 블로그 글을 쓴 다음 지정된 태그를 사용

Page 84: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

56 l 프로 스프링 3

해 XML 파일로 저장하고 파일을 서버의 특정 위치에 업로드하면 된다. SpringBlog는 이 폴더를

주기적으로 폴링해 새 파일을 찾으면 이를 불러온다.

SpringBlog의 구현

스프링을 사용하면 좋은 점 중 하나는 전통적인 OOP 방식으로 애플리케이션을 설계하고 개발

하기가 훨씬 쉬워진다는 점이다. 더불어 최근의 성숙한 스프링 개발 환경과 광범위한 기능들은

JEE 애플리케이션을 든든하게 뒷받침해 준다. 스프링을 사용하면 필요에 따라 애플리케이션을

자유롭게 설계하고 각기 다른 컴포넌트를 연결하는 걱정을 스프링에게 맡겨두면 된다. 스프링은

테스트를 방해하고, 해결하기로 약속한 문제를 일부만 해결해주는 팩터리와 싱글턴 같은 그릇된

패턴을 사용해야 하는 필요를 없애준다.

이 절에서는 전체 설계를 개략적으로 살펴보고 SpringBlog 애플리케이션의 설계 결정과 해당

주제를 상세히 다루는 장을 함께 소개한다.

개발 툴과 의존성 관리

설계에 들어가기 전에 SpringBlog 애플리케이션을 어떻게 구현해야 할지 결정하자. 스프링 기

반의 애플리케이션을 개발할 때 가장 적합한 툴은 스프링소스 툴 스위트(STS)다. STS는 이클립

스 IDE, 스프링 IDE, 그 밖의 여러 유용한 툴(이클립스용 메이븐 플러그인, Mylyn, 이클립스용

AspectJ 개발 툴 등)이 한 패키지에 담겨 있다. 이미 개발 장비에 이클립스 버전이 설치돼 있다면

STS 이클립스 업데이트 사이트(www.springsource.com/downloads/sts)를 통해 STS 플러그인

을 설치하면 된다. SpringBlog 애플리케이션은 STS를 사용해 개발한다. STS에 대한 상세 내용

은 부록 A에서 볼 수 있다.

앞서 설명한 대로 전체 스프링 프레임워크는 많은 모듈로 구성되고, 각 모듈은 다른 자바 라이

브러리에 의존한다. 물론 개발자가 직접 필요한 스프링 모듈과 서드파티 라이브러리 의존성을 선

택해 프로젝트에 추가할 수도 있지만, 의존성 관리 툴을 사용해 이 작업을 처리하는 게 훨씬 간

편하다. STS는 인기 있는 의존성 관리 툴인 메이븐을 기본으로 지원한다. SpringBlog 애플리케

이션은 메이븐을 사용해 의존성과 빌드 생명주기를 관리한다. 버전 관리에는 깃을 사용한다. 책

의 페이지에서 예제 애플리케이션의 소스코드를 내려받는 방법 외에 깃허브(http://github.com)

에서 애플리케이션의 최신 버전을 체크아웃할 수도 있다.

Page 85: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 57

애플리케이션 설계

SpringBlog의 설계는 매우 간단하며, 각 티어는 구체적인 클래스 대신 인터페이스를 통해 정의

한다. 각 티어에서 해당 티어에 속한 인터페이스는 클라이언트 티어로 분류되는 다른 티어로 노

출하는 메서드만을 정의한다. SpringBlog를 설정하는 매개변수는 애플리케이션에 하드코딩하

지 않는다. 대신 각 티어의 인터페이스를 구현하는 클래스에 설정 메서드를 선언하고 스프링의

IoC 기반 설정 메커니즘을 사용해 설정 데이터를 주입한다. 21장에서는 SpringBlog를 구성하는

각기 다른 인터페이스에 대한 전체 설명과 스프링을 사용해 이들 인터페이스가 어떻게 연결되는

지, 또 인터페이스에 영향을 미치는 요소에 대해 상세히 나와 있다.

SpringBlog 애플리케이션은 데이터와 기능을 캡슐화하는 기본적인 도메인 객체 모델(DOM)

도 포함한다. 12장에서는 독자들이 기존 프로젝트에서 봤을 법한 다양한 DOM을 살펴보고

DOM으로 기능을 캡슐화할지 별도 서비스 객체로 캡슐화할지 결정할 때 고려할 사항들을 살펴

본다.

애플리케이션 설정 관리

SpringBlog 애플리케이션에서는 최신 스프링 프레임워크(이 책을 쓰고 있는 현 시점 기준으로

3.1 버전)를 DI 설정에 사용한다. 버전 3.0부터 스프링은 XML 파일과 자바 애노테이션을 통해

설정을 관리할 수 있게 지원한다.

SpringBlog에서는 두 가지 설정을 혼용한다. 먼저 인프라 설정(예를 들어 데이터 소스, 트랜잭

션 매니저 등)은 유지보수가 쉽게끔 다양한 XML 설정 파일에 정의한다. 또 주입할 빈이나 DI를

필요로 하는 빈은 자바 애노테이션을 사용해 DI 요구 조건을 표현한다. 두 번째로 트랜잭션 지원

기능 요구 사항도 자바 애노테이션을 사용해 정의한다. 그런 다음 스프링의 컴포넌트 스캔과 자

동 연결 기능을 사용해 이들 애노테이션이 적용된 클래스를 스캔해 빈 및 빈에 필요한 다양한 리

소스를 관리한다.

스프링 설정과 관련한 상세 내용은 4장과 5장에서 다룬다.

Page 86: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

58 l 프로 스프링 3

SpringBlog의 계층화된 애플리케이션 아키텍처

각 레이어의 구현으로 들어가기 전에 SpringBlog에서 구현할 레이어를 개략적으로 살펴보자. 그

림 3-10에는 SpringBlog 애플리케이션의 계층화된 아키텍처의 모습이 나오고, 아래 목록에는

SpringBlog 애플리케이션 내에서 각 레이어가 하는 일을 (백엔드부터 프론트엔드 순으로) 설명

하고 있다.

· 영속성 레이어 : 이 레이어는 내부 영속성 데이터 저장소(이 경우 RDBMS)와 직접 상

호작용하고 조회한 데이터를 서비스 레이어에서 사용할 수 있게 자바 도메인 객체로

변형한다. 백엔드 데이터베이스로는 배포하기 쉬운 임베디드 H2 데이터베이스(www.

h2database.com)를 사용한다. 하지만 MySQL도 함께 지원하며 MySQL을 백엔드 데이

터베이스로 사용할 경우에 대비해 예제 애플리케이션에서는 이에 대한 설명을 제공하

고 있다.

· 서비스 레이어: 이 레이어는 애플리케이션 내의 핵심 레이어다. 모든 비즈니스 로직은 이

레이어에서 구현한다. 어떤 채널에서 오든 상관없이 모든 애플리케이션 서비스 요청(브

라우저 인터페이스, RESTful-WS 요청, 배치 작업 등)은 이 레이어를 통과한 후 요청한

비즈니스 처리를 수행한다. 예를 들어 빈 속성의 유효성 검증도 여기서 수행한다. 더불

어 이 레이어는 DI를 통해 영속성 레이어에 의존해 데이터베이스에 접근한다.

· 배치 작업 및 연동 레이어: 이 레이어는 외부 연동 기능을 제공한다. 예를 들어 사용자

의 블로그 글을 담고 있는 XML 파일을 (설정 가능한 폴더 위치에서) 폴링하고 배치 처

리를 통해 SpringBlog로 불러온다. 이 레이어는 서비스 레이어와 연동해 블로그 글을

업로드한다.

· 프레젠테이션 레이어: 이 레이어는 SpringBlog 사용자에게 프론트엔드를 제공하는 웹

애플리케이션용 레이어다. 이 레이어는 RSS 피드를 처리해 컨수머에게 XML 데이터를

생성해준다. 또 서비스 레이어와 상호작용해 데이터를 처리하고 비즈니스 로직을 수행

한다.

· 보안 레이어: 보안 레이어는 보안 리소스를 보호한다. 예를 들어 익명 사용자는 블로그

글을 볼 권한만 있다. 또 로그인 기능을 제공하고 사용자가 자신의 역할에 맞는 기능에

만 접근할 수 있게 사용자에게 역할 기반 접근 권한을 부여한다.

Page 87: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 59

프레젠테이션 및 보안 레이어 서비스 레이어 영속성 레이어

스프링 MVCJSPX제이쿼리RESTful-WS

스프링 시큐리티

브라우저 클라이언트

RSS 피드(XML/JSON)

스프링(IoC, AOP, TX, 유효성 검증)

스프링 배치

스프링 인티그레이션

XML 파일 애플리케이션 클라이언트

하이버네이트 JPA, 마이바티스

배치 및 연동 레이어

DB

그림 3-10 SpringBlog 애플리케이션의 계층화된 아키텍처

이제 계층화된 아키텍처에 대해 설명했으니 SpringBlog 애플리케이션에서 각 레이어를 어떻

게 구현할지 살펴보자.

영속성 레이어의 구현

데이터 접근은 많은 개발자들이 피부로 느끼는 주제이며, 인터넷 개발자 포럼에서 열띤 토론의

주제이기도 하다. 최근 자바 세계에서는 객체-관계 매핑(ORM) 툴로 JDBC를 대체하는 데 초점

이 맞춰져 있었지만, 많은 사람들은 이를 지나치게 복잡하다고 생각한다. 스프링 기능에서는 데

이터 접근 기술 지원 기능이 큰 부분을 차지한다. 전반적으로 스프링은 다양한 데이터 접근 메커

니즘(일반 JDBC, 하이버네이트, 마이바티스(아이바티스), 자바 데이터 객체(JDO), JPA)을 지원

한다.

SpringBlog 애플리케이션에서는 데이터 접근 구현체를 바꾸는 게 얼마나 쉬운지 보여주려고

했다. 따라서 여기서는 JPA(하이버네이트를 내부 영속성 서비스 제공자로 사용)를 사용한 구현

체와 마이바이티를 사용한 구현체를 통해 각기 다른 두 구현체로 영속성 레이어를 개발했다.

하이버네이트는 아마도 자바 세계에서 가장 유명한 ORM 툴일 것이다. 하이버네이트는 고성

능 영속성 로직을 손쉽게 개발할 수 있다는 점에서 큰 성공을 거뒀다. 물론 하이버네이트의 API

가 매우 단순하기는 하지만 하이버네이트를 사용할 때의 에러 처리 코드는 매우 길다. 스프링은

Page 88: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

60 l 프로 스프링 3

대부분의 하이버네이트 작업을 한두 줄의 코드로 줄임으로써 이 작업을 크게 단순화해줬다. 더

불어 하이버네이트 같은 ORM 툴이 큰 성공을 거두고 개발자들이 데이터에 접근할 때 하이버

네이트를 폭넓게 사용함에 따라 JCP는 이 기술을 JEE의 기술 스택에 표준으로 포함시켰다. EJB

3.0부터 엔티티 빈 명세는 자바 영속성 API(JPA)로 바뀌었다. 현재 버전이 2.0인 JPA는 JEE 6 표

준의 일부로서, 인기 있는 ORM 프레임워크에서 많은 개념을 도입했다. JPA는 자바 애플리케이

션이 RDBMS에 접근할 때 사용할 객체-관계 매핑을 위한 공통 ORM 패턴을 정의하면서 구현체

(영속성 제공자라고 부름)는 오픈소스 커뮤니티와 상업 벤더들에게 그대로 맡겨뒀다. 인기 있는

JPA 영속성 제공자로는 하이버네이트, 이클립스링크, OpenJPA가 있다. 9장에서는 스프링의 하

이버네이트 지원 기능을 설명하고, 10장에서는 스프링의 JPA 지원 기능을 살펴본다. 이들 두 장

은 하이버네이트를 영속성 서비스 제공자로 사용해 스프링과 JPA 2를 통해 데이터 접근 티어를

구현하는 법을 보여준다. 스프링과 JPA 및 하이버네이트를 다룰 때는 스프링 데이터 JPA를 사용

해 블로그 글과 댓글을 남기는 사용자 로깅을 구현하는 법, 하이버네이트의 Envers(엔티티 버전

시스템)를 사용해 오디팅 로그 기능을 구현하는 법도 함께 살펴본다.

엄밀히 말해 마이바티스는 ORM 툴이 아니며 개발자가 자바 객체를 RDBMS에 있는 데이터

로 매핑하는 데 필요한 SQL 구문을 만들지 않아도 되게끔 해주는 DataMapper 프레임워크다.

마이바티스는 SQL 맵 개념을 사용해 개발자가 다양한 SQL 쿼리를 지정하고 이들 쿼리가 입력

매개변수와 출력 매개변수로 어떻게 매핑될지 정의할 수 있게 해준다. 마이바티스는 일부 데이터

접근 측면에서 매우 강력하다. 11장에서는 마이바티스를 자세히 살펴보고 두 번째 데이터 접근

구현체를 개발하는 법을 보여준다.

이 책에서는 각 데이터 접근 툴에 항상 스프링의 인프라스트럭처 클래스를 사용한다. 이들 클

래스는 스프링의 트랜잭션 아키텍처와 연동한다. 이로 인해 트랜잭션은 플랫폼이나 리소스 제공

자와 상관없이 SpringBlog 애플리케이션의 서비스 레이어에서 관리할 수 있다.

서비스 레이어의 구현

SpringBlog 애플리케이션은 매우 간단하며 블로그 데이터의 기본 저장과 조회를 제외하면 이

시스템에 비즈니스 규칙이 거의 없다. 하지만 SpringBlog 애플리케이션에는 가장 흥미로운 스프

링 기능 두 개를 활용하는 두 가지 비즈니스 기능이 있다. 바로 AOP 기반 비속어 필터링과 오디

팅 로그다.

Page 89: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 61

AOP를 활용한 비속어 필터링

AOP는 현재 자바 세계에서 뜨거운 주제이며, 그 결과 자바 개발자들은 다양한 AOP 구현체를

사용할 수 있게 됐다. 스프링 AOP 지원 기능은 두 가지 형태로 제공된다. 바로 스프링의 네이티

브 AOP 프레임워크 지원 기능과 AspectJ AOP 프레임워크와의 연동 기능이다. 6장과 7장에서는

스프링 AOP와 AspectJ 연동에 대해 자세히 살펴본다.

SpringBlog 애플리케이션에서는 전통적인(지루한) 로깅 예제보다는 실제 AOP를 활용한 예제

를 제공하려고 했다. 예제 애플리케이션에서 제공하는 기능 중 하나는 비속어 필터링이었고, 설

계 당시 우리는 AOP를 활용해 비속어 필터링을 처리하는 게 가장 적합하다고 판단했다. 21장에

서는 비속어 필터를 개발하는 법을 보여주고 AOP를 사용해 선택적으로 관련 메서드에만 필터

를 적용하는 법을 보여준다.

스프링 트랜잭션 지원 기능의 활용

개발자들이 느끼기에 스프링에서 가장 인상적인 기능 중 하나는 바로 트랜잭션 지원 기능이다.

스프링의 트랜잭션 지원 기능은 프로그래밍적으로 또는 선언적으로 하나 이상의 리소스 제공자

사이에서 트랜잭션을 간단히 관리할 수 있게 해준다. 13장에서는 로컬 트랜잭션과 분산 트랜잭

션을 사용해 데이터베이스 트랜잭션을 중점적으로 트랜잭션 프레임워크를 살펴본다.

SpringBlog 애플리케이션에서는 블로그의 모든 작업을 오디팅하고 데이터베이스 로그로 남기

도록 정의했다. 오디팅 과정이 실패하면 작업을 롤백할 수 있게끔 여기서는 스프링 트랜잭션 프

레임워크를 사용해 각 작업과 오디팅 절차를 하나의 트랜잭션으로 캡슐화했다. 이에 대해서는

13장에서 자세히 설명한다.

빈 유효성 검증

개발자들이 바라는 것 중 하나는 데이터가 어디에서 오든 상관없이(예를 들어 사용자가 웹 애플

리케이션에서 정보를 입력하든, 배치 처리 중 XML 파일로부터 정보가 들어오든) 모든 유효성 검

증 규칙을 적용하고 데이터와 비교할 수 있는 중앙 집중형 유효성 검증 엔진이다. 이런 유효성 검

증 규칙은 한 번만 정의하고 아무 레이어에서나 유효성 검증이 필요할 때 사용할 수 있는 게 가장

이상적이다. 빈 유효성 검증 API(JSR-303)는 이런 역할을 하기 위해 나왔다. SpringBlog는 빈 유

효성 검증 API를 사용해 중앙 집중화된 유효성 검증을 한다. 스프링의 빈 유효성 검증 API 지원

기능은 13장에서 살펴본다.

Page 90: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

62 l 프로 스프링 3

SpringBlog에서는 빈 유효성 검증 API 구현체로 인기 있는 하이버네이트 밸리데이터(http://

hibernate.org/subprojects/validator)를 사용한다.

두 가지 다른 서비스 레이어 구현체

전통적으로 데이터 접근 레이어에서 개발자는 데이터 접근 객체(DAO)를 구현해 데이터 접근 로

직을 비즈니스 로직과 분리한다. 이들 DAO는 서비스 레이어 안에 들어 있는 객체에 주입한다.

이렇게 하는 이유는 한 데이터 접근 구현체를 다른 구현체로 쉽게 바꿀 수 있게 하기 위해서다.

하지만 DAO 패턴은 서비스 레이어와 백엔드 데이터베이스 사이에 또 다른 레이어를 추가로 도

입하며, 이런 방식은 대부분의 상황에서 꽤 번거롭다. 더불어 JPA 같은 표준을 사용하면 JPA에

서 정의한 표준 API를 사용해 데이터 접근에 사용하는 서비스 레이어에 영속성 컨텍스트를 바

로 주입할 수 있다. 이렇게 하면 한 JPA 영속성 제공자에서 다른 제공자(예를 들어 하이버네이트

에서 이클립스링크로)로 쉽게 바꿀 수 있다. 그 결과 오늘날 대부분의 애플리케이션은 DAO를

사용하지 않으며, 모든 비즈니스 및 데이터 접근 로직이 서비스 레이어에 캡슐화돼 있다.

SpringBlog 애플리케이션에서 서비스 레이어에는 이 레이어가 제공할 수 있는 비즈니스 서비

스를 반영하는 다양한 인터페이스가 들어 있다. 앞서 언급한 대로 여기서는 각기 다른 영속성 기

술을 사용해 두 가지 다른 버전의 서비스 구현체를 제공한다. 그 중 하나는 JPA와 하이버네이트

를 사용하고, 다른 구현체는 마이바티스를 사용한다.

또 이 책에서는 스프링 3.1의 프로필이라는 새로운 설정 기능을 사용해 애플리케이션에 어떤

서비스 구현체를 사용할지 지정하는 법도 살펴본다. 설정과 프로필은 5장에서 설명한다.

배치 및 연동 레이어의 구현

대부분의 애플리케이션은 배치 방식이든, 실시간 기반이든 다른 시스템과 연동해 정보를 교환한

다. 스프링 배치와 스프링 인티그레이션이라는 두 개의 스프링 프로젝트를 함께 사용하면 배치

작업과 기본적인 엔터프라이즈 연동 패턴(EIP)을 구현한 강력한 표준 플랫폼을 제공할 수 있다.

여기서는 두 스프링 프로젝트를 사용해 XML 파일에서 블로그 글을 불러오는 배치 작업을 구

현한다. 또 스프링 인티그레이션의 파일 폴링 지원 기능을 사용해 새 파일이 특정 폴더에 올라올

때마다 배치 작업을 실행하게끔 한다. 스프링 배치와 스프링 인티그레이션에 대한 간단한 설명

은 20장에서 볼 수 있다.

Page 91: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

3장 예제 애플리케이션 l 63

프레젠테이션 레이어의 구현

스프링은 데이터 접근 기술 지원 기능뿐 아니라 다양한 웹 애플리케이션 프레임워크와 툴을 지

원하는 것으로도 유명하다. 17장과 18장에서는 스프링 MVC 프레임워크와 자바 서버 페이지

(JSP) 및 자바 서버 페이스(JSF)를 비롯해 스프링에서 지원하는 일반적인 뷰 기술에 대해 자세히

살펴본다.

스프링 MVC

현재 많은 웹 프레임워크가 나와 있고 이들 프레임워크는 각각 장단점이 있다. 이들 프레임워크

를 단일 프레임워크로 표준화하고 이 프레임워크가 성격과 비즈니스 목적이 각기 다른 웹 애플

리케이션의 요구 사항을 모두 충족해주리라 기대하는 것은 불가능에 가깝다.

SpringBlog에서는 좀 더 일반적이고 간단한 접근 방식을 사용해 웹 레이어를 구현한다. 여기

서는 MVC 패턴을 사용하고, 스프링 MVC를 프레임워크로 사용한다. 뷰 단에서는 표준 JSP 페

이지를 사용한다. Ajax 기능으로는 제이쿼리 자바스크립트 라이브러리를 사용한다. 더불어 스

프링 MVC의 폭넓은 RESTful-WS 지원 기능도 함께 사용한다. 이와 관련한 상세 내용은 17장에

서 다룬다.

타일즈 활용

대부분의 웹 애플리케이션에서는 새 요청을 처리하고 나면 화면의 일부만 변하고 헤더, 내비게이

션 바 같은 공통 요소는 그대로 남는다. 타일즈를 사용하면 타일즈라고 부르는 개별 영역으로 페

이지를 구성할 수 있으며, 이를 통해 공통 요소를 한 번만 정의하고 애플리케이션 전체에서 재사

용할 수 있다. 17장에서는 스프링 애플리케이션에서 타일즈를 사용하는 법을 살펴보고 타일즈

를 사용해 예제 애플리케이션을 개발하는 법을 보여준다.

RESTful-WS와 OXM

스프링이 제공하는 또 다른 기능으로 XML 형식의 RSS 피드가 있다. 이를 활용하려면 스프링

MVC의 RESTful-WS와 객체-관계 매핑(OXM) 지원 기능을 사용해야 한다. XML 마샬링과 언

마샬링으로는 캐스터(www.castor.org)를 사용한다. 스프링이 지원하는 다른 XML 바인딩 프레

임워크로는 JAXB, XStream, XMLBeans 등이 있다. 이 내용은 16장에서 다룬다.

Page 92: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

64 l 프로 스프링 3

보안 레이어의 구현

보안은 모든 애플리케이션, 특히 웹 애플리케이션에서 중요한 고려 사항이다. 적절한 보안 제어가

없다면 웹 애플리케이션은 웹 공격을 받아 중요한 비즈니스 데이터를 잃어버릴 수 있다.

스프링 시큐리티(과거 스프링을 위한 아시지 시큐리티 프레임워크)는 스프링 애플리케이션에

서 거의 기본 보안 프레임워크로 사용된다. 스프링 시큐리티는 선언적인 보안 접근 제어와 프로

그래밍적인 보안 접근 제어를 방대하게 지원하며 스프링과 긴밀하게 연동해 개발자들이 작성해

야 하는 코드를 매우 단순화한다. SpringBlog 애플리케이션에서는 스프링 시큐리티 3.1을 사용

해 웹 애플리케이션을 보호하고 허용된 사용자가 자신에게 부여된 역할에 따라 승인된 행동만

할 수 있게 한다. 스프링 시큐리티는 16장과 17장에서 다룬다.

사용자 정보와 역할은 SpringBlog 데이터베이스에 저장하는 만큼 이 예제에서는 스프링 시큐

리티가 정보를 조회하고 적절히 보안 처리를 할 수 있게 사용자 상세 정보 서비스도 구현한다.

정리

이 장에서는 이 책을 통해 계속해서 살펴볼 SpringBlog 애플리케이션에 대해 살펴보고 Spring

Blog의 다양한 기능과, 이들 기능이 어떻게 구현되는지, 또 관련 내용을 책의 어느 곳에서 찾아

볼 수 있는지 살펴봤다.

다음 장에서는 스프링 프레임워크의 코어에 대해 얘기한다(스프링의 제어 역전(IoC) 컨테이

너). 4장에서는 2장에서 다룬 예제를 확장하고 다양한 IoC의 종류 및 스프링에서 이러한 IoC를

어떻게 지원하는지 배운다.

Page 93: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

04스프링의 IoC 및 DI 소개

1장에서는 제어 역전(IoC)과 의존성 주입(DI)의 기본 원칙을 다뤘다. 보통은 두 용어를 서로 번

갈아 쓰는 것도 자주 볼 수 있지만 사실 DI는 IoC의 특수한 형태다. 이 장에서는 IoC와 DI를 훨

씬 더 자세히 들여다보며 두 개념 사이의 관계를 정립하고 스프링이 이를 어떻게 활용하는지 자

세히 살펴본다.

두 용어를 정의하고 스프링과 이들 사이의 관계를 살펴본 후에는 스프링의 DI 구현에 있어 핵

심이 되는 개념을 자세히 들여다본다. 이 장에서는 스프링 DI 구현체의 기본적인 내용만을 다룬

다. 스프링의 고급 DI 기능은 5장에서 살펴보고, DI를 애플리케이션 설계에 적용하는 법은 5장

과 12장에서 살펴본다. 좀 더 구체적으로 이 장에서는 다음 주제를 다룬다.

▒ 제어 역전 개념: 이 절에서는 의존성 주입과 의존성 룩업을 비롯해 다양한 IoC의 종류를

살펴본다. 이 절에서는 여러 IoC 접근 방식 사이의 차이를 살펴보고 각각의 장단점을 비

교한다.

▒ 스프링에서의 제어 역전: 이 절에서는 스프링에서 사용할 수 있는 IoC 기능과 이들 기능

이 어떻게 구현됐는지 살펴본다. 특히 이 절에서는 세터 주입, 생성자 주입, 메서드 주입

같은 스프링이 제공하는 의존성 주입을 살펴본다.

▒ 스프링에서의 의존성 주입: 이 절에서는 스프링 IoC 컨테이너의 구현체를 살펴본다.

빈 정의 및 DI 요구 조건과 관련해서 애플리케이션이 주로 사용하는 인터페이스는

BeanFactory다. 하지만 처음 일부 예제를 제외하면 이 장에서 제공하는 나머지 예제 코

드는 BeanFactory를 확장하고 엔터프라이즈 애플리케이션에서 필요한 강력한 기능을

제공하는 스프링의 ApplicationContext 인터페이스에 초점을 맞춘다. BeanFactory와

ApplicationContext의 주된 차이점은 이후 절에서 다룬다.

Page 94: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

66 l 프로 스프링 3

▒ 스프링 애플리케이션 컨텍스트 설정: 이 장의 마지막 절에서는 XML 기반의 설정과 자바

애노테이션 방식을 사용해 ApplicationContext 설정을 지정하는 법을 중점적으로 다룬

다. 이 절에서는 먼저 DI 설정에 대해서 얘기한 후 (스프링의 ApplicationContext의 일부

인) BeanFactory에서 제공하는 빈 상속, 생명주기 관리, 자동 연결 같은 부가 서비스를

살펴본다.

제어 역전과 의존성 주입

IoC, 즉 DI는 컴포넌트 의존성(종종 객체의 협력 객체(collaborator)라고 부름)을 제공하는 간단

한 메커니즘을 제공하고 생명주기를 통해 의존성을 관리하는 게 핵심이다. 특정 의존성을 필요

로 하는 컴포넌트는 의존 객체 또는 IoC의 경우 타깃이라고 부른다. 이는 컴포넌트가 의존성에 접

근할 수 있는 서비스 및 생명주기 동안 이들 의존성과 상호작용할 수 있는 서비스를 IoC가 제공한

다는 말을 좀 거창하게 표현한 것이다. 일반적으로 IoC는 두 개의 하위 분류로 나눌 수 있다. 바로

의존성 주입과 의존성 룩업이다. 이들 하위 분류는 다시 구체적인 IoC 서비스 구현체로 더 분류할

수 있다. 이런 정의를 감안하면 DI에 대해 얘기할 때는 항상 IoC를 가리키지만, IoC를 얘기할 때

는 항상 DI에 대해 얘기하는 게 아님을 알 수 있다(예를 들어 의존성 룩업은 IoC의 한 형태다).

제어 역전의 종류

독자들 중에는 왜 IoC에 각기 다른 두 종류가 있고 왜 이들 분류가 각기 다른 구현체로 더 쪼개

지는지 의아한 사람도 있을 것이다. 이 질문에 대한 명확한 해답은 없다. 물론 제어 역전 방식이

다르면 유연한 정도도 달라지지만, 우리가 생각하기에 IoC는 전통적인 생각과 새로운 생각을 혼

합한 것에 가깝다. 즉 두 개의 각기 다른 IoC 방식은 이런 복합적인 생각들을 나타내는 것이다.

의존성 룩업의 첫 인상은 전통적인 접근 방식에 더 가까우며 자바 프로그래머에게도 익숙한

내용이다. 의존성 주입은 새로운 접근 방식으로, 처음 볼 때는 비직관적인 것처럼 보일 수 있지만

실제로는 의존성 룩업보다 훨씬 유연하며 사용 효과도 크다.

의존성 룩업 방식의 IoC를 사용하면 컴포넌트가 의존성에 대한 참조를 가져와야 하지만, 의존

성 주입을 사용하면 IoC 컨테이너에 의해 컴포넌트에 의존성이 주입된다. 의존성 룩업에는 두 종

류가 있다. 의존성 풀과 컨텍스트화된 의존성 룩업(CDL)이다. 의존성 주입도 두 가지 방식으로

사용한다. 바로 생성자 의존성 주입과 세터 의존성 주입이다.

Page 95: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 67

알아두기

이 절의 설명에서는 어떻게 IoC 컨테이너가 각기 다른 의존성을 모두 알게 되는지 신경 쓰지

않아도 된다. 다만 특정 시점이 되면 IoC 컨테이너가 각 메커니즘에 기술된 행동을 수행한다

는 점만 알아두면 된다.

의존성 풀

자바 개발자에게 의존성 풀(Dependency Pull)은 가장 익숙한 IoC 타입이다. 의존성 풀을 사용

하면 필요할 때마다 레지스트리에서 의존성을 가져온다. EJB(2.1 이하 버전)에 접근하는 코드를

작성해 본 사람이라면 의존성 풀(JNDI API를 사용한 EJB 컴포넌트 룩업)을 사용해본 적이 있

을 것이다. 그림 4-1에는 의존성 풀을 룩업 메커니즘으로 사용하는 시나리오가 나와 있다.

컨테이너JNDI 레지스트리

룩업

의존 객체

그림 4-1 의존성 풀을 JNDI 룩업으로 사용

스프링은 프레임워크가 관리하는 컴포넌트를 조회하는 메커니즘으로 의존성 풀을 사용할 수

있게 해준다. 2장에서 이를 실제로 본 적이 있다. 예제 4-1은 전형적인 의존성 풀 룩업을 스프링

기반의 애플리케이션에서 사용하는 예제다.

예제 4-1 스프링의 의존성 풀

public static void main(String[] args) throws Exception {

// 빈 팩터리를 가져옴

BeanFactory factory = getBeanFactory();

MessageRenderer mr = (MessageRenderer) factory.getBean("renderer");

Page 96: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

68 l 프로 스프링 3

mr.render();

}

이런 방식의 IoC는 JNDI 룩업을 폭넓게 활용해 레지스트리에서 의존성을 가져오는 JEE 기반

애플리케이션(EJB 2.1 이하 버전)에서 자주 사용할 뿐 아니라 많은 환경에서 스프링을 사용하는

데도 꼭 필요하다.

컨텍스트화된 의존성 룩업

컨텍스트화된 의존성 룩업(CDL)은 어떤 면에서는 의존성 풀과 비슷하지만 CDL에서는 중앙 레

지스트리가 아니라 리소스를 관리하는 컨테이너를 대상으로 룩업을 수행하며, 보통 정해진 시점

에 룩업이 수행된다는 점이 다르다. 그림 4-2는 CDL 메커니즘을 보여준다.

컨테이너

룩업

의존 객체

그림 4-2 컨텍스트화된 의존성 룩업

CDL은 컴포넌트가 예제 4-2와 유사한 인터페이스를 구현하게 함으로써 동작한다.

예제 4-2 CDL에 사용할 컴포넌트 인터페이스

package com.apress.prospring3.ch4;

public interface ManagedComponent {

public void performLookup(Container container);

}

이 인터페이스를 구현하면 컴포넌트는 의존성이 필요하다는 신호를 컨테이너로 보낸다. 컨테

이너는 보통 내부 애플리케이션 서버(톰캣, JBoss 등)나 프레임워크(스프링)에서 제공한다. 예제

4-3은 의존성 룩업 서비스를 제공하는 간단한 Container 인터페이스다.

Page 97: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 69

예제 4-3 간단한 Container 인터페이스

package com.apress.prospring3.ch4;

public interface Container {

public Object getDependency(String key);

}

컨테이너가 컴포넌트로 의존성을 넘겨줄 준비가 되면 컨테이너는 각 컴포넌트에 대해 차례로

performLookup()을 호출한다. 컴포넌트는 예제 4-4에 나온 것처럼 Container 인터페이스를 사

용해 의존성을 룩업할 수 있다.

예제 4-4 CDL에서 의존성 가져오기

package com.apress.prospring3.ch4;

public class ContextualizedDependencyLookup implements ManagedComponent {

private Dependency dependency;

public void performLookup(Container container) {

this.dependency = (Dependency) container.getDependency("myDependency");

}

}

예제 4-4에서 Dependency는 비어 있는 클래스라는 점을 참고하자.

생성자 의존성 주입

생성자 의존성 주입은 컴포넌트의 의존성을 생성자(들)에 제공하는 의존성 주입 방식이다. 컴포

넌트는 의존성을 인자로 받는 생성자 또는 여러 생성자를 선언하고, IoC 컨테이너는 컴포넌트 인

스턴스를 생성할 때 컴포넌트로 의존성을 넘겨준다. 예제 4-5를 참고하자.

예제 4-5 생성자 의존성 주입

package com.apress.prospring3.ch4;

public class ConstructorInjection {

private Dependency dependency;

public ConstructorInjection(Dependency dependency) {

this.dependency = dependency;

}

}

Page 98: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

70 l 프로 스프링 3

세터 의존성 주입

세터 의존성 주입 방식을 사용하면 IoC 컨테이너가 자바빈 방식의 세터 메서드를 통해 컴포넌트

에 의존성을 주입해준다. 이때 컴포넌트의 세터는 IoC가 관리할 수 있는 의존성을 노출한다. 예

제 4-6은 전형적인 세터 의존성 주입 기반의 컴포넌트를 보여준다.

예제 4-6 세터 의존성 주입

package com.apress.prospring3.ch4;

public class SetterInjection {

private Dependency dependency;

public void setDependency(Dependency dependency) {

this.dependency = dependency;

}

}

컨테이너 내에서 setDependency() 메서드를 통해 노출한 의존성의 요구 조건은 자바빈 방식

의 명명 관례를 따라 dependency로 참조한다. 사실 세터 의존성 주입은 의존성 주입 방식으로

가장 많이 사용하며 IoC 메커니즘 중 가장 구현하기 쉬운 방식 중 하나다.

주입 vs. 룩업

어떤 IoC 방식(주입 또는 룩업)을 사용할지 선택하는 일은 보통 어렵지 않다. 많은 경우 현재 사

용 중인 컨테이너에 따라 IoC 방식이 결정된다. 예를 들어 EJB 2.1 이하 버전을 사용하고 있다면

(JNDI를 통한) 룩업 방식을 사용해 JEE 컨테이너에서 EJB를 가져와야 한다. 스프링에서는 초기

빈 룩업을 제외하면 컴포넌트 및 의존성이 항상 주입 방식의 IoC를 통해 자동으로 연결된다.

알아두기

스프링을 사용할 때도 명시적인 룩업을 수행하지 않고 EJB 리소스에 접근할 수 있다. 스프링

은 룩업과 주입 방식의 IoC 시스템 사이에서 어댑터 역할을 할 수 있으므로 모든 리소스를 주

입 방식을 사용해 관리할 수 있다.

실제 문제는 이것이다. 즉, 선택할 수 있는 상황에서 주입을 사용할지 룩업을 사용할지 결정해

야 하는 것이다. 이 질문에 대한 답은 당연히 주입이다. 예제 4-4와 4-5의 코드를 보면 주입을 사

Page 99: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 71

용하면 컴포넌트의 코드에 아무런 영향도 주지 않는 것을 볼 수 있다. 그에 반해 의존성 풀 방식

을 사용한 코드는 레지스트리에 대한 참조를 갖고 있어야 하며 레지스트리와 연동해 의존성을

가져와야 한다. 또 CDL을 사용할 때는 클래스가 특정 인터페이스를 구현해야 하고 모든 의존성

을 직접 가져와야 한다. 하지만 의존성 주입 방식을 사용하면 대부분의 클래스가 할 일은 생성자

나 세터를 통해 의존성을 주입받을 수 있게 하는 것뿐이다.

의존성 주입을 사용하면 클래스가 의존 객체를 협력 객체에게 제공하는 IoC 컨테이너로부터

완전히 분리될 수 있는 반면, 룩업을 사용하면 클래스가 항상 이들 클래스 및 컨테이너에서 정의

한 인터페이스에 의존하게 된다. 룩업의 또 다른 단점은 컨테이너와 별개로 클래스를 테스트하기

가 매우 어려워진다는 점이다. 하지만 주입을 사용하면 적절한 생성자나 세터를 통해 직접 의존

성을 주입해줄 수 있는 만큼 컴포넌트를 간단히 테스트할 수 있다.

알아두기

의존성 주입과 스프링을 활용해 컴포넌트를 테스트하는 내용은 19장에서 자세히 다룬다.

룩업 기반의 솔루션은 주입 기반의 솔루션보다 훨씬 복잡하다. 물론 이런 복잡성은 미미한 수

준이지만 문제는 의존성 관리처럼 애플리케이션에 핵심이 되는 프로세스에 불필요한 복잡성을

추가할 이유가 전혀 없다는 점이다.

이런 이유를 제쳐두더라도 룩업과 비교해 주입을 선택해야 하는 가장 큰 이유는 주입을 사용

하면 개발이 그만큼 편해지기 때문이다. 주입을 사용하면 작성해야 하는 코드가 훨씬 줄어들고,

작성하는 코드도 단순해지며, 좋은 IDE를 통해 자동화할 수도 있다. 주입 예제에 사용한 코드는

모두 특정 작업을 능동적으로 하려고 하지 않는다는 점에서 수동적임을 알 수 있다. 주입 코드에

서 가장 좋은 점은 객체가 필드에만 저장된다는 점이다. 레지스트리나 컨테이너에서 의존성을 가

져오는 코드는 전혀 없다. 따라서 코드가 훨씬 단순해지고 에러가 날 일도 그만큼 적다. 수동적

인 코드는 잘못될 일이 거의 없다는 점에서 능동적인 코드보다 유지보수가 쉽다. 예제 4-4에서

가져온 다음 코드를 살펴보자.

public void performLookup(Container container) {

this.dependency = (Dependency) container.getDependency("myDependency");

}

이 코드에서는 많은 부분이 잘못될 수 있다. 의존성 키가 바뀔 수도 있고, 컨테이너 인스턴스

가 널일 수도 있으며, 반환된 의존성이 잘못된 타입일 수도 있다. 이런 코드는 많은 부분이 잘못

Page 100: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

72 l 프로 스프링 3

될 여지가 많다는 점에서 리팩터링의 여지가 많다. 룩업을 사용하면 컴포넌트와 애플리케이션은

분리되지만 작업을 수행하기 위해 이들 컴포넌트를 다시 연결하느라 코드를 추가로 작성해야 하

고 복잡도가 증가한다.

세터 주입 vs. 생성자 주입

이제 어떤 IoC 방식을 더 선호해야 하는지 결론을 내렸지만 아직 세터 주입과 생성자 주입 사이

에서는 결정을 내리지 못했다. 생성자 주입은 컴포넌트를 사용하기 전에 의존성 클래스의 인스턴

스를 꼭 갖고 있어야 할 때 매우 유용하다. 스프링을 비롯한 많은 컨테이너가 세터 주입을 사용할

때 모든 의존성이 정의돼 있는지 확인할 수 있는 메커니즘을 제공하지만 생성자 주입을 사용하

면 컨테이너를 모르는 상태에서도 의존성에 대한 요구 조건을 지정할 수 있다.

세터 주입은 여러 상황에서 유용하다. 컴포넌트가 의존성을 컨테이너로 노출하지만 기

본 의존성을 제공한다면 보통 세터 주입이 가장 적합하다. 세터 주입을 사용할 때 또 다

른 장점은 의존성을 인터페이스로 선언할 수 있다는 점인데, 이 장점은 생각만큼 크지 않다.

de�neMeaningOfLife()라는 비즈니스 메서드 하나만 갖고 있는 전형적인 비즈니스 인터페이스

가 있다고 가정하자. 만일 이 메서드 외에 setEncylopedia() 같은 의존성 주입용 세터를 정의하

면, 결국 이 인터페이스의 모든 구현체가 encyclopedia 의존성을 사용하거나 최소한 알고 있어야

한다고 강제하게 된다. 하지만 이런 비즈니스 인터페이스에서는 setEncylopedia()를 정의하지 않

아도 된다. 대신 비즈니스 인터페이스를 구현하는 클래스에 메서드를 정의하면 된다. 이런 식으

로 프로그래밍하면 스프링을 비롯한 현대 IoC 컨테이너들은 비즈니스 인터페이스를 통해 컴포

넌트와 연동하면서도 구현체 클래스에게 의존성을 제공해줄 수 있다. 실제 예제를 보면 이 문제

를 좀 더 명확히 이해할 수 있을 것이다. 예제 4-7의 비즈니스 인터페이스를 살펴보자.

예제 4-7 Oracle 인터페이스

package com.apress.prospring3.ch4;

public interface Oracle {

public String defineMeaningOfLife();

}

이 비즈니스 인터페이스는 의존성 주입용 세터를 전혀 정의하지 않은 것을 볼 수 있다. 이 인터

페이스는 예제 4-8과 같이 구현할 수 있다.

예제 4-8 Oracle 인터페이스의 구현

package com.apress.prospring3.ch4;

Page 101: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 73

public class BookwormOracle implements Oracle {

private Encyclopedia encyclopedia;

public void setEncyclopedia(Encyclopedia encyclopedia) {

this. encyclopedia = encyclopedia;

}

public String defineMeaningOfLife() {

return "Encyclopedias are a waste of money - use the Internet";

}

}

이처럼 BookwormOracle 클래스는 Oracle 인터페이스를 구현할 뿐 아니라 의존성 주입용 세

터도 정의한다. 스프링은 이런 구조를 다루는 데 좀 더 익숙하다. 여기서는 비즈니스 인터페이스

에 의존성을 정의할 이유가 전혀 없다. 인터페이스를 사용해 의존성을 정의하는 능력이 세터 주

입 방식의 장점이라고 사람들이 자주 말하지만 실제로 세터는 꼭 필요한 경우와 DAO 인터페이

스를 주입하는 용도로만 사용하도록 노력해야 한다. 특정 비즈니스 인터페이스의 모든 구현체가

특정 의존성을 필요로 한다고 완전히 확신할 수 없다면 각 구현체 클래스가 자신의 의존성을 정

의하게 하고 비즈니스 인터페이스는 비즈니스 메서드만을 정의하게 해야 한다.

비즈니스 인터페이스의 의존성에 항상 세터를 두지는 않지만 비즈니스 인터페이스의 설정 파

라미터에 세터와 게터를 두고 세터 주입을 활용하는 방식은 권장할 만하다. 설정 파라미터는 특

별한 의존성의 사례로 볼 수 있다. 물론 컴포넌트는 이런 설정 데이터에 의존하지만, 설정 데이터

는 지금까지 살펴본 의존성 타입과는 사뭇 다르다. 이런 차이에 대해서는 잠시 후에 얘기하기로

하고 지금은 먼저 예제 4-9에 나온 비즈니스 인터페이스부터 살펴보자.

예제 4-9 NewsletterSender 인터페이스

package com.apress.prospring3.ch4;

public interface NewsletterSender {

public void setSmtpServer(String smtpServer);

public String getSmtpServer();

public void setFromAddress(String fromAddress);

public String getFromAddress();

public void send();

}

Page 102: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

74 l 프로 스프링 3

NewsletterSender 인터페이스는 이메일로 신문을 보내는 클래스가 구현한다. send() 메서드

는 이 인터페이스의 유일한 비즈니스 메서드이며, 이 인터페이스에서는 두 개의 자바빈 프로퍼티

를 정의한 것을 볼 수 있다. 방금 전만 해도 비즈니스 인터페이스에 의존성을 정의하지 말라고 해

놓고 여기서는 왜 이런 의존성을 정의하는 것일까? 그 이유는 이들 값(SMTP 서버 주소와 이메

일 발신 주소)은 실제로 의존적인 성격을 띠지 않고 NewsletterSender 인터페이스의 기능을 구

현한 모든 구현체에 영향을 미치는 설정 상세 정보를 담고 있기 때문이다. 스프링의 의존성 주입

기능은 이처럼 애플리케이션 컴포넌트의 설정을 외부화해주는 이상적인 솔루션이다. 이는 의존

성을 부여하는 게 아니라 컴포넌트 설정을 외부화하는 메커니즘이다. 그럼 여기서 질문이 하나

생길 수 있다. 도대체 설정 파라미터와 다른 의존성의 차이는 무엇일까? 보통은 어떤 의존성을

설정 파라미터로 분류할지 여부를 쉽게 파악할 수 있다. 하지만 좀처럼 확신이 들지 않는다면 설

정 파라미터를 가리키는 다음 세 가지 특징을 찾아보면 된다.

▒ 설정 파라미터는 수동적이다. 예제 4-8에서 보여준 NewsletterSender 예제에서 SMTP

서버 파라미터는 수동적인 의존성의 한 예다. 수동적인 의존성은 특정 행동을 직접 수행

하는 데 사용되지 않는다. 대신 내부적으로 사용되거나 다른 의존성이 특정 행동을 하는

데 사용된다. 2장의 MessageRenderer 예제에서 MessageProvider는 수동적이지 않았다.

MessageProvider는 MessageRenderer가 작업을 완료하는 데 필요한 행동을 했기 때문

이다.

▒ 설정 파라미터는 주로 정보이며, 다른 컴포넌트가 아니다. 이 말은 설정 파라미터

가 보통 컴포넌트가 작업을 완료하는 데 필요한 정보라는 뜻이다. 여기서 SMTP

서버는 NewsletterSender에서 필요로 하는 정보인데 반해, MessageProvider는

MessageRenderer가 제대로 동작하는 데 필요한 다른 컴포넌트였다.

▒ 설정 파라미터는 주로 단순 값 또는 단순 값의 컬렉션이다. 사실 이 점은 앞의 두 특성으

로 인한 부산물이며, 설정 파라미터는 보통 단순 값이 된다. 자바에서 이 말은 원시 타입

(또는 대응되는 래퍼 클래스), String, 이들 값의 컬렉션을 뜻한다. 단순 값은 보통 수동적

이다. 이 말은 예컨대 String으로는 안에 들어 있는 데이터를 수정하는 것 외에 별다른 일

을 할 수 없다는 뜻이다. 더불어 이런 값은 주로 정보로만 사용한다. 예컨대 네트워크 소

켓이 리스닝하는 포트 번호를 나타내는 int 값이나 이메일 프로그램이 메시지를 보낼 때

사용하는 STMP 서버 String 값 등이 그러한 예다.

비즈니스 인터페이스에 설정 옵션을 사용해야 할지 고려할 때는 설정 파라미터가 비즈니스 인

Page 103: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 75

터페이스의 모든 구현체에 적용되는지 또는 한 곳에만 적용되는지도 고려해야 한다. 예를 들어

NewsletterSender의 경우 모든 구현체가 이메일을 보내려면 당연히 SMTP 서버를 알고 있어야

한다. 하지만 보안 이메일을 보내는 설정 옵션이 있다면 이 비즈니스 인터페이스에서 제외하는

게 좋을 것이다. 모든 이메일 API가 이 기능을 지원하지는 않는 만큼 많은 구현체에서 보안을 전

혀 고려하지 않는다고 가정하는 게 더 정확하다.

알아두기

2장에서 비즈니스 목적에 따라 의존성을 정의한 것을 기억할 것이다. 이 예제는 예시를 보여

주기 위한 것이었을 뿐 모범 기법으로 보여주기 위한 예제가 아니었다.

세터 주입은 부모 컴포넌트의 새 인스턴스를 생성하지 않고 동적으로 각기 다른 구현체를 사

용해 의존성을 대체할 수 있게 해준다. 이는 스프링의 JMX 지원 기능 덕분에 가능하다. 아마도

세터 주입의 가장 큰 장점은 가장 덜 강압적인 주입 메커니즘일 것이다.

기본 생성자만 있던 클래스에 생성자 주입을 정의하면 비IoC 환경에서 해당 클래스를 사용하

는 모든 코드에 영향을 주게 된다. 하지만 IoC 용도로 클래스에 정의한 추가 세터는 다른 클래스

가 이 클래스와 상호작용하는 데 전혀 영향을 주지 않는다.

일반적으로 세터 기반의 주입 방식을 선택하는 게 가장 좋다. 세터 기반의 주입 방식은 비IoC

설정에서 코드를 사용하는 데 최소한의 영향만 주기 때문이다. 생성자 주입 방식은 컴포넌트로

항상 의존성이 전달되게 하려고 할 때 적합하지만 많은 컨테이너가 세터 주입 방식을 통해서도

이와 동일한 메커니즘을 제공한다는 점을 염두에 두자. 예제 애플리케이션에서 사용하는 코드

는 일부 생성자 주입을 사용한 예제가 있기는 하지만 대부분 세터 주입 방식을 사용한다.

스프링의 제어 역전

앞서 언급한 대로 제어 역전은 스프링이 하는 일 중 큰 부분을 차지하며, 스프링 구현체의 핵심

은 제어 역전을 기반으로 한다(물론 의존성 룩업 기능도 함께 제공하지만). 의존 객체에 협력 객

체를 자동으로 제공할 때 스프링은 의존성 주입을 사용해 이를 처리한다. 스프링 기반의 애플리

케이션에서는 의존성 룩업을 통해 의존 객체가 협력 객체를 가져오게 하는 대신 항상 의존성 주

입을 사용해 협력 객체를 의존 객체에 주입하는 게 좋다. 그림 4-3은 스프링의 의존성 주입 메커

니즘(의존성 룩업과 관련해서는 그림 4-2를 참고하자)을 보여준다.

Page 104: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

76 l 프로 스프링 3

의존 객체

의존성 주입

스프링 BeanFactory 컨테이너

그림 4-3 스프링의 의존성 주입 메커니즘

물론 의존성 주입 방식을 사용해 협력 객체와 의존 객체를 서로 연결하는 방식을 더 권장하

지만 의존성 룩업을 사용해 의존 객체에 접근해야 할 때도 있다. 많은 환경에서 스프링은 의존

성 주입을 사용해 모든 애플리케이션 컴포넌트를 자동 연결할 수 없으며, 초기 컴포넌트셋에

접근할 때는 항상 의존성 룩업을 사용해야 한다. 예를 들어 단독 실행형 자바 애플리케이션의

main() 메서드에서는 스프링 컨테이너를 부트스트랩하고 (ApplicationContext 인터페이스를

통해) 의존성을 가져와 처리해야 한다. 하지만 스프링의 MVC 지원 기능을 사용해 웹 애플리케

이션을 개발할 때는 전체 애플리케이션을 자동으로 연결함으로써 이 과정을 피할 수 있다. 스프

링에서 의존성 주입을 사용할 수 있을 때는 항상 의존성 주입을 사용하면 된다. 그렇지 않은 경

우에는 의존성 룩업 기능을 사용하면 된다. 이 둘을 활용하는 예제는 이 책을 통해 계속해서 살

펴볼 것이며, 각 기능이 처음 등장할 때마다 자세히 언급하겠다.

스프링 IoC 컨테이너의 재미있는 기능은 의존성 주입 컨테이너와 외부 의존성 룩업 컨테이너

사이에서 어댑터 역할을 할 수 있다는 점이다. 이 기능에 대해서는 이 장에서 나중에 자세히 설

명한다.

스프링은 생성자 주입과 세터 주입을 둘 다 지원하며 유용한 추가 기능을 통해 표준 IoC 기능

을 강화함으로써 개발을 더 쉽게 해준다.

이 장의 나머지 내용은 다양한 예제로 채워진 스프링 DI 컨테이너의 기본 설명을 다룬다.

스프링을 통한 의존성 주입

스프링의 의존성 주입 지원 기능은 매우 방대하며, 5장에서 보겠지만 지금까지 얘기한 표준 IoC

기능을 훨씬 넘어선다. 이 장의 나머지 내용은 스프링의 의존성 주입 컨테이너에 대한 기본 정보

와, 세터 주입, 생성자 주입, 메서드 주입을 다룬다. 더불어 스프링에서 의존성 주입을 어떻게 설

정하는지도 자세히 설명한다.

Page 105: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 77

빈 및 빈팩터리

스프링의 의존성 주입 컨테이너의 핵심은 BeanFactory 인터페이스다. BeanFactory는 의존성과

생명주기 등을 포함해 컴포넌트를 관리하는 책임을 진다. 스프링에서 빈이라는 용어는 컨테이너

가 관리하는 컴포넌트를 가리킨다. 보통 빈은 어느 정도 수준에서 자바빈 명세를 준수하지만, 이

사항이 꼭 필요한 것은 아니다. 특히 생성자 주입을 사용해 빈을 연결할 생각이라면 더욱 그렇다.

애플리케이션에서 DI 지원 기능만 필요하다면 BeanFactory 인터페이스를 통해 스프링 DI 컨

테이너와 직접 연동할 수 있다. 이 경우 애플리케이션은 BeanFactory 인터페이스를 구현하는 클

래스 인스턴스를 생성하고 이 인스턴스에 빈과 의존성 설정 정보를 지정해야 한다. 이렇게 하고

나면 애플리케이션은 BeanFactory를 통해 빈에 접근하고 빈을 사용해 필요한 처리를 할 수 있

다. 때로는 이런 설정이 자동으로 처리되기도 한다(예를 들어 웹 애플리케이션에서는 web.xml

디스크립터 파일에 선언한 스프링의 ContextLoaderListener 클래스에 의해 애플리케이션 시작

시점에 웹 컨테이너에 의해 스프링의 ApplicationContext가 부트스트랩된다). 하지만 많은 경우

직접 설정을 코딩해야 한다. 이 장의 예제를 따라 하려면 모두 BeanFactory 구현체를 직접 설정

해야 한다.

BeanFactory를 프로그래밍적으로 설정할 수도 있지만 이 설정은 설정 파일을 통해 외부에서

설정하는 방식을 더 많이 쓴다. 내부적으로 빈 설정은 BeanDe�nition 인터페이스를 구현하는 클

래스 인스턴스로 표현한다. 빈 설정은 빈 자체에 대한 정보뿐 아니라 빈이 의존하는 빈에 대한 정

보도 저장한다. BeanDefinitionRegistry 인터페이스도 함께 구현하는 BeanFactory 구현체 클

래스에서는 PropertiesBeanDe�nitionReader나 XmlBeanDe�nitionReader를 사용해 설정 파

일로부터 BeanDe�nition 데이터를 읽을 수 있다. 스프링에서 기본으로 제공하는 두 개의 메인

BeanFactory는 BeanDe�nitionRegistry를 구현하고 있다. PropertiesBeanDe�nitionReader는

프로퍼티 파일에서 빈 정의를 읽고, XmlBeanDe�nitionReader는 XML 파일에서 빈 정의를 읽

는다.

BeanFactory 내에 포함된 빈에는 각각 ID나 이름, 또는 둘을 모두 지정해 빈을 식별할 수 있다.

빈은 ID와 이름 없이도 인스턴스화할 수 있으며(익명 빈이라고 부름), 특정 빈 내에 내부 빈으로

인스턴스화할 수도 있다. 각 빈은 이름이 적어도 한 개는 있지만 이름은 원하는 수만큼 가질 수

있다(추가 이름은 콤마로 구분). 첫 번째 이름 다음에 나오는 이름은 모두 같은 빈에 대한 별칭으

로 간주한다. 빈 ID나 이름을 사용하면 BeanFactory에서 빈을 가져올 수 있고 의존성 관계를 설

정할 수 있다(즉, 빈 X가 빈 Y에 의존).

Page 106: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

78 l 프로 스프링 3

BeanFactory 구현체

BeanFactory에 대한 설명을 듣고 보면 사용법이 무척이나 복잡할 것 같지만 실제 사용법은 복

잡하지 않다. 다음 예제 코드를 살펴보자.

인생의 의미를 알려줄 수 있는 신탁 구현체가 있다고 가정하자. 예제 4-10과 4-11은 이 인터페

이스와 구현체를 각각 간단히 보여준다.

예제 4-10 Oracle 인터페이스

package com.apress.prospring3.ch4;

public interface Oracle {

public String defineMeaningOfLife();

}

예제 4-11 간단한 Oracle 인터페이스 구현체

package com.apress.prospring3.ch4;

public class BookwormOracle implements Oracle {

public String defineMeaningOfLife() {

return "Encyclopedias are a waste of money - use the Internet";

}

}

이제 단독 실행형 프로그램에서 스프링의 BeanFactory를 초기화하고 작업 처리를 위해

oracle 빈을 가져오는 법을 살펴보자(예제 4-12 참고).

예제 4-12 BeanFactory의 활용

package com.apress.prospring3.ch4;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

import org.springframework.core.io.FileSystemResource;

public class XmlConfigWithBeanFactory {

public static void main(String[] args) {

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);

rdr.loadBeanDefinitions(new

Page 107: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 79

FileSystemResource("src/main/resources/xmlBeanFactory.xml"));

Oracle oracle = factory.getBean("oracle", Oracle.class);

System.out.println(oracle.defineMeaningOfLife());

}

}

예제 4-12에서는 스프링에서 제공하는 두 가지 메인 BeanFactory 구현체 중 하나인 Default

ListableBeanFactory를 사용하는 것과 XmlBeanDe�nitionReader를 사용해 XML 파일에서

BeanDe�nition을 읽는 것을 볼 수 있다. BeanFactory 구현체를 생성하고 설정한 후에는 XML

설정 파일에서 설정한 oracle이라는 이름을 사용해 Oracle 빈을 가져온다. 예제 4-13은 스프링의

BeanFactory를 부트스트랩하는 데 사용한 XML 파일의 내용이다(xmlBeanFactory.xml).

예제 4-13 간단한 스프링 XML 설정

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<!-- 예제에 사용할 oracle 빈 -->

<bean id="oracle" name="wiseworm" class="com.apress.prospring3.ch4.BookwormOracle"/>

</beans>

이 파일에서는 스프링 빈을 선언하고 ID로 ‘oracle’, 이름으로 ‘wiseworm’을 지정하고 내부 구

현체 클래스가 com.apress.prospring3.ch4.BookwormOracle이라고 스프링에게 알려준다. 지

금은 설정에 대해 크게 걱정하지 않아도 된다. 이 내용은 이후 절에서 자세히 설명하겠다.

이렇게 설정을 정의한 후 예제 4-12의 프로그램을 실행하면 STS의 콘솔에 defineMeaning

OfLife() 메서드에서 정의한 구문이 출력되는 것을 볼 수 있다.

XmlBeanDe�nitionReader 외에 스프링은 XML 대신 프로피티를 사용해 빈 설정을 관리할

수 있게 PropertiesBeanDe�nitionReader도 제공한다. 프로퍼티는 규모가 작고 간단한 애플리

케이션에는 이상적이지만, 많은 빈을 다루다보면 금세 번거로워진다. 이런 이유로 아주 간단한

애플리케이션이 아니라면 항상 XML 설정 형식을 사용할 것을 권장한다.

물론 커스텀 BeanFactory 구현체도 얼마든지 정의할 수 있다. 하지만 이 작업을 하려면

꽤 많은 일을 해야 한다. 일단 기본으로 제공되는 BeanFactory 구현체 수준의 기능을 제공

하려고 해도 BeanFactory 외에 수많은 인터페이스를 구현해야 한다. 하려고 하는 작업이 새

Page 108: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

80 l 프로 스프링 3

로운 설정 메커니즘을 정의하는 정도라면 BeanFactory 인터페이스를 이미 구현하고 있는

DefaultListableBeanFactory 클래스를 상속해 새로운 설정 정의를 읽는 클래스를 만드는 게

낫다.

ApplicationContext

스프링에서 ApplicationContext 인터페이스는 BeanFactory를 상속한다. ApplicationContext

는 DI 서비스뿐 아니라 트랜잭션과 AOP 서비스, 국제화를 위한 메시지 소스(i18n), 애플리케이

션 이벤트 처리 등 다른 서비스도 함께 제공한다.

스프링 기반의 애플리케이션을 개발할 때는 ApplicationContext 인터페이스를 통해 스프링

과 연동하는 방식을 권장한다. 스프링은 직접 작성한 코드를 통해 ApplicationContext를 부트

스트랩할 수 있게 지원하고(ApplicationContext를 직접 인스턴스화하고 적절한 설정을 로드)

웹 컨테이너 환경에서는 ContextLoaderListener를 통한 부트스트랩도 지원한다. 이 책에서는

이 시점 이후로 계속해서 ApplicationContext만을 사용한다.

ApplicationContext의 설정

이제 IoC와 DI의 기본 개념을 살펴보고 스프링의 BeanFactory 인터페이스를 사용하는 간단한

예제도 봤으니 스프링 애플리케이션을 어떻게 설정하는지 자세히 알아보자.

이어지는 절에서는 스프링 애플리케이션을 설정하 는 다양 한 측면을 살펴본다. 특

히 여기서는 전통적인 BeanFactory 인터페이스보다 훨씬 더 많은 설정 옵션을 제공하는

ApplicationContext 인터페이스에 초점을 둔다.

스프링 설정 옵션(XML 및 자바 애노테이션)

스프링의 ApplicationContext를 설정하는 세부 내용으로 들어가기 전에 먼저 스프링 내에서 애

플리케이션의 설정을 정의할 때 사용할 수 있는 옵션을 살펴보자.

본래 스프링은 프로퍼티와 XML 파일을 통해서만 빈 선언을 지원하며 XML 파일은 대부분의

스프링 애플리케이션 개발자가 오랫동안 사용한 방식이다. JDK 5의 출시와 스프링의 자바 애노

테이션 지원 기능이 추가되면서 스프링(스프링 2.5를 시작으로)도 ApplicationContext를 설정

할 때 자바 애노테이션을 지원하기 시작했다.

Page 109: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 81

그럼 XML과 애노테이션 중 어떤 게 더 나을까? 이 주제에 대해서는 많은 논쟁이 있었고 인

터넷에서 다양한 토론 내용을 찾아볼 수 있다(예를 들어 http://forum.springsource.org에 있

는 스프링 커뮤니티 포럼 등). 이와 관련해서는 명확한 정답은 없으며 각 방식마다 장단점이

있다. XML 파일을 사용하면 모든 설정을 자바 코드로부터 외부화할 수 있는 반면, 애노테이

션을 사용하면 개발자들이 코드 내에서 DI 설정을 정의하고 있다고 볼 수 있다. 스프링은 한

ApplicationContext에서 두 접근 방식을 혼용하는 방식도 지원한다(이 경우 XML 파일 설정이

애노테이션 설정보다 우선시된다). 요즘 자주 사용하는 방식은 애플리케이션 인프라스트럭처(예

를 들어 데이터 소스, 트랜잭션 매니저, JMS 커넥션 팩터리, JMX 등)는 XML 파일에 정의하고,

DI 설정(주입할 빈 및 빈의 의존성)은 애노테이션에 정의하는 방식이다. 하지만 어떤 방식을 선택

하든 그 방식을 계속해서 준수하고 전체 개발 팀이 분명히 이해할 수 있게 메시지를 전달해야 한

다. 사용 방식에 동의하고 이를 모든 애플리케이션 영역에서 일관되게 유지하면 그만큼 개발과

유지보수도 쉬워진다.

XML과 애노테이션 설정에 대한 이해를 돕기 위해 여기서는 가능한 한 XML과 애노테이션을

사용한 예제 코드를 나란히 제공한다.

기본 설정 살펴보기

XML 설정을 사용하려면 애플리케이션에서 필요한 스프링 네임스페이스 베이스를 선언해야 한

다. 예제 4-13은 스프링 빈을 정의하는 데 필요한 beans 네임스페이스만 사용한 가장 기본적인

예제를 보여준다.

beans 외에 스프링은 각기 다른 용도별로 다양한 네임스페이스를 제공한다. 예제 4-14는 이 장

의 예제에서 사용할 스프링 설정에 대한 네임스페이스 선언이다. 스프링 설정 절에서는 예제에서

사용할 두 개의 설정 파일을 준비한다. 그 중 하나는 XML 방식의 설정에 사용할 app-context-

xml.xml이고 다른 파일은 애노테이션 설정에 사용할 app-context-annotation.xml이다.

예제 4-14 스프링 XML 설정

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:c="http://www.springframework.org/schema/c"

xmlns:util="http://www.springframework.org/schema/util"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/context

Page 110: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

82 l 프로 스프링 3

http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.1.xsd">

</beans>

이 네임스페이스 선언에서는 beans를 기본 네임스페이스로 지정했다. 더불어 다음 네임스페이

스도 함께 선언했다.

context: context 네임스페이스는 스프링의 ApplicationContext를 설정하는 지원

기능을 제공한다.

p: p 네임스페이스는 세터 주입을 쉽게 할 수 있는 DI 설정을 제공한다.

c: 스프링 3.1에서 새로 추가된 c 네임스페이스는 생성자 주입을 좀 더 간편하게 할 수 있

는 DI 설정을 제공한다.

util: util 네임스페이스는 DI 설정에 유용한 유틸리티를 제공한다.

스프링은 AOP 지원을 위한 aop 네임스페이스, 트랜잭션 지원을 위한 tx 네임스페이스 등 용도

별로 다양한 네임스페이스를 제공한다. 이들 네임스페이스는 해당 기능을 사용하는 장에서 설

명한다.

스프링의 애노테이션 지원 기능을 애플리케이션에서 사용하려면 XML 설정(app-context-

annotation.xml)에서 예제 4-15에 보이는 태그를 선언해야 한다.

예제 4-15 애노테이션 지원 기능을 갖춘 스프링 XML 설정

// 네임스페이스 선언 생략

<context:annotation-config/>

<context:component-scan base-package="com.apress.prospring3.ch4.annotation" />

<context:annotation-con�g> 태그는 스프링이 코드 기반에서 의존성 요구 조건을 스캔하게

끔 하고, <context:component-scan> 태그는 스프링이 코드에서 지정된 패키지(및 하위 패키지)

아래에 있는 주입 가능한 빈을 모두 스캔하게끔 한다. <context:component-scan> 태그에는 여

러 개의 패키지를 콤마, 세미콜론, 또는 공백 구분자를 사용해 정의할 수 있다. 예를 들어 예제

4-16의 설정을 살펴보자.

Page 111: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 83

예제 4-16 스프링 XML 설정 컴포넌트 스캔

<context:component-scan base-package="com.apress.prospring3.ch4.annotation" >

<context:exclude-filter type="assignable" expression="com.example.NotAService"/>

</context:component-scan>

앞의 태그는 스프링이 지정된 패키지를 스캔하게 하지만 표현식에서 지정한 타입(클래스 또

는 인터페이스)으로 대입할 수 있는 클래스는 제외시키게 했다. exclude 필터 외에 include

필터도 포함시킬 수 있다. 타입으로는 annotation, regex, assignable, AspectJ, custom(org.

springframework.core.type.�lter.TypeFilter를 구현하는 커스텀 필터 클래스)을 필터 조건으로

지정할 수 있다. expression의 형식은 지정한 type에 따라 다르다.

스프링 컴포넌트의 선언

서비스 클래스를 개발한 후 스프링 기반의 애플리케이션에서 사용하려면 해당 빈을 다른 빈에

주입할 수 있다는 사실을 스프링에게 알려주고 스프링이 빈을 관리하게 해야 한다. 2장의 예제에

서 MessageRenderer가 메시지 결과를 출력하고 MessageProvider에 의존해 메시지를 렌더러로

전달한 것을 생각하자. 예제 4-17은 두 서비스를 인터페이스와 구현체로 다시 작성한 것이다.

예제 4-17 MessageRenderer와 MessageProvider

package com.apress.prospring3.ch4;

public interface MessageRenderer {

public void render();

public void setMessageProvider(MessageProvider provider);

public MessageProvider getMessageProvider();

}

package com.apress.prospring3.ch4.xml;

import com.apress.prospring3.ch4.MessageProvider;

import com.apress.prospring3.ch4.MessageRenderer;

public class StandardOutMessageRenderer implements MessageRenderer {

private MessageProvider messageProvider = null;

public void render() {

if (messageProvider == null) {

Page 112: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

84 l 프로 스프링 3

throw new RuntimeException(

"You must set the property messageProvider of class:"

+ StandardOutMessageRenderer.class.getName());

}

System.out.println(messageProvider.getMessage());

}

public void setMessageProvider(MessageProvider provider) {

this.messageProvider = provider;

}

public MessageProvider getMessageProvider() {

return this.messageProvider;

}

}

package com.apress.prospring3.ch4;

public interface MessageProvider {

public String getMessage();

}

package com.apress.prospring3.ch4.xml;

import com.apress.prospring3.ch4.MessageProvider;

public class HelloWorldMessageProvider implements MessageProvider {

public String getMessage() {

return "Hello World!";

}

}

XML 파일에서 빈을 선언하려면 기본 설정(예제 4-14에서 말한 설정) 외에 예제 4-18의

<bean> 태그를 app-context-xml.xml 파일에 추가해야 한다.

예제 4-18 스프링 빈 선언 (XML)

<bean id="messageRenderer" class="com.apress.prospring3.ch4.xml.StandardOutMessageRenderer"/>

<bean id="messageProvider" class="com.apress.prospring3.ch4.xml. HelloWorldMessageProvider"/>

Page 113: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 85

앞의 태그는 두 개의 빈, 즉 ID가 ‘messageProvider’이고 HelloWorldMessageProvider 구현체

인 빈과 ID가 ‘messageRenderer’이고 StandardOutMessageRenderer 구현체인 빈을 선언한다.

애노테이션을 통해 스프링 빈을 정의할 때는 XML 설정 파일(app-context-annotation.

xml)을 더는 수정하지 않아도 된다. 이때는 해당 애노테이션을 com.apress.prospring3.ch4.

annotation 패키지 아래에 있는 서비스 구현체 클래스에 추가하기만 하면 된다(예제 4-19 참고).

예제 4-19 스프링 빈 선언(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.stereotype.Service;

// 나머지 코드 생략

@Service("messageRenderer")

public class StandardOutMessageRenderer implements MessageRenderer {

package com.apress.prospring3.ch4.annotation;

import org.springframework.stereotype.Service;

// 나머지 코드 생략

@Service("messageProvider")

public class HelloWorldMessageProvider implements MessageProvider {

이 예제 코드에서는 스프링의 @Service 애노테이션을 사용해 빈이 다른 빈에서 요구할 수 있

는 서비스를 제공한다는 사실을 지정했고, 파라미터로 빈의 이름을 넘겼다. 예제 4-15의 XML

설정 파일을 사용해 스프링의 ApplicationContext를 부트스트랩할 때 스프링은 이들 컴포넌트

를 찾고 지정된 이름을 갖고 있는 빈의 인스턴스를 생성한다.

둘 중 어떤 방식을 사용하든 ApplicationContext를 통해 빈을 가져오는 방식에는 영향이 없

다. 예제 4-20은 예제 코드를 사용해 메시지 제공자를 가져오는 법을 보여준다.

예제 4-20 스프링 빈 선언(테스트)

package com.apress.prospring3.ch4;

import org.springframework.context.support.GenericXmlApplicationContext;

public class DeclareSpringComponents {

public static void main(String[] args) {

Page 114: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

86 l 프로 스프링 3

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:META-INF/spring/app-context-annotation.xml");

ctx.refresh();

MessageProvider messageProvider = ctx.getBean("messageProvider",

MessageProvider.class);

System.out.println(messageProvider.getMessage());

}

}

예제 4-20에서는 DefaultListableBeanFactory 대신 GenericXmlApplicationContext 인스턴

스를 생성한다. GenericXmlApplicationContext는 ApplicationContext 인터페이스를 구현하

며 XML 파일에 정의된 설정으로부터 스프링의 ApplicationContext를 부트스트랩할 수 있다.

이 장의 소스코드에서는 app-context-xml.xml 파일을 app-context-annotation.xml 파일로

바꿔도 상관없으며, 이렇게 하더라도 두 경우 모두 결과는 같다. 즉, 둘 다 똑같이 ‘Hello World!’

가 출력된다.

예제 4-21(app-context-xml.xml)과 예제 4-22(app-context-annotation.xml)는 지금까지 얘

기한 XML 설정과 애노테이션 설정 파일의 내용을 정리한 것이다.

예제 4-21 XML 설정(app-context-xml.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:c="http://www.springframework.org/schema/c"

xmlns:util="http://www.springframework.org/schema/util"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.1.xsd">

<bean id="messageProvider"

class="com.apress.prospring3.ch4.xml.HelloWorldMessageProvider"/>

</beans>

Page 115: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 87

예제 4-22 애노테이션 설정(app-context-annotation.xml)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:c="http://www.springframework.org/schema/c"

xmlns:util="http://www.springframework.org/schema/util"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/util

http://www.springframework.org/schema/util/spring-util-3.1.xsd">

<context:annotation-config/>

<context:component-scan

base-package="com.apress.prospring3.ch4.annotation"/>

</beans>

세터 주입의 활용

XML 설정에서 세터 주입을 설정하려면 주입하려는 프로퍼티별로 <bean> 태그 아래에

<property> 태그를 지정해야 한다. 예를 들어 messageRenderer 빈의 messageProvider 프로퍼

티에 메시지 제공자 빈을 지정하려면 예제 4-23에 보이는 것처럼 messageRenderer의 <bean> 태

그를 간단히 수정하면 된다.

예제 4-23 세터 주입(XML)

<bean id="messageRenderer" class="com.apress.prospring3.ch4.xml.StandardOutMessageRenderer">

<property name="messageProvider">

<ref bean="messageProvider"/>

</property>

</bean>

이 코드에서는 messageProvider 빈을 messageProvider 프로퍼티에 대입하는 것을 볼 수 있

다. <ref> 태그는 빈 참조를 프로퍼티에 대입할 때 사용한다(잠시 후 자세히 설명한다).

스프링 2.5 이상을 사용한다면 XML 설정 파일에서 p 네임스페이스를 선언하고, 다음과 같이

의존성 주입을 선언할 수도 있다.

Page 116: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

88 l 프로 스프링 3

<bean id="messageRenderer" class="com.apress.prospring3.ch4.xml.StandardOutMessageRenderer"

p:messageProvider-ref="messageProvider"/>

p 네임스페이스를 이용하면 세터 주입을 좀 더 간편하게 할 수 있다.

애노테이션을 사용하면 훨씬 더 간단하다. 이때는 예제 4-24에 보이는 것처럼 @Autowired 애

노테이션을 세터 메서드에 그냥 추가하기만 하면 된다.

예제 4-24 세터 주입(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.beans.factory.annotation.Autowired;

// 나머지 코드 생략

@Service("messageRenderer")

public class StandardOutMessageRenderer implements MessageRenderer {

… @Autowired

public void setMessageProvider(MessageProvider provider) {

this.messageProvider = provider;

}

…}

XML 설정 파일에서 <context:annotation-con�g> 태그를 선언했으므로 스프링의 Application

Context 초기화 과정 동안 스프링은 이들 @Autowired 애노테이션을 찾은 후 필요에 따라 의존

성(<context:component-scan> 태그를 통해 찾음)을 주입해준다.

알아두기

@Autowired 대신 @Resource(name="messageProvider")를 사용하더라도 결과는 같다.

@Resource는 JSE와 JEE 플랫폼 모두에서 사용할 수 있는 공통 자바 애노테이션을 정의하는

JSR-250 표준 애노테이션 중 하나다. @Autowired와 달리 @Resource 애노테이션은 좀 더

미세한 DI 요구 조건을 제어할 수 있게 name 파라미터를 지원한다.

이제 예제 4-25의 코드를 사용해 결과를 확인하자.

예제 4-25 세터 주입의 활용(테스트)

package com.apress.prospring3.ch4;

import org.springframework.context.support.GenericXmlApplicationContext;

Page 117: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 89

public class UsingSetterInjection {

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

MessageRenderer messageRenderer = ctx.getBean("messageRenderer",

MessageRenderer.class);

messageRenderer.render();

}

}

앞 절과 마찬가지로 여기서도 app-context-xml.xml 파일을 app-context-annotation.xml로

바꾸더라도 결과는 같다. 즉 ‘Hello World!’가 똑같이 출력된다.

생성자 주입의 활용

앞의 예제에서 MessageProvider 구현체인 HelloWorldMessageProvider는 매번 getMessage()

메서드를 호출할 때마다 동일한 하드코딩된 메시지를 반환했다. 스프링 설정 파일에서는 예제

4-26에서 볼 수 있듯이 메시지를 외부에 정의함으로써 설정 가능한 MessageProvider를 손쉽게

생성할 수 있다.

예제 4-26 ConfigurableMessageProvider 클래스(XML)

package com.apress.prospring3.ch4.xml;

import com.apress.prospring3.ch4.MessageProvider;

public class ConfigurableMessageProvider implements MessageProvider {

private String message;

public ConfigurableMessageProvider(String message) {

this.message = message;

}

public String getMessage() {

return message;

}

}

Page 118: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

90 l 프로 스프링 3

이 코드에서 볼 수 있듯이 Con�gurableMessageProvider는 (null을 제공하는 경우를 제외하고)

메시지 값을 제공하지 않으면 인스턴스를 생성할 수 없다. 이는 바로 우리가 원하는 바이므로 이 클

래스는 생성자 주입을 사용하기에 매우 적합하다. 예제 4-27은 Con�gurableMessageProvider 인

스턴스를 생성하고 생성자 주입을 사용해 메시지를 주입하게끔 messageProvider 빈 정의를 수

정하는 법을 보여준다.

예제 4-27 생성자 주입 사용(XML)

<bean id="messageProvider" class="com.apress.prospring3.ch4.xml.ConfigurableMessageProvider">

<constructor-arg>

<value>This is a configurable message</value>

</constructor-arg>

</bean>

이 코드에서는 <property> 태그 대신 <constructor-arg> 태그를 사용했다. 이번에는 다른

빈을 넘겨주는 게 아니라 String 리터럴을 넘겨주므로 생성자 인자 값을 지정할 때 <ref> 대신

<value> 태그를 사용한다.

생성자 인자가 둘 이상이거나 클래스에 생성자가 둘 이상이라면 인자의 인덱스를 지정하기 위

해 개별 <constructor-arg> 태그마다 index 속성을 0부터 시작해 생성자 시그너처 순으로 지정

해야 한다. index 속성은 여러 인자를 받는 생성자에서는 항상 사용하는 게 좋다. 이렇게 하면 인

자를 혼동하는 상황을 방지해 스프링이 항상 올바른 생성자를 선택하게 할 수 있다.

p 네임스페이스와 마찬가지로 스프링 3.1에서는 다음과 같이 c 네임스페이스도 사용할 수 있다.

<bean id="messageProvider"

class="com.apress.prospring3.ch4.xml.ConfigurableMessageProvider"

c:message="This is a configurable message"/>

애노테이션을 사용해 생성자 주입을 할 때도 예제 4-28에 보이는 것처럼 타깃 빈의 생성자 메

서드에 @Autowired 애노테이션을 사용한다. 이 예제는 예제 4-28에서 본 세터 주입 방식 대신

사용할 수 있는 옵션이다.

예제 4-28 생성자 주입 사용(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.beans.factory.annotation.Value;

…@Service("messageProvider")

public class ConfigurableMessageProvider implements MessageProvider {

Page 119: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 91

private String message;

@Autowired

public ConfigurableMessageProvider(@Value("This is a configurable message") String message) {

this.message = message;

}

…}

이 예제에서는 생성자로 주입할 값을 나타내는 또 다른 애노테이션인 @Value를 볼 수 있다. 스

프링에서는 이런 식으로 빈에 값을 주입한다. 단순 문자열 외에 강력한 SpEL을 사용해 동적으로

값을 주입할 수도 있다(자세한 내용은 이 장에서 나중에 설명한다).

하지만 코드 내에 값을 하드코딩하는 건 좋은 생각이 아니다. 이런 값을 바꾸려면 매번 프로그

램을 재컴파일해야 하기 때문이다. 애노테이션 방식의 DI를 사용하더라도 이들 값은 외부에서

주입할 수 있게 하는 게 좋다. 메시지를 외부화할 수 있게 이번에는 예제 4-29와 같이 애노테이션

설정 파일에서 메시지를 스프링 빈으로 정의해 보자.

예제 4-29 생성자 주입 사용(애노테이션)

<bean id="message" class="java.lang.String"

c:_0="This is a configurable message"/>

여기서는 ID가 ‘message’이고 타입이 java.lang.String인 빈을 정의했다. 이 설정에서는 생성자

주입을 통해 문자열 값을 설정할 수 있게 c 네임스페이스를 사용하고 _0이 생성자 인자의 인덱스

를 가리키는 것을 볼 수 있다.

이제 빈을 선언했으니 예제 4-30처럼 타깃 빈에서 @Value 애노테이션은 제거해도 된다.

예제 4-30 생성자 주입 사용(애노테이션)

package com.apress.prospring3.ch4.annotation;

…@Service("messageProvider")

public class ConfigurableMessageProvider implements MessageProvider {

private String message;

@Autowired

public ConfigurableMessageProvider(String message) {

this.message = message;

}

…}

Page 120: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

92 l 프로 스프링 3

앞서 message 빈을 선언했고 이 빈의 ID가 생성자에서 지정한 인자의 이름과 같으므로 스프

링은 애노테이션을 감지하고 그 값을 생성자 메서드에 주입해준다.

이제 XML(app-context.xml.xml)과 애노테이션 설정(app-context-annotation.xml)을 대상

으로 테스트(예제 4-25에 있는 UsingSetterInjection 클래스)를 하면 두 경우 모두 설정된 메시

지가 보일 것이다. 출력 결과는 다음과 같다.

This is a configurable message

생성자 혼동 피하기

때로는 스프링이 생성자 주입에 사용할 생성자를 판단하지 못할 때도 있다. 이런 일은 주로 두 개

의 생성자가 인자 수가 같고 인자에 사용된 타입도 정확히 동일할 때 일어난다. 예제 4-31의 코드

를 살펴보자.

예제 4-31 생성자 혼동

package com.apress.prospring3.ch4.xml;

import org.springframework.context.support.GenericXmlApplicationContext;

public class ConstructorConfusion {

private String someValue;

public ConstructorConfusion(String someValue) {

System.out.println("ConstructorConfusion(String) called");

this.someValue = someValue;

}

public ConstructorConfusion(int someValue) {

System.out.println("ConstructorConfusion(int) called");

this.someValue = "Number: " + Integer.toString(someValue);

}

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext()

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

ConstructorConfusion cc = (ConstructorConfusion) ctx.getBean("constructorConfusion");

System.out.println(cc);

}

public String toString() {

Page 121: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 93

return someValue;

}

}

이 코드가 하는 일은 쉽게 이해할 수 있다. 이 코드는 ApplicationContext로부터 Constructor

Confusion 타입의 빈을 가져오고 값을 콘솔에 출력한다. 이번에는 예제 4-32의 설정 코드(app-

context-xml.xml)를 살펴보자.

예제 4-32 혼란스러운 생성자

<bean id="constructorConfusion"

class="com.apress.prospring3.ch4.xml.ConstructorConfusion">

<constructor-arg>

<value>90</value>

</constructor-arg>

</bean>

이 경우 어떤 생성자가 호출될까? 이 예제를 실행하면 다음 결과가 출력된다.

ConstructorConfusion(String) called

90

이 결과를 보면 String 인자를 사용한 생성자가 호출됐음을 알 수 있다. 하지만 본래 의도는

int 생성자에 보이는 것처럼 생성자 주입을 통해 주입한 정수 앞에 Number 접두어를 사용해 출

력하려는 것이었으므로 원하던 결과가 아니다. 이 문제를 해결하려면 예제 4-33(app-context-

xml.xml)처럼 설정을 조금 수정해야 한다.

예제 4-33 생성자 혼동의 극복

<bean id="constructorConfusion"

class="com.apress.prospring3.ch4.xml.ConstructorConfusion">

<constructor-arg type="int">

<value>90</value>

</constructor-arg>

</bean>

이번에는 <constructor-arg> 태그에 type이란 추가 태그를 붙인 것을 볼 수 있다. 이 태그는 스

프링이 찾을 인자의 타입을 지정한다. 이 예제를 다시 실행하면 이번에는 올바른 설정으로 인해

올바른 결과가 출력된다.

ConstructorConfusion(int) called

Number: 90

Page 122: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

94 l 프로 스프링 3

애노테이션 방식의 생성자 주입을 사용할 때는 예제 4-34에서 한 것처럼 타깃 생성자 메서드에

직접 애노테이션을 적용해 이런 혼란을 피할 수 있다.

예제 4-34 생성자 혼동(애노테이션)

package com.apress.prospring3.ch4.annotation;

// 코드 생략

@Service("constructorConfusion")

public class ConstructorConfusion {

private String someValue;

public ConstructorConfusion(String someValue) {

System.out.println("ConstructorConfusion(String) called");

this.someValue = someValue;

}

@Autowired

public ConstructorConfusion(@Value("90") int someValue) {

System.out.println("ConstructorConfusion(int) called");

this.someValue = "Number: " + Integer.toString(someValue);

}

// 코드 생략

}

@Autowired 애노테이션을 원하는 생성자 메서드에 적용하면 스프링은 해당 메서드를 사용

해 빈 인스턴스를 생성하고 지정한 값을 주입한다. 앞에서와 마찬가지로 이 값은 설정으로 외부

화할 수 있다.

알아두기

@Autowired 애노테이션은 생성자 메서드 중 한 곳에만 적용할 수 있다. 이 애노테이션을 둘

이상의 생성자 메서드에 적용하면 스프링은 ApplicationContext의 부트스트랩 과정에서 오

류를 일으킨다.

Page 123: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 95

주입 파라미터

앞의 두 예제에서는 세터 주입과 생성자 주입을 통해 다른 컴포넌트와 값을 빈에 주입하는 법을

살펴봤다. 스프링은 다양한 주입 파라미터 옵션을 지원하며, 다른 컴포넌트와 단순 값 외에 자바

컬렉션, 외부에서 정의한 프로퍼티, 다른 팩터리의 빈까지 주입할 수 있게 해준다. 이들 주입 파라

미터 타입은 <property>와 <constructor-args> 태그 아래에 각각 해당하는 태그를 사용해 세터

주입과 생성자 주입 모두에서 사용할 수 있다.

단순 값 주입

빈에 단순 값을 주입하는 일은 아주 쉽다. 이때는 <value> 태그로 감싸 값을 지정하기만 하면 된

다. 기본적으로 <value> 태그는 String 값을 읽지만 이 값을 다른 원시 타입이나 원시 래퍼 클래

스로 변환할 수도 있다. 예제 4-35는 값을 주입할 수 있게 다양한 프로퍼티를 노출한 간단한 빈

을 보여준다.

예제 4-35 단순 값 주입(XML)

package com.apress.prospring3.ch4.xml;

import org.springframework.context.support.GenericXmlApplicationContext;

public class InjectSimple {

private String name;

private int age;

private float height;

private boolean programmer;

private Long ageInSeconds;

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

InjectSimple simple = (InjectSimple)ctx.getBean("injectSimple");

System.out.println(simple);

}

Page 124: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

96 l 프로 스프링 3

public void setAgeInSeconds(Long ageInSeconds) {

this.ageInSeconds = ageInSeconds;

}

public void setProgrammer(boolean programmer) {

this.programmer = programmer;

}

public void setAge(int age) {

this.age = age;

}

public void setHeight(float height) {

this.height = height;

}

public void setName(String name) {

this.name = name;

}

public String toString() {

return "Name :" + name + "\n"

+ "Age:" + age + "\n"

+ "Age in Seconds: " + ageInSeconds + "\n"

+ "Height: " + height + "\n"

+ "Is Programmer?: " + programmer;

}

}

이들 프로퍼티 외에 InjectSimple 클래스는 ApplicationContext를 생성하고 스프링으로부터

InjectSimple을 조회하는 main() 메서드도 정의한다. 이 빈의 프로퍼티 값은 콘솔에 출력된다.

예제 4-36은 이 빈의 설정(app-context-xml.xml)을 보여준다.

예제 4-36 간단한 빈 주입 설정

<bean id="injectSimple" class="com.apress.prospring3.ch4.xml.InjectSimple">

<property name="name">

<value>John Smith</value>

</property>

<property name="age">

<value>35</value>

</property>

<property name="height">

<value>1.78</value>

</property>

<property name="programmer">

<value>true</value>

</property>

Page 125: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 97

<property name="ageInSeconds">

<value>1103760000</value>

</property>

</bean>

예제 4-35와 4-36을 보면 String 값, 원시 값, 원시 래퍼 값을 받게끔 빈에서 프로퍼티를 정의하

고 <value> 태그를 사용해 이들 프로퍼티에 대한 값을 주입할 수 있음을 볼 수 있다. 이 예제를

실행한 결과는 예상대로 다음과 같다.

Name: John Smith

Age: 35

Age in Seconds: 1103760000

Height: 1.78

Is Programmer?: true

애노테이션 방식으로 단순 값을 주입하려면 @Value 애노테이션을 빈 프로퍼티에 적용하면 된

다. 이번에는 세터 메서드 대신 예제 4-37처럼 프로퍼티 선언문에 애노테이션을 적용한다(스프

링은 세터 메서드나 프로퍼티 중 한 곳에 애노테이션을 쓸 수 있게 지원한다).

예제 4-37 단순 값 주입(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.support.GenericXmlApplicationContext;

import org.springframework.stereotype.Service;

@Service("injectSimple")

public class InjectSimple {

@Value("John Smith")

private String name;

@Value("35")

private int age;

@Value("1.78")

private float height;

@Value("true")

private boolean programmer;

@Value("1103760000")

private Long ageInSeconds;

public static void main(String[] args) {

Page 126: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

98 l 프로 스프링 3

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-annotation.xml");

ctx.refresh();

InjectSimple simple = (InjectSimple)ctx.getBean("injectSimple");

ctx.getBean("injectRef");

System.out.println(simple);

}

// 게터/세터 메서드 생략

public String toString() {

return "Name :" + name + "\n"

+ "Age:" + age + "\n"

+ "Age in Seconds: " + ageInSeconds + "\n"

+ "Height: " + height + "\n"

+ "Is Programmer?: " + programmer;

}

}

이 예제의 결과는 XML 설정 방식과 동일하다.

SpEL을 활용한 값 주입

스프링 3에서 새로 추가된 강력한 기능 중 하나는 바로 스프링 표현 언어(SpEL)다. SpEL을 사용

하면 동적으로 표현식을 해석하고 그 결과를 스프링의 ApplicationContext에서 사용할 수 있다.

이를 활용할 만한 사례로 결과 값을 스프링 빈에 주입하는 경우가 있다. 이 절에서는 앞서 소개

한 예제를 토대로 SpEL을 활용해 다른 빈으로부터 프로퍼티 값을 주입하는 법을 살펴본다.

이번에는 예제 4-38과 같이 스프링 빈에 주입할 값을 설정 클래스로 외부화한다고 가정하자.

예제 4-38 SpEL을 활용한 값 주입(XML)

package com.apress.prospring3.ch4.xml;

public class InjectSimpleConfig {

private String name = "John Smith";

private int age = 35;

private float height = 1.78f;

private boolean programmer = true;

Page 127: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 99

private Long ageInSeconds = 1103760000L;

// 게터/세터 생략

}

이제 예제 4-39(app-context-xml.xml)와 같이 XML 설정에서 빈을 정의하고 SpEL을 사용해

빈의 프로퍼티를 의존 빈에 주입할 수 있다.

예제 4-39 SpEL을 활용한 값 주입(XML)

<bean id="injectSimpleConfig" class="com.apress.prospring3.ch4.xml.InjectSimpleConfig"/>

<bean id="injectSimpleSpel" class="com.apress.prospring3.ch4.xml.InjectSimpleSpel">

<property name="name">

<value>#{injectSimpleConfig.name}</value>

</property>

<property name="age">

<value>#{injectSimpleConfig.age + 1}</value>

</property>

<property name="height">

<value>#{injectSimpleConfig.height}</value>

</property>

<property name="isProgrammer">

<value>#{injectSimpleConfig.programmer}</value>

</property>

<property name="ageInSeconds">

<value>#{injectSimpleConfig.ageInSeconds}</value>

</property>

</bean>

여기서 다른 빈의 프로퍼티를 참조할 때 #{injectSimpleCon�g.name}라는 SpEL을 사용하는

것을 볼 수 있다. age 프로퍼티 값을 주입할 때는 사용한 SpEL이 프로퍼티 값을 지정한 대로 수

정하고 의존 빈에 주입하는지 확인하기 위해 빈의 값에 1을 더했다. 이제 이 설정을 예제 4-40의

프로그램을 통해 테스트해보자.

예제 4-40 SpEL을 활용한 값 주입(XML)

package com.apress.prospring3.ch4.xml;

import org.springframework.context.support.GenericXmlApplicationContext;

public class InjectSimpleSpel {

private String name;

private int age;

Page 128: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

100 l 프로 스프링 3

private float height;

private boolean programmer;

private Long ageInSeconds;

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

InjectSimpleSpel simple = (InjectSimpleSpel)ctx.getBean("injectSimpleSpel");

System.out.println(simple);

}

// 나머지 코드 생략

}

다음은 이 프로그램을 실행한 결과다.

Name: John Smith

Age:36

Age in Seconds: 1103760000

Height: 1.78

Is Programmer?: true

애노테이션 방식의 값 주입을 사용할 때는 값 애노테이션을 SpEL 표현식으로 바꾸기만 하면

된다(예제 4-41 참고).

예제 4-41 SpEL을 활용한 값 주입(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.support.GenericXmlApplicationContext;

import org.springframework.stereotype.Service;

@Service("injectSimpleSpel")

public class InjectSimpleSpel {

@Value("#{injectSimpleConfig.name}")

private String name;

@Value("#{injectSimpleConfig.age + 1}")

private int age;

@Value("#{injectSimpleConfig.height}")

Page 129: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 101

private float height;

@Value("#{injectSimpleConfig.programmer}")

private boolean programmer;

@Value("#{injectSimpleConfig.ageInSeconds}")

private Long ageInSeconds;

// 나머지 코드 생략

}

예제 4-42는 InjectSimpleCon�g 클래스의 애노테이션 버전을 보여준다.

예제 4-42 InjectSimpleConfig 클래스(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.stereotype.Component;

@Component("injectSimpleConfig")

public class InjectSimpleConfig {

private String name = "John Smith";

private int age = 35;

private float height = 1.78f;

private boolean programmer = true;

private Long ageInSeconds = 1103760000L;

// 게터/세터 생략

}

예제 4-42에서는 @Service 애노테이션 대신 @Component를 사용했다. 기본적으로

@Component를 사용하면 @Service와 효과가 동일하다. 두 애노테이션은 모두 스프링에게 애

노테이션 기반의 설정과 클래스 스캔을 사용해 애노테이션으로 지정한 클래스를 자동 감지하게

끔 명령한다. 하지만 InjectSimpleCon�g 클래스는 비즈니스 서비스를 제공하는 게 아니라 애플

리케이션 설정을 저장하는 클래스이므로 여기서는 @Component를 사용하는 게 더 이치에 맞

다. 사실 @Service는 @Component를 특화해 애노테이션으로 지정한 클래스가 애플리케이션

내의 다른 레이어에 비즈니스 서비스를 제공한다는 사실을 표시해주는 애노테이션일 뿐이다.

이 프로그램을 테스트해 보면 결과는 같다. SpEL을 사용하면 스프링의 정교한 언어 기능과 구

문을 사용해 스프링 관리 빈과 프로퍼티에 접근해 애플리케이션에서 원하는 대로 사용할 수 있다.

Page 130: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

102 l 프로 스프링 3

같은 XML 단위 내에서 빈 주입하기

앞에서 본 것처럼 <ref> 태그를 사용해 한 빈을 다른 빈에 주입할 수 있다. 예제 4-43은 다른 빈

을 주입받을 수 있게 세터를 노출하는 클래스를 보여준다.

예제 4-43 빈 주입

package com.apress.prospring3.ch4.xml;

import org.springframework.context.support.GenericXmlApplicationContext;

import com.apress.prospring3.ch4.Oracle;

public class InjectRef {

private Oracle oracle;

public void setOracle(Oracle oracle) {

this.oracle = oracle;

}

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

InjectRef injectRef = (InjectRef) ctx.getBean("injectRef");

System.out.println(injectRef);

}

public String toString() {

return oracle.defineMeaningOfLife();

}

}

스프링이 한 빈을 다른 빈에 주입하게 설정하려면 먼저 두 개의 빈을 설정해야 한다. 이 중 한

빈은 주입할 빈이고, 다른 빈은 주입 타깃이 되는 빈이다. 이렇게 빈을 정의한 후에는 타깃 빈에서

<ref> 태그를 사용해 주입을 설정하면 된다. <ref> 태그는 세터 주입을 사용 여부 또는 생성자 주

입을 사용 여부에 따라 항상 <property>나 <constructor-arg> 다음에 나와야 한다는 점을 기억

하자. 예제 4-44는 이런 설정(app-context-xml.xml)을 보여준다.

예제 4-44 빈 주입 설정

<bean id="oracle" name="wiseworm" class="com.apress.prospring3.ch4.BookwormOracle"/>

Page 131: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 103

<bean id="injectRef" class="com.apress.prospring3.ch4.xml.InjectRef">

<property name="oracle">

<ref local="oracle"/>

</property>

</bean>

예제 4-43에 있는 클래스를 실행하면 다음과 같은 출력 결과가 나온다.

Encyclopedias are a waste of money - use the Internet

여기서 중요하게 짚고 넘어갈 사항은 주입하는 타입과 타깃에 정의한 타입이 항상 일치하지 않

아도 된다는 점이다. 타입은 서로 호환되기만 하면 된다. 호환이란 말은 예컨대 타깃에 선언한 타

입이 인터페이스라면 주입하는 타입은 이 인터페이스를 구현해야 한다는 의미다. 선언된 타입이

클래스라면 주입하는 타입은 같은 타입이거나 하위 타입이어야 한다. 이 예제에서는 InjectRef

클래스가 Oracle 인터페이스 타입의 인스턴스를 받는 setOracle() 메서드를 정의하고, 주입하는

타입은 Oracle을 구현하는 BookwormOracle이다. 개발자 중에는 이 점을 제대로 이해하지 못

하는 사람도 있는데 사실 매우 간단한 내용이다. 주입은 다른 자바 코드와 동일한 타입 규칙을

따르며, 독자들이 자바 타입 규칙이 어떻게 적용되는지 잘 알고 있다면 주입에서 사용하는 타입

도 쉽게 이해할 수 있을 것이다.

앞의 예제에서 주입할 빈의 id는 <ref> 태그의 local 속성을 사용해 지정했다. 잠시 후 ‘빈 이름

지정 방식의 이해’ 절에서 보겠지만 빈 하나에 이름을 두 개 이상 지정하고 여러 별칭을 사용해

참조할 수도 있다. local 속성을 사용한다는 말은 <ref> 태그가 빈의 id만 살펴보고, 별칭은 전혀

살펴보지 않는다는 의미다. 더불어 빈 정의는 항상 같은 XML 설정 파일 내에 존재해야 한다. 다

른 이름의 빈을 주입하거나 다른 설정 파일에서 빈을 불러오려면 local 속성 대신 <ref> 태그의

bean 속성을 사용해야 한다. 예제 4-45는 주입하는 빈에 다른 이름을 사용해 앞의 예제에 대한

설정을 수정한 예제다.

예제 4-45 빈 별칭을 사용한 주입

<bean id="oracle" name="wiseworm" class="com.apress.prospring.ch4.BookwormOracle"/>

<bean id="injectRef" class="com.apress.prospring3.ch4.xml.InjectRef">

<property name="oracle">

<ref bean="wiseworm"/>

</property>

</bean>

Page 132: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

104 l 프로 스프링 3

이 예제에서 oracle 빈은 name 속성을 사용해 별칭을 지정했고, <ref> 태그의 bean 속성과 연

계해 이 별칭을 사용해 injectRef으로 주입된다. 아직까지는 이런 이름 지정 방식에 대해 크게 걱

정하지 않아도 된다. 이 내용은 이 장에서 나중에 상세히 설명할 것이다. InjectRef 클래스(예제

4-43)를 한 번 더 실행하면 앞의 예제와 같은 결과가 나온다.

주입과 ApplicationContext 중첩

지금까지는 주입한 빈이 주입을 받는 빈과 같은 ApplicationContext 내에 있었다(더불어 같은

BeanFactory). 하지만 스프링은 ApplicationContext에 대해 계층구조도 지원한다. 따라서 한

컨텍스트(더불어 관련 BeanFactory)는 다른 컨텍스트의 부모가 될 수도 있다. 스프링은 이와 같

이 ApplicationContext를 중첩할 수 있는 기능을 통해 설정을 여러 파일로 나눌 수 있게 해준다

(빈의 개수가 수없이 많은 대규모 프로젝트에서는 반가운 기능이 아닐 수 없다).

ApplicationContext를 여러 개 중첩할 때 스프링은 자식 컨텍스트에 있는 빈이 부모 컨텍스

트에 있는 빈을 참조할 수 있게 해준다. GenericXmlApplicationContext를 활용한 Application

Context 중첩은 매우 쉽게 사용해볼 수 있다. 한 GenericXmlApplicationContext를 다른

GenericXmlApplicationContext 안에 집어 넣으려면 예제 4-46에서처럼 자식 Application

Context에서 setParent() 메서드를 호출하면 된다.

예제 4-46 중첩 GenericXmlApplicationContext

package com.apress.prospring3.ch4;

import org.springframework.context.support.GenericXmlApplicationContext;

public class HierarchicalAppContextUsage {

public static void main(String[] args) {

GenericXmlApplicationContext parent = new GenericXmlApplicationContext();

parent.load("classpath:parent.xml");

parent.refresh();

GenericXmlApplicationContext child = new GenericXmlApplicationContext();

child.load("classpath:app-context-xml.xml");

child.setParent(parent);

child.refresh();

SimpleTarget target1 = (SimpleTarget) child.getBean("target1");

SimpleTarget target2 = (SimpleTarget) child.getBean("target2");

SimpleTarget target3 = (SimpleTarget) child.getBean("target3");

Page 133: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 105

System.out.println(target1.getVal());

System.out.println(target2.getVal());

System.out.println(target3.getVal());

}

}

예제 4-47은 SimpleTarget 클래스를 보여준다.

예제 4-47 SimpleTarget 클래스

package com.apress.prospring3.ch4;

public class SimpleTarget {

private String val;

public void setVal(String val) {

this.val = val;

}

public String getVal() {

return val;

}

}

자식 ApplicationContext의 설정 파일 내에서 부모 ApplicationContext에 있는 빈을 참조

하는 방법은 자식 ApplicationContext 내에 이름이 같은 빈이 있지 않는 한 자식 Application

Context에 있는 빈을 참조하는 방법과 동일하다. 하지만 이름이 같은 빈이 있다면 <ref> 태그

의 bean 속성을 그냥 parent로 바꾸면 된다. 예제 4-48은 부모 BeanFactory의 예제 설정 파일

(parent.xml)을 보여준다.

예제 4-48 부모 ApplicationContext 설정

<bean id="injectBean" class="java.lang.String">

<constructor-arg>

<value>Bean In Parent</value>

</constructor-arg>

</bean>

<bean id="injectBeanParent" class="java.lang.String">

<constructor-arg>

<value>Bean In Parent</value>

</constructor-arg>

</bean>

Page 134: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

106 l 프로 스프링 3

예제에서 볼 수 있듯이 이 설정 파일은 두 개의 빈을 정의한다. 바로 injectBean과 injectBean

Parent다. 두 빈은 모두 값이 Bean In Parent인 String 객체다. 예제 4-49는 자식 Application

Context의 설정 파일(app-context-xml.xml)을 보여준다.

예제 4-49 자식 ApplicationContext 설정

<bean id="target1" class="com.apress.prospring3.ch4.SimpleTarget">

<property name="val">

<ref bean="injectBeanParent"/>

</property>

</bean>

<bean id="target2" class="com.apress.prospring3.ch4.SimpleTarget">

<property name="val">

<ref bean="injectBean"/>

</property>

</bean>

<bean id="target3" class="com.apress.prospring3.ch4.SimpleTarget">

<property name="val">

<ref parent="injectBean"/>

</property>

</bean>

<bean id="injectBean" class="java.lang.String">

<constructor-arg>

<value>Bean In Child</value>

</constructor-arg>

</bean>

여기서는 네 개의 빈을 정의했다. 이 예제에서 injectBean은 부모에 있는 injectBean과 비슷하지

만 나타내는 String 값이 다르다. 이 값을 다르게 지정한 이유는 이 빈이 자식 ApplicationContext

에 있는 빈임을 나타내기 위해서다.

target1 빈은 <ref> 태그의 bean 속성을 사용해 injectBeanParent라는 이름의 빈을 참조한

다. 이 빈은 부모 BeanFactory에만 존재하므로 target1은 이 빈에 대한 참조를 받는다. 여기서 두

가지 점이 중요하다. 먼저 bean 속성을 사용하면 자식 ApplicationContext와 부모 Application

Context에 있는 빈을 모두 참조할 수 있다. 이로 인해 빈을 투명하게 참조할 수 있으며, 애플리케

이션 규모가 커짐에 따라 빈 위치를 다른 설정 파일로 옮길 수 있다. 두 번째로 중요한 점은 local

속성을 사용하면 부모 ApplicationContext에 있는 빈을 참조할 수 없다는 점이다. XML 파서는

local 속성의 값이 같은 파일 내의 유효한 엘리먼트에 존재하는지 검사하므로 이 속성은 부모 컨

텍스트에 있는 빈을 참조하는 데 사용할 수 없다.

Page 135: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 107

target2 빈은 <ref> 태그의 bean 속성을 사용해 injectBean을 참조한다. 이 빈은 두

ApplicationContext에 모두 정의돼 있으므로 target2는 자체 ApplicationContext 내에 있는

injectBean에 대한 참조를 받는다.

target3 빈은 <ref> 태그의 parent 속성을 사용해 부모 ApplicationContext에 있는

injectBean 빈을 직접 참조한다. target3은 <ref> 태그의 parent 속성을 사용하므로 이번에는 자

식 ApplicationContext에 선언한 injectBean은 완전히 무시된다.

예제 4-46의 코드는 자식 BeanFactory에서 이들 세 targetX 빈을 조회함으로써 여기서 설명

한 내용을 보여주고 매번 val 프로퍼티의 값을 출력한다.

다음은 HierarchicalAppContextUsage 클래스(예제 4-46)를 실행한 결과다.

Bean In Parent

Bean In Child

Bean In Parent

예상한 대로 target1과 target3 빈은 둘 다 부모 ApplicationContext에 있는 빈을 참조하는 반

면, target2 빈은 자식 ApplicationContext에 있는 빈을 참조한다.

컬렉션 주입

빈에서 개별 빈이나 값이 아니라 객체의 컬렉션에 접근해야 하는 일이 종종 있다. 따라서 스프링

이 컬렉션 객체를 빈에 주입할 수 있게 해주는 것도 새삼스러운 기능이 아니다. 컬렉션을 사용

하는 법은 간단하다. 이때는 List, Map, Set, Properties를 각각 나타내는 <list>, <map>, <set>,

<props> 중 하나를 선택하고 평상시 주입할 때처럼 개별 항목을 넘겨주면 된다. Properties 클

래스는 프로퍼티 값으로 String만 지원하므로 <props> 태그를 사용하면 String 값만 넘겨줄 수

있다. <list>, <map>, <set>을 사용할 때는 프로퍼티에 주입할 때 다른 태그, 심지어 다른 컬렉션

태그도 얼마든지 사용할 수 있다. 이를 활용해 Map의 List를 넘겨주거나 Set의 Map, List의 Set

으로 구성된 Map의 List도 넘겨줄 수 있다. 예제 4-50은 네 개의 컬렉션 타입을 모두 주입하는

클래스를 보여준다.

예제 4-50 컬렉션 주입(XML)

package com.apress.prospring3.ch4.xml;

import java.util.List;

import java.util.Map;

import java.util.Properties;

Page 136: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

108 l 프로 스프링 3

import java.util.Set;

import org.springframework.context.support.GenericXmlApplicationContext;

public class CollectionInjection {

private Map<String, Object> map;

private Properties props;

private Set set;

private List list;

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

CollectionInjection instance = (CollectionInjection) ctx.getBean("injectCollection");

instance.displayInfo();

}

public void setList(List list) {

this.list = list;

}

public void setSet(Set set) {

this.set = set;

}

public void setMap(Map <String, Object> map) {

this.map = map;

}

public void setProps(Properties props) {

this.props = props;

}

public void displayInfo() {

// Map을 표시

System.out.println("Map contents:\n");

for (Map.Entry<String, Object> entry: map.entrySet()) {

System.out.println("Key: " + entry.getKey() + " - Value: " + entry.getValue());

}

// 프로퍼티를 표시

System.out.println("\nProperties contents:\n");

for (Map.Entry<Object, Object> entry: props.entrySet()) {

System.out.println("Key: " + entry.getKey() + " - Value: " + entry.getValue());

Page 137: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 109

}

// Set을 표시

System.out.println("\nSet contents:\n");

for (Object obj: set) {

System.out.println("Value: " + obj);

}

// List를 표시

System.out.println("\nList contents:\n");

for (Object obj: list) {

System.out.println("Value: " + obj);

}

}

}

이 코드는 꽤 길지만 실제로 하는 일은 거의 없다. main() 메서드는 스프링을 통해 Collection

Injection 빈을 가져오고 displayInfo() 메서드를 호출한다. 이 메서드는 스프링에서 주입한 Map,

Properties, Set, List 인스턴스의 내용을 그냥 출력하는 일을 한다. 예제 4-51에서는 Collection

Injection 클래스에서 이들 프로퍼티 값 각각을 주입하는 데 필요한 설정을 볼 수 있다.

이 예제에서는 Map<String,Object> 프로퍼티 선언도 주의해서 보자. JDK 5 이상 버전에서 스

프링은 강타입의 컬렉션 선언 또한 지원하며, XML 설정을 통해 대응되는 타입에 맞게 적절히 변

환을 수행한다(app-context-xml.xml).

예제 4-51 컬렉션 주입 설정(XML)

<bean id="oracle" name="wiseworm" class="com.apress.prospring3.ch4.BookwormOracle"/>

<bean id="injectCollection" class="com.apress.prospring3.ch4.xml.CollectionInjection">

<property name="map">

<map>

<entry key="someValue">

<value>Hello World!</value>

</entry>

<entry key="someBean">

<ref local="oracle"/>

</entry>

</map>

</property>

<property name="props">

<props>

<prop key="firstName">Clarence</prop>

<prop key="secondName">Ho</prop>

</props>

</property>

<property name="set">

Page 138: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

110 l 프로 스프링 3

<set>

<value>Hello World!</value>

<ref local="oracle"/>

</set>

</property>

<property name="list">

<list>

<value>Hello World!</value>

<ref local="oracle"/>

</list>

</property>

</bean>

이 코드에서는 CollectionInjection 클래스에서 노출한 네 개의 세터 모두에 값을 주입한 것

을 볼 수 있다. map 프로퍼티에는 <map> 태그를 사용해 Map 인스턴스를 주입했다. 각 항목

은 <entry> 태그를 사용해 지정했고, 각각은 String 키와 항목 값을 갖고 있다. 이 항목 값으로

는 프로퍼티를 개별적으로 주입할 때 사용할 수 있는 모든 값을 사용할 수 있다. 이 예제에서는

<value>와 <ref> 태그를 사용해 String 값과 Map의 빈 참조를 추가했다. props 프로퍼티에 대해

서는 <props> 태그를 사용해 java.util.Properties 인스턴스를 생성하고 <prop> 태그를 사용해

내용을 채웠다. <prop> 태그도 <entry> 태그와 비슷한 방식으로 키를 사용하지만 Properties

인스턴스에 들어가는 각 프로퍼티에 대해서만 String 값을 지정할 수 있다.

<list>와 <set> 태그는 사용 방식이 동일하다. 이때는 프로퍼티에 단일 값을 주입할 때 사용하

는 <value>와 <ref> 같은 개별 값 태그를 얼마든지 사용해 각 요소를 지정할 수 있다. 예제 4-51

에서는 String 값과 빈 참조를 List와 Set 모두에 추가한 것을 볼 수 있다.

다음은 예제 4-50을 실행한 결과다. 예상한 대로 설정 파일에서 컬렉션에 추가한 요소 목록을

그대로 볼 수 있다.

Map contents:

Key: someValue - Value: Hello World!

Key: someBean - Value: com.apress.prospring3.ch4.xml.BookwormOracle@2a0ab444

Properties contents:

Key: secondName - Value: Ho

Key: firstName - Value: Clarence

Set contents:

Value: Hello World!

Value: com.apress.prospring3.ch4.xml.BookwormOracle@2a0ab444

Page 139: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 111

List contents:

Value: Hello World!

Value: com.apress.prospring3.ch4.xml.BookwormOracle@2a0ab444

<list>, <map>, <set> 요소를 사용할 때는 비컬렉션 프로퍼티의 값을 설정하는 데 사용하는

태그 중 아무 태그나 사용해 컬렉션 내의 항목 값을 지정할 수 있다는 점을 기억하자. 이 개념은

컬렉션에 원시 값만 주입할 수 있다는 제약을 없애주는 만큼 매우 강력한 개념이다. 즉 빈의 컬렉

션이나 다른 컬렉션도 주입할 수 있는 것이다.

이 기능을 사용하면 애플리케이션을 모듈화하기가 훨씬 쉽고 각기 다른, 애플리케이션 로직의

주요 부분을 사용자가 선택한 구현체 형태로 제공할 수 있다. 회사 직원이 개인용 사무용품을 제

작하고, 교정하고, 주문할 수 있는 시스템을 상상해 보자. 이 시스템에서 작업을 마친 아트워크는

인쇄해서 쓸 수 있게 적절한 프린터로 전달된다. 여기서 복잡한 문제는 일부 프린터는 아트워크

를 이메일로 받고, 일부는 FTP, 일부는 SCP(보안 복사 프로토콜)를 사용한다는 점이다. 스프링

의 컬렉션 주입을 사용하면 예제 4-52에 보이는 것처럼 이 기능에 대한 표준 인터페이스를 만들

수 있다.

예제 4-52 ArtworkSender 인터페이스

package com.apress.prospring3.ch4;

public interface ArtworkSender {

public void sendArtwork(String artworkPath, Recipient recipient);

public String getFriendlyName();

public String getShortName();

}

예제 4-52에서 Recipient 클래스는 비어 있는 클래스다. 이 인터페이스로부터 다양한 구현체

를 만들 수 있다. 예제 4-53을 참고하자.

예제 4-53 FtpArtworkSender 클래스

package com.apress.prospring3.ch4;

public class FtpArtworkSender implements ArtworkSender {

public void sendArtwork(String artworkPath, Recipient recipient) {

// 여기에 ftp 로직을 집어넣음...

Page 140: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

112 l 프로 스프링 3

}

public String getFriendlyName() {

return "File Transfer Protocol";

}

public String getShortName() {

return "ftp";

}

}

그런 다음 ArtworkSender 인터페이스의 모든 구현체를 지원하는 ArtworkManager 클래스

를 개발한다고 가정하자. 구현체를 모두 구현했다면 ArtworkManager 클래스로 구현체의 List

를 그냥 넘겨주면 모든 작업이 끝난다. getFriendlyName() 메서드를 사용하면 시스템 관리자가

여러분이 사무용품 템플릿을 설정할 때 선택할 수 있는 배달 옵션 목록을 보여줄 수 있다. 더불

어 ArtworkSender 인터페이스를 대상으로 코딩하기만 하면 애플리케이션은 개별 구현체와 완

전히 분리될 수 있다. 여기서 ArtworkManager 클래스의 구현은 독자의 몫으로 남긴다.

XML 설정 외에 애노테이션을 사용해 컬렉션을 주입할 수도 있다. 하지만 이때도 유지보수를

쉽게 하려면 컬렉션의 값을 외부화해 설정 파일에 집어넣는 게 좋다. 예제 4-54는 앞의 예제의 컬

렉션 프로퍼티를 그대로 따르는 네 개의 스프링 빈에 대한 설정이다(app-context-annotation.

xml).

예제 4-54 컬렉션 주입 설정(애노테이션)

<util:map id="map" map-class="java.util.HashMap">

<entry key="someValue">

<value>Hello World!</value>

</entry>

<entry key="someBean">

<ref bean="oracle"/>

</entry>

</util:map>

<util:properties id="props">

<prop key="firstName">Clarence</prop>

<prop key="secondName">Ho</prop>

</util:properties>

<util:set id="set">

<value>Hello World!</value>

<ref bean="oracle"/>

</util:set>

Page 141: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 113

<util:list id="list">

<value>Hello World!</value>

<ref bean="oracle"/>

</util:list>

이제 BookwormOracle 클래스의 애노테이션 버전도 개발해 보자. 예제 4-55는 이 클래스의

내용이다.

예제 4-55 BookwormOracle 클래스(애노테이션)

package com.apress.prospring3.ch4.annotation;

import org.springframework.stereotype.Service;

import com.apress.prospring3.ch4.Oracle;

@Service("oracle")

public class BookwormOracle implements Oracle {

public String defineMeaningOfLife() {

return "Encyclopedias are a waste of money - use the Internet";

}

}

예제 4-54의 설정에서는 스프링에서 제공하는 util 네임스페이스를 사용해 컬렉션 프로퍼티

를 저장하는 빈을 선언했다. 이 네임스페이스를 사용하면 과거 스프링 버전보다 설정이 훨씬 쉬

워진다. 테스트 클래스에서는 예제 4-56과 같이 앞의 빈을 주입하고 이름을 지정해 JSR-250 @

Resource 애노테이션을 사용한다.

예제 4-56 컬렉션 주입 설정(애노테이션)

package com.apress.prospring3.ch4.annotation;

// 다른 코드 생략

@Service("injectCollection")

public class CollectionInjection {

@Resource(name="map")

private Map<String, Object> map;

@Resource(name="props")

private Properties props;

@Resource(name="set")

private Set set;

Page 142: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

114 l 프로 스프링 3

@Resource(name="list")

private List list;

// 다른 코드 생략

}

이 테스트 프로그램을 실행하면 XML 설정을 사용한 것과 같은 결과가 나온다.

알아두기

독자들 중에는 @Autowired 대신 @Resource를 사용한 이유가 궁금한 사람도 있을 것이다.

그 이유는 @Autowired 애노테이션은 항상 배열, 컬렉션, 맵을 선언된 값 타입에서 파생된

타깃 빈을 가져와 해당 빈의 배열, 컬렉션, 맵을 처리하기 때문이다. 따라서 예컨대 클래스에

List<Oracle> 타입의 프로퍼티가 있고 @Autowired 애노테이션을 정의하면 스프링은 현재

ApplicationContext 내에서 Oracle 타입의 모든 빈을 이 프로퍼티에 주입하려고 시도한다(

설정 파일에 선언한 <util:list> 대신). 그 결과 예상하지 못한 의존성이 주입되거나 Oracle 타

입의 빈이 정의돼 있지 않아서 스프링이 예외를 던질 수 있다. 따라서 컬렉션 타입의 빈을 주

입할 때는 @Resource 애노테이션이 지원하는 빈 이름을 지정해 스프링에게 명시적으로 주

입 방법을 알려줘야 한다.

메서드 주입

자주 쓰지는 않지만 생성자 주입과 세터 주입 외에 스프링이 제공하는 DI 기능으로 메서드 주입

이 있다. 스프링의 메서드 주입 기능은 룩업 메서드 주입과 메서드 대체라는 두 가지 형태로 제공

된다. 룩업 메서드 주입은 빈이 의존성을 가져올 수 있는 또 다른 메커니즘을 제공하고, 메서드

대체는 원본 소스코드를 수정하지 않고도 빈에서 아무 메서드 구현체나 임의로 바꿀 수 있게 해

준다.

이들 두 기능을 제공하기 위해 스프링은 CGLIB의 동적 바이트코드 향상 기능을 사용한다.

룩업 메서드 주입이나 메서드 대체를 애플리케이션에서 쓰고 싶다면 클래스패스에 CGLIB JAR

파일이 있어야 한다.

룩업 메서드 주입

룩업 메서드 주입은 빈이 생명주기가 같지 않은 다른 빈에 의존할 때 발생하는 문제(특히 싱글턴

이 비싱글턴에 의존하는 문제)를 극복하려고 버전 1.1 이후 스프링에 추가됐다. 이런 상황에서

Page 143: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 115

세터와 생성자 주입을 사용하면 싱글턴이 비싱글턴 빈이어야 할 단일 인스턴스를 유지하는 결과

를 낳는다. 또 어떤 경우에는 싱글턴 빈이 매번 빈을 요청할 때마다 새로운 비싱글턴 인스턴스를

얻게 되기도 한다.

사물함을 열 수 있는 서비스를 제공하는 LockOpener 클래스가 있다고 가정하자. LockOpener

클래스는 KeyHelper 클래스에 의존해 사물함을 열고, KeyHelper는 LockOpener로 주입된

다. 하지만 KeyHelper 클래스는 설계상 재사용에 적합하지 않은 내부 상태를 포함하고 있다.

매번 openLock() 메서드를 호출할 때마다 새로운 KeyHelper 인스턴스가 필요하다. 이 경우

LockOpener는 싱글턴이다. 하지만 일반적인 메커니즘을 사용해 KeyHelper 클래스를 주입하면

매번 같은 KeyHelper 클래스 인스턴스(스프링이 처음 주입을 수행했을 때 생성된 인스턴스)가

재사용된다. 매번 openLock() 메서드를 호출할 때마다 새로운 KeyHelper 인스턴스가 주입되게

하려면 룩업 메서드 주입을 사용해야 한다.

보통 룩업 메서드 주입은 싱글턴 빈이 ApplicationContextAware 인터페이스(이 인터페이스

는 다음 장에서 설명한다)를 구현하게 함으로써 가능하다. 이 인터페이스를 구현하고 나면 싱글

턴 빈은 ApplicationContext 인스턴스를 사용해 의존성이 필요할 때마다 매번 비싱글턴 의존성

의 새로운 인스턴스를 룩업할 수 있다. 룩업 메서드 주입은 싱글턴 빈이 스프링의 특정 인터페이

스를 구현하지 않고도 비싱글턴 의존성이 필요함을 선언하게 해주고, 매번 비싱글턴 빈의 새로

운 인스턴스를 받을 수 있게 해준다.

룩업 메서드 주입은 싱글턴이 비싱글턴 빈 인스턴스를 반환하는 룩업 메서드를 선언하게 함으

로써 동작한다. 애플리케이션에서 싱글턴에 대한 참조를 가져올 때 실제로 받게 되는 참조는 스

프링이 룩업 메서드를 구현하고 동적으로 생성한 하위 클래스에 대한 참조다. 전형적인 구현에는

룩업 메서드 정의가 필요하고, 따라서 빈 클래스는 추상 클래스가 된다. 이는 메서드 주입 설정을

하는 것을 잊어버려서 이상한 에러가 끼어드는 것을 막아주며, 개발자는 스프링에서 개선한 클

래스 대신 빈(empty) 메서드 구현체를 갖고 있는 빈 클래스를 가지고 직접 작업하게 된다. 이 주

제는 매우 복잡하며 예제를 통해 가장 쉽게 이해할 수 있다.

이 예제에서는 비싱글턴 빈 하나와 같은 인터페이스를 구현하는 두 개의 싱글턴 빈을 만든다.

싱글턴 중 하나는 전통적인 세터 주입을 사용해 비싱글턴 빈 인스턴스를 가져온다. 다른 싱글턴

빈은 메서드 주입을 사용한다. 예제 4-57은 이 중 비싱글턴 빈인 MyHelper 빈을 보여준다.

예제 4-57 MyHelper 빈

package com.apress.prospring3.ch4.mi;

Page 144: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

116 l 프로 스프링 3

public class MyHelper {

public void doSomethingHelpful() {

// 뭔가를 한다!

}

}

이 빈은 의도적으로 매우 단순하게 만들었지만 예제에 사용하기에는 충분하다. 예제 4-58에서

는 두 싱글턴 빈이 모두 구현하는 DemoBean 인터페이스를 볼 수 있다.

예제 4-58 DemoBean 인터페이스

package com.apress.prospring3.ch4.mi;

public interface DemoBean {

public MyHelper getMyHelper();

public void someOperation();

}

이 빈은 getMyHelper()와 someOperation()이라는 두 메서드를 갖고 있다. 예제 애플리케이

션은 getMyHelper() 메서드를 사용해 MyHelper 인스턴스에 대한 참조를 가져오고, 룩업 빈의

경우 실제 메서드 룩업을 수행한다. someOperation() 메서드는 MyHelper 클래스에 의존해 작

업을 처리하는 간단한 메서드다.

예제 4-59는 세터 주입을 사용해 MyHelper 클래스 인스턴스를 가져오는 StandardLookup

DemoBean 클래스를 보여준다.

예제 4-59 StandardLookupDemoBean 클래스

package com.apress.prospring3.ch4.mi;

public class StandardLookupDemoBean implements DemoBean {

private MyHelper myHelper;

public void setMyHelper(MyHelper myHelper) {

this.myHelper = myHelper;

}

public MyHelper getMyHelper() {

return this.myHelper;

}

public void someOperation() {

Page 145: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 117

myHelper.doSomethingHelpful();

}

}

이 코드의 내용은 익숙할 것이다. 하지만 someOperation() 메서드가 저장된 MyHelper 인스

턴스를 사용해 작업을 완료하는 것을 주의해서 보자. 예제 4-60에서는 메서드 주입을 사용해

MyHelper 클래스의 인스턴스를 가져오는 AbstractLookupDemoBean 클래스를 볼 수 있다.

예제 4-60 AbstractLookupDemoBean 클래스

package com.apress.prospring3.ch4.mi;

public abstract class AbstractLookupDemoBean implements DemoBean {

public abstract MyHelper getMyHelper();

public void someOperation() {

getMyHelper().doSomethingHelpful();

}

}

getMyHelper() 메서드가 abstract로 선언된 것과 이 메서드를 someOperation() 메서드에서

호출해 MyHelper 인스턴스를 가져오는 것을 주의해서 보자. 예제 4-61에서는 이 예제에 필요한

설정 코드를 볼 수 있다(lookup.xml).

예제 4-61 메서드 룩업 주입 설정

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<bean id="helper" class="com.apress.prospring3.ch4.mi.MyHelper" scope="prototype"/>

<bean id="abstractLookupBean" class="com.apress.prospring3.ch4.mi.AbstractLookupDemoBean">

<lookup-method name="getMyHelper" bean="helper"/>

</bean>

<bean id="standardLookupBean" class="com.apress.prospring3.ch4.mi.StandardLookupDemoBean">

<property name="myHelper">

<ref local="helper"/>

</property>

</bean>

</beans>

Page 146: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

118 l 프로 스프링 3

helper와 standardLookupBean의 설정은 이제 익숙할 것이다. abstractLookupBean의 경

우 <lookup-method> 태그를 사용해 룩업 메서드를 설정해야 한다. <lookup-method> 태그의

name 속성은 스프링이 오버라이드해야 할 메서드의 이름을 스프링에게 알려준다. 이 메서드는

아무런 인자도 받지 않아야 하며, 반환 타입으로는 메서드로부터 반환받으려는 타입을 지정한

다. 이 경우 이 메서드는 MyHelper 또는 하위 클래스를 반환해야 한다. bean 속성은 스프링에게

룩업 메서드가 반환할 빈을 알려준다.

예제 4-62는 이 예제의 마지막 코드다.

예제 4-62 LookupDemo 클래스

package com.apress.prospring3.ch4.mi;

import org.springframework.context.support.GenericXmlApplicationContext;

import org.springframework.util.StopWatch;

public class LookupDemo {

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:lookup.xml");

DemoBean abstractBean = (DemoBean) ctx.getBean("abstractLookupBean");

DemoBean standardBean = (DemoBean) ctx.getBean("standardLookupBean");

displayInfo(standardBean);

displayInfo(abstractBean);

}

public static void displayInfo(DemoBean bean) {

MyHelper helper1 = bean.getMyHelper();

MyHelper helper2 = bean.getMyHelper();

System.out.println("Helper Instances the Same?: "+ (helper1 == helper2));

StopWatch stopWatch = new StopWatch();

stopWatch.start("lookupDemo");

for (int x = 0; x < 100000; x++) {

MyHelper helper = bean.getMyHelper();

helper.doSomethingHelpful();

}

stopWatch.stop();

Page 147: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 119

System.out.println("100000 gets took " + stopWatch.getTotalTimeMillis() + " ms");

}

}

이 코드에서는 abstractLookupBean(추상 클래스의 인스턴스화는 메서드 룩업 주입을 사

용할 때만 지원하며, 이 경우 스프링은 CGLIB을 사용해 AbstractLookupDemoBean 클래스

의 하위 클래스를 생성하고 메서드를 동적으로 오버라이드한다)과 standardLookupBean를

GenericXMLApplicationContext에서 가져오고 각 참조를 displayInfo() 메서드로 넘겨준다.

displayInfo() 메서드의 처음 부분에서는 MyHelper의 두 지역변수를 생성하고 넘겨받은 빈을

사용해 getMyHelper()를 호출해 이들 변수에 값을 대입한다. 이들 두 변수를 사용해 이 메서드

는 두 참조가 같은 객체를 가리키는지 여부를 콘솔에 출력한다. abstractLookupBean 클래스의

경우 매번 getMyHelper()를 호출할 때마다 새로운 MyHelper 인스턴스를 가져오므로 참조가

같지 않다. 하지만 standardLookupBean의 경우 MyHelper의 단일 인스턴스를 세터 주입을 통

해 주입받고, 이 인스턴스가 저장되므로 getMyHelper()를 호출할 때마다 두 참조가 같아진다.

알아두기

앞의 예제에서 사용한 StopWatch 클래스는 스프링에서 사용할 수 있는 유틸리티 클래스다.

StopWatch 클래스는 간단한 성능 테스트를 수행하거나 애플리케이션을 테스트할 때 매우

유용하다.

displayInfo() 메서드의 마지막 부분에서는 간단한 성능 테스트를 수행해 어떤 빈이 더 빠른지

살펴본다. 당연히 standardLookupBean은 같은 매번 같은 인스턴스를 반환하므로 더 빠르지만

그 성능 차이만은 지켜볼 만하다.

예제를 실행하기 전에 프로젝트에 표 4-1에 보이는 CGLIB 의존성을 먼저 추가해야 한다. STS

에서 프로젝트 의존성을 추가하는 법은 부록 A를 참고하자.

표 4-1 메서드 주입에 필요한 의존성

그룹 ID 아티팩트 ID 버전 설명

cglib cglib 2.2.2 스프링에서 메서드 주입을 하는 데 필요한 코드 생성 라이브러리

이제 LookupDemo 클래스(예제 4-62)를 실행해 테스트해 보자. 실행 결과는 다음과 같다.

Page 148: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

120 l 프로 스프링 3

Helper Instances the Same?: true

100000 gets took 3 ms

Helper Instances the Same?: false

100000 gets took 367 ms

보다시피 예상대로 standardLookupBean을 사용할 때는 헬퍼 인스턴스가 같지만 abstract

LookupBean을 사용할 때는 인스턴스가 다르다. 더불어 standardLookupBean을 사용할 때와

비교해 눈에 띄는 성능 차이가 있는 것을 볼 수 있는데, 이는 충분히 예상할 수 있는 사안이다.

메서드 룩업 주입에 대한 고려 사항

메서드 룩업 주입은 생명주기가 서로 다른 두 빈에 대한 작업을 할 때 사용하는 게 본래 용도다.

빈의 생명주기가 같을 때는, 특히 이들 빈이 싱글턴이라면 메서드 룩업 주입을 사용하려는 유혹

을 피해야 한다. 예제 4-62에서는 메서드 주입을 사용해 새로운 의존성 인스턴스를 가져올 때와

표준 DI를 사용해 단일 의존성을 가져올 때의 큰 성능 차이를 볼 수 있다. 더불어 메서드 룩업 주

입은 빈의 생명주기가 서로 다를 때도 불필요하게 사용하지 말아야 한다.

공통 의존성을 갖고 있는 세 개의 싱글턴이 있다고 가정하자. 이때 각 싱글턴이 자신만의 의존

성 인스턴스를 갖게끔 비싱글턴 의존성을 생성할 수도 있겠지만, 이 경우 각 싱글턴이 자신의 생

명주기 동안 같은 인스턴스를 사용하더라도 아무 문제가 없다. 이 경우에는 세터 주입이 가장 이

상적인 해결책이다. 메서드 룩업 주입은 불필요한 과부하만 더할 뿐이다.

메서드 룩업 주입을 사용할 때는 클래스를 개발하면서 염두에 둬야 할 몇 가지 설계 가이드라

인이 있다. 앞의 예에서 룩업 메서드는 인터페이스에 선언했다. 이렇게 한 이유는 단지 각기 다른

두 개의 빈 타입에서 displayInfo() 메서드를 반복하고 싶지 않았기 때문이다. 앞에서 말한 것처

럼 보통 비즈니스 인터페이스는 IoC 용도로만 사용하는 불필요한 정의로 인해 지저분해지지 않

게 해야 한다. 또 다른 주의할 점은 룩업 메서드를 추상 메서드로 만들 필요는 없지만 이렇게 하

면 룩업 메서드를 설정하는 것을 까먹고 실수로 빈 구현체를 사용하는 일을 막을 수 있다.

메서드 대체

스프링 문서에서는 메서드 대체를 주입 방식으로 구분하고 있지만 메서드 대체는 지금까지 살펴

본 내용과는 매우 다르다. 지금까지는 빈에 협력 객체를 제공하기 위해 주입을 사용했다. 메서드

대체를 사용하면 빈의 소스코드를 수정하지 않고도 아무 빈에서나 메서드 구현체를 임의로 수

정할 수 있다. 예를 들어 스프링 애플리케이션에서 사용 중인 서드파티 라이브러리가 있고 특정

메서드의 로직을 변경해야 한다고 가정하자. 하지만 서드파티에서 제공했기 때문에 소스를 수정

Page 149: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 121

할 수 없다면 메서드 대체를 활용해 해당 메서드를 커스텀 구현체로 대체할 수 있다.

이때 내부적으로는 빈 클래스의 하위 클래스를 동적으로 생성해 이 작업을 수행한다. 이를 위

해 CGLIB을 사용하고 대체하려는 메서드에 대한 호출을 MethodReplacer 인터페이스를 구현

하는 다른 빈으로 리다이렉트한다.

예제 4-63에서는 두 개의 오버로드된 formatMessage() 메서드가 들어 있는 간단한 빈을 볼

수 있다.

예제 4-63 ReplacementTarget 클래스

package com.apress.prospring3.ch4.mi;

public class ReplacementTarget {

public String formatMessage(String msg) {

return "<h1>" + msg + "</h1>";

}

public String formatMessage(Object msg) {

return "<h1>" + msg + "</h1>";

}

}

스프링의 메서드 대체 기능을 사용하면 ReplacementTarget 클래스의 메서드를 얼마든지 대

체할 수 있다. 이 예제에서는 formatMessage(String) 메서드를 대체하는 법을 보여주고 대체한

메서드와 원본 메서드의 성능도 비교해본다.

메서드를 대체하려면 먼저 MethodReplacer 인터페이스의 구현체를 생성해야 한다. 예제

4-64에 이 구현체가 나와 있다.

예제 4-64 MethodReplacer의 구현

package com.apress.prospring3.ch4.mi;

import java.lang.reflect.Method;

import org.springframework.beans.factory.support.MethodReplacer;

public class FormatMessageReplacer implements MethodReplacer {

public Object reimplement(Object arg0, Method method, Object[] args)

throws Throwable {

if (isFormatMessageMethod(method)) {

Page 150: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

122 l 프로 스프링 3

String msg = (String) args[0];

return "<h2>" + msg + "</h2>";

} else {

throw new IllegalArgumentException("Unable to reimplement method "

+ method.getName());

}

}

private boolean isFormatMessageMethod(Method method) {

// 인자 개수가 정확한지 확인

if (method.getParameterTypes().length != 1) {

return false;

}

// 메서드 이름을 확인

if (!("formatMessage".equals(method.getName()))) {

return false;

}

// 반환 타입을 확인

if (method.getReturnType() != String.class) {

return false;

}

// 인자 타입이 정확한지 확인

if (method.getParameterTypes()[0] != String.class) {

return false;

}

return true;

}

}

MethodReplacer 인터페이스는 reimplement() 메서드를 하나만 갖고 있으며, 이 메서드

는 반드시 구현해야 한다. reimplement()로 전달하는 세 개의 인자는 원본 메서드를 호출한

bean, 오버라이드할 메서드를 나타내는 Method 인스턴스, 메서드로 넘길 인자의 배열이다.

reimplement() 메서드는 재구현한 로직의 결과를 반환해야 하며, 당연히 반환 타입은 대체하려

는 원본 메서드의 반환 타입과 호환돼야 한다. 예제 4-64에서 FormatMessageReplacer는 먼저

오버라이드되는 메서드가 formatMessage(String) 메서드인지 확인한다. 이 메서드가 맞다면 대

체 로직을 수행한다. 이 예제에서는 메시지를 <h2>와 </h2>로 감싼 다음 호출자에게 형식을 수

정한 메시지를 반환한다. 이때 메시지가 정확한지는 검사하지 않아도 되지만 비슷한 인자를 사

Page 151: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 123

용하는 MethodReplacer가 몇 개 있다면 메시지를 검사하는 게 도움이 될 수 있다. 메시지 검사

기능을 사용하면 호환되는 인자와 반환 타입을 갖고 있는 다른 MethodReplacer를 실수로 사용

하는 것을 막을 수 있다.

예제 4-65는 ReplacementTarget 타입의 두 빈을 정의하는 ApplicationContext를 보여준

다. 두 빈 중 하나는 formatMessage(String) 메서드가 대체되고 다른 빈은 대체되지 않는다

(replacement.xml).

예제 4-65 메서드 대체 설정

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

<bean id="methodReplacer" class="com.apress.prospring3.ch4.mi.FormatMessageReplacer"/>

<bean id="replacementTarget" class="com.apress.prospring3.ch4.mi.ReplacementTarget">

<replaced-method name="formatMessage" replacer="methodReplacer">

<arg-type>String</arg-type>

</replaced-method>

</bean>

<bean id="standardTarget" class="com.apress.prospring3.ch4.mi.ReplacementTarget"/>

</beans>

예제 4-65에서 볼 수 있듯이 MethodReplacer 구현체는 ApplicationContext에서 빈으

로 선언한다. 그런 다음 <replaced-method> 태그를 사용해 replacementTargetBean에서

formatMessage(String) 메서드를 대체한다. <replaced-method> 태그의 name 속성은 대체할

메서드의 이름을 나타내며, replacer 속성은 메서드 구현체를 대체하려는 MethodReplacer의 이

름을 지정한다. ReplacementTarget 클래스에 오버로드된 메서드가 있는 경우에는 <arg-type>

태그를 사용해 메서드 시그너처가 일치하게끔 지정할 수 있다. <arg-type>는 패턴 매칭을 지원

하므로 String은 java.lang.String과 java.lang.StringBu�er로 매칭된다.

예제 4-66은 ApplicationContext에서 standardTarget과 replacementTarget 빈을 가져와

formatMessage(String) 메서드를 실행하고 어떤 메서드가 더 빠른지 비교하는 간단한 성능 테

스트를 수행하는 예제다.

Page 152: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

124 l 프로 스프링 3

예제 4-66 메서드 대체 실행

package com.apress.prospring3.ch4.mi;

import org.springframework.context.support.GenericXmlApplicationContext;

import org.springframework.util.StopWatch;

public class MethodReplacementExample {

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:replacement.xml");

ctx.refresh();

ReplacementTarget replacementTarget = (ReplacementTarget) ctx

.getBean("replacementTarget");

ReplacementTarget standardTarget = (ReplacementTarget) ctx

.getBean("standardTarget");

displayInfo(replacementTarget);

displayInfo(standardTarget);

}

private static void displayInfo(ReplacementTarget target) {

System.out.println(target.formatMessage("Hello World!"));

StopWatch stopWatch = new StopWatch();

stopWatch.start("perfTest");

for (int x = 0; x < 1000000; x++) {

String out = target.formatMessage("foo");

}

stopWatch.stop();

System.out.println("1000000 invocations took: "

+ stopWatch.getTotalTimeMillis() + " ms");

}

}

이 코드는 지금쯤 꽤 익숙할 테니 자세히 설명하지는 않겠다. 우리 장비에서 이 예제를 실행해

보니 다음과 같은 결과가 나왔다.

<h2>Hello World!</h2>

1000000 invocations took: 363 ms

<h1>Hello World!</h1>

1000000 invocations took: 107 ms

Page 153: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 125

예상대로 replacementTarget 빈의 출력 결과는 MethodReplacer가 제공하는 오버라이드된

구현체를 반영한다. 하지만 재미있게도 동적으로 대체한 메서드가 정적으로 정의한 메서드보다

3배 이상 느렸다. MethodReplacer의 메서드에서 유효한 메서드를 검사하는 로직을 제거한 후 여

러 번 실행하더라도 성능 차이는 거의 없었다. 이런 결과를 보면 대부분의 연산 부담이 CGLIB의

하위 클래스로 인한 것으로 결론 내릴 수 있다.

메서드 대체를 사용해야 할 때

메서드 대체는 다양한 환경에서 매우 유용하다. 특히 같은 타입의 모든 빈이 아니라 특정 빈의

특정 메서드만 오버라이드해야 할 때 효과적이다. 하지만 여전히 런타임 바이트코드 개선 기능

에 의존하기보다는 표준 자바 메커니즘을 사용해 메서드를 오버라이드하는 게 더 바람직하다.

애플리케이션의 일부 영역에서 메서드 대체 기능을 사용한다면 메서드별로 또는 오버로드된

메서드 그룹별로 MethodReplacer를 하나만 사용할 것을 권장한다. MethodReplacer를 한 개

만 사용해 수많은 관련 없는 메서드를 대체하려는 유혹을 피해야 한다. 이렇게 하면 코드에서 어

떤 메서드를 대체해야 하는지 판단하는 동안 불필요한 문자열 비교가 수없이 일어난다. 간단한

검사를 수행하면 MethodReplacer가 항상 올바른 메서드에 적용할 수 있고 코드에도 큰 부담을

주지 않는다. 성능에 대해 걱정된다면 MethodReplacer에 불리언 프로퍼티를 추가해 의존성 주

입을 사용할 때 이 검사 로직을 활성화하거나 비활성화하면 된다.

빈 이름 지정 방식의 이해

스프링은 복잡한 빈 이름 지정 구조를 지원하며 이로 인해 여러 상황을 유연하게 처리할 수 있

다. 모든 빈은 자신이 속하는 ApplicationContext 내에서 적어도 하나의 고유 이름을 갖고 있어

야 한다. 스프링은 간단한 절차를 따라 빈이 사용하는 이름을 판단한다. <bean> 태그에 id 속성

을 지정하면 이 속성의 값이 이름으로 사용된다. id 속성을 지정하지 않으면 스프링은 name 속

성을 찾고, 이 속성이 지정돼 있으면 name 속성에 지정된 첫 번째 이름을 사용한다(여기서 첫

번째 이름이라고 말하는 이유는 name 속성 내에 여러 이름을 지정할 수 있기 때문이다. 이 내

용은 잠시 후 좀 더 자세히 다룬다). id와 name 속성 둘 다 지정하지 않으면 스프링은 빈의 클래

스명을 이름으로 사용한다. 물론 이때는 같은 클래스 이름을 사용하는 다른 빈이 없다는 전제

가 있어야 한다. id와 name 속성이 지정되지 않은 여러 빈이 같은 클래스명을 사용하면 스프링

은 ApplicationContext 초기화 중 빈을 주입할 때 예외를 던진다(org.springframework.beans.

factory.NoSuchBeanDe�nitionException 타입). 예제 4-67에서는 이런 세 가지 이름 스키마를

모두 사용하는 예제 설정을 볼 수 있다.

Page 154: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

126 l 프로 스프링 3

예제 4-67 빈 이름 지정

<bean id="string1" class="java.lang.String"/>

<bean name="string2" class="java.lang.String"/>

<bean class="java.lang.String"/>

이들 방식은 기술적인 관점에서는 모두 유효하다. 하지만 애플리케이션에서 가장 적합한 방식

은 어떤 방식일까? 먼저 클래스명을 이름으로 자동으로 사용하는 방식은 피해야 한다. 이 방식을

사용하면 같은 타입의 여러 빈을 마음대로 정의할 수 없으며, 직접 이름을 정하는 게 훨씬 낫다.

그럼 나중에 스프링이 기본 동작을 바꾸더라도 애플리케이션이 계속해서 동작할 수 있다. id와

name 중 선택할 때는 항상 id를 사용해 빈의 기본 이름을 지정하는 게 좋다. 스프링 3.1 이전에

는 id 속성이 XML id(즉, xsd:ID)와 같아서 사용할 수 있는 문자에 제한이 있었다. 하지만 스프

링 3.1부터 스프링은 id 속성에 xsd:String를 사용하므로 기존의 문자 제약이 이제는 사라졌다.

하지만 이렇게 바뀌었더라도 스프링은 계속해서 전체 ApplicationContext 내에서 id가 고유하

도록 보장해준다. 일반적으로 빈의 이름은 id로 지정하고 다른 이름을 사용하는 빈과 연계할 때

는 다음 절에서 사용하는 이름 별칭을 사용하는 게 좋다.

빈 이름 별칭

스프링에서는 빈이 두 개 이상의 이름을 가질 수 있다. 빈에 둘 이상의 이름을 지정하려면 공백,

콤마, 세미콜론으로 이름을 구분해 빈의 <bean> 태그의 name 속성에 입력하면 된다. 이런 이름

지정 방식은 id 속성 지정 대신 사용할 수도 있고, id 속성과 연계해 사용할 수도 있다.

name 속성 외에 <alias> 태그를 사용해 스프링 빈 이름의 별칭을 정의할 수도 있다. 예제 4-68

에서는 빈 하나에 여러 이름을 정의한 간단한 <bean> 설정을 보여준다(app-context-xml.xml).

예제 4-68 여러 빈 이름 설정

<bean id="name1" name="name2 name3,name4;name5" class="java.lang.String"/>

<alias name="name1" alias="name6"/>

이 예제에서는 보다시피 6개의 이름을 지정했다. 이름 중 하나는 id 속성을 사용해 지정했고

다른 네 개의 이름은 빈 이름 구분자를 모두 사용해 목록으로 지정했다(이는 단순한 예시 용도

이지 실제 개발에서는 권장하지 않는다). 물론 실제 개발에서는 애플리케이션 내에서 빈 이름을

선언할 때 구분자로 사용할 구분자를 표준으로 정할 것을 권장한다. 끝으로 <alias> 태그를 사

용해 별칭을 하나 더 추가했다. 예제 4-69는 각기 다른 이름을 사용해 ApplicationContext에서

같은 빈을 가져와 이들 빈이 서로 같은지 확인하는 간단한 자바 루틴을 보여준다.

Page 155: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 127

예제 4-69 별칭을 사용한 빈 접근

package com.apress.prospring3.ch4.xml;

import org.springframework.context.support.GenericXmlApplicationContext;

public class BeanNameAliasing {

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

String s1 = (String) ctx.getBean("name1");

String s2 = (String) ctx.getBean("name2");

String s3 = (String) ctx.getBean("name3");

String s4 = (String) ctx.getBean("name4");

String s5 = (String) ctx.getBean("name5");

String s6 = (String) ctx.getBean("name6");

System.out.println((s1 == s2));

System.out.println((s2 == s3));

System.out.println((s3 == s4));

System.out.println((s4 == s5));

System.out.println((s5 == s6));

}

}

이 코드는 예제 4-68의 설정에 대해 다섯 번 콘솔에 true를 출력해 각기 다른 이름을 사용해

접근한 빈이 사실상 같은 빈임을 확인시켜준다.

빈 별칭 목록은 ApplicationContext.getAliases(String)을 호출하고 인자로 빈의 이름이나 ID

중 아무거나 넘기면 가져올 수 있다. 이렇게 하면 지정한 이름 외에 별칭 목록이 String 배열로 반

환된다.

빈 이름 별칭은 새 애플리케이션을 개발할 때는 보통 사용하지 않는다. 한 개의 빈을 주입하는

여러 빈이 있다면 같은 이름을 사용해 해당 빈에 접근하는 게 더 낫다. 하지만 애플리케이션을

배포하고 유지보수를 하고, 애플리케이션을 수정하다 보면 빈 이름 별칭이 유용해질 때가 있다.

다음 시나리오를 고려해보자. 애플리케이션에 50개의 각기 다른 빈을 스프링으로 설정했고,

이들 빈이 모두 Foo 인터페이스의 구현체를 필요로 한다. 이들 빈 중 25개는 standardFoo라는

빈 이름의 StandardFoo 구현체를 사용하고, 나머지 25개는 superFoo라는 빈 이름의 SuperFoo

Page 156: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

128 l 프로 스프링 3

구현체를 사용한다. 배포 후 6개월이 지났을 때 여러분은 처음 25개의 빈을 SuperFoo 구현체로

옮기기로 결정한다. 이때 다음과 같은 세 가지 옵션을 사용할 수 있다.

▒ 첫 번째 옵션은 standardFoo 빈 클래스의 구현체를 SuperFoo로 바꾸는 것이다. 이 방식

의 단점은 실제로는 한 개만 필요한데도 SuperFoo 클래스 인스턴스가 두 개가 생긴다는

점이다. 더불어 이제 설정이 바뀌면 두 빈을 모두 수정해야 한다.

▒ 두 번째 옵션은 수정하려는 25개의 빈에 대한 주입 설정을 수정해 빈의 이름을

standardFoo에서 superFoo로 바꾸는 것이다. 이 방식은 가장 우아한 방식은 아니다. 찾

아서 바꾸기를 수행해 바꿀 수는 있지만 나중에 수정 사항을 되돌리려면 버전 관리 시스

템에서 과거 설정 파일을 다시 찾아봐야 하기 때문이다.

▒ 세 번째 옵션이자 가장 좋은 방식은 standardFoo 빈에 대한 정의를 제거(또는 주석 처리)

하고 standardFoo를 superFoo의 별칭으로 만드는 것이다. 이렇게 하면 최소한의 노력만

들고 과거 설정으로 복원하는 일도 아주 간단하다.

빈 인스턴스화 모드

기본적으로 스프링의 모든 빈은 싱글턴이다. 이 말은 스프링이 빈의 단일 인스턴스를 유지하며,

의존 객체가 모두 단일 인스턴스를 사용하고, ApplicationContext.getBean()를 호출할 때마다

항상 같은 인스턴스가 반환된다는 뜻이다. 이는 예제 4-64에서 equals() 비교 대신 비교 연산자

(==)를 사용해 두 빈이 같은지 비교하면서 살펴본 바 있다.

싱글턴이라는 용어는 자바에서 두 가지 개념을 가리키는 데 혼용해서 사용한다. 즉, 애플리케

이션 내에서 단일 인스턴스를 갖는 객체라는 개념과 싱글턴 디자인 패턴이다. 이 책에서 싱글턴

이라고 말하면 첫 번째 개념을 가리키고 싱글턴 패턴은 싱글턴 디자인 패턴이라고 부른다. 싱글

턴 디자인 패턴은 Design Patterns: Elements of Reusable Object Oriented So�ware by Erich

Gamma, et al. (Addison-Wesley, 1995)를 통해 대중화됐다. 하지만 문제는 사람들이 싱글턴 패

턴이 필요한 시점을 혼동하는 데서 발생한다. 예제 4-70은 자바에서 전형적인 싱글턴 패턴 구현

체를 보여준다.

Page 157: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 129

예제 4-70 싱글턴 디자인 패턴

package com.apress.prospring3.ch4;

public class Singleton {

private static Singleton instance;

static {

instance = new Singleton();

}

public static Singleton getInstance() {

return instance;

}

}

이 패턴은 애플리케이션 내에서 클래스에 단일 인스턴스를 유지하고 이에 접근하게 한다는 소

정의 목적을 달성하지만 결합도를 증가시킨다. 애플리케이션 코드가 이 인스턴스를 가져오려면

싱글턴 클래스에 대한 명확한 지식을 갖고 있어야 한다. 이로 인해 인터페이스로 코딩할 수 있는

능력이 완전히 사라진다. 실제로 싱글턴 패턴은 사실 두 패턴이 하나로 합쳐진 것이다. 첫 번째 패

턴은 객체의 단일 인스턴스를 유지하는 것과 관련이 있다. 두 번째 패턴은 인터페이스를 사용할

수 있는 가능성을 완전히 없애는 객체 룩업 패턴이다. 싱글턴 패턴을 사용하면 싱글턴 인스턴스

를 필요로 하는 객체가 대부분 싱글턴 객체에 직접 접근하므로 구현체를 임의로 바꾸기도 매우

어렵다. 이로 인해 목(mock) 객체로 싱글턴을 대신해 테스트할 수도 없으므로 애플리케이션을

단위 테스트하려고 할 때 온갖 골치 아픈 일이 벌어진다.

다행히 스프링에서는 싱글턴 디자인 패턴을 사용하지 않고도 싱글턴 인스턴스화 모델을 활용

할 수 있다. 스프링의 모든 빈은 기본적으로 싱글턴 인스턴스로 생성되며, 스프링은 같은 인스턴

스를 사용해 모든 빈에 대한 요청을 처리한다. 물론 스프링에서 싱글턴 인스턴스만 사용할 수 있

는 것은 아니다. 스프링은 매번 의존성을 요청하거나 getBean()을 호출할 때마다 새로운 빈 인스

턴스를 생성해줄 수도 있다. 스프링은 애플리케이션 코드에 아무런 영향도 주지 않고 이런 작업

을 모두 처리하므로 이런 점에서 스프링은 인스턴스화 모드에서 자유롭다고 한다. 이 개념은 매

우 강력한 개념이다. 처음에는 싱글턴 객체를 사용했다가 나중에 멀티 스레드에 접근할 때 싱글

턴이 적합하지 않음을 깨달았다면 애플리케이션 코드를 수정하지 않고도 객체를 비싱글턴으로

바꿀 수 있다.

Page 158: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

130 l 프로 스프링 3

알아두기

인스턴스화 모드를 바꾸는 게 애플리케이션 코드에는 영향을 주지 않지만 스프링의 생명주기

인터페이스에 의존할 경우 문제를 일으킬 수 있다. 이 내용은 5장에서 자세히 다룬다.

싱글턴에서 비싱글턴으로 인스턴스화 모드를 바꾸는 일은 간단히 할 수 있다(예제 4-71의

app-context-xml.xml 파일 참고).

예제 4-71 비싱글턴 빈 설정

<bean id="nonSingleton" class="java.lang.String" scope="prototype">

<constructor-arg>

<value>Clarence Ho</value>

</constructor-arg>

</bean>

이 빈 선언과 지금까지 본 선언의 차이점은 scope 속성을 추가하고 값을 prototype으로 선언

한 것뿐이다. 스프링은 기본적으로 이 스코프를 singleton으로 설정한다. prototype 스코프는

스프링에게 매번 애플리케이션에서 빈 인스턴스를 요청할 때마다 새 빈 인스턴스를 만들도록 지

시한다. 예제 4-72는 이 설정을 애플리케이션에 적용한 결과를 보여준다.

예제 4-72 비싱글턴 빈을 사용하는 예제

package com.apress.prospring3.ch4;

import org.springframework.context.support.GenericXmlApplicationContext;

public class NonSingleton {

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

ctx.refresh();

String s1 = (String) ctx.getBean("nonSingleton");

String s2 = (String) ctx.getBean("nonSingleton");

System.out.println("Identity Equal?: " + (s1 ==s2));

System.out.println("Value Equal:? " + s1.equals(s2));

System.out.println(s1);

System.out.println(s2);

}

}

Page 159: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 131

이 예제를 실행하면 다음 결과가 출력된다.

Identity Equal?: false

Value Equal:? true

Clarence Ho

Clarence Ho

이 결과를 보면 두 개의 String 객체 값은 분명히 일치하지만 같은 빈 이름을 사용해 두 인스턴

스를 조회했음에도 빈 객체가 서로 다른 것을 볼 수 있다.

인스턴스화 모드의 선택

대부분의 시나리오에서는 어떤 인스턴스화 모드가 적당한지 쉽게 알 수 있다. 보통은 빈의 기본

모드로 싱글턴을 사용한다. 일반적으로 싱글턴은 다음 상황에서 사용한다.

▒ 상태가 없는 공유 객체: 상태를 유지하지 않고 많은 의존 객체를 갖고 있는 객체의 경우.

상태가 없다면 동기화가 필요 없으므로 의존 객체가 인스턴스를 필요할 때 매번 새로운

빈 인스턴스를 생성하지 않아도 된다.

▒ 읽기 전용 상태를 갖는 공유 객체: 앞의 경우와 비슷한 경우로, 읽기 전용 상태만 있을 때

다. 이 경우도 마찬가지로 동기화가 필요 없으므로 빈을 요청할 때마다 인스턴스를 생성

하면 불필요한 연산 부담만 늘어난다.

▒ 공유 상태를 갖는 공유 객체: 상태를 공유해야 하는 객체가 있다면 싱글턴이 이상적인 선

택이다. 이 경우 상태에 대한 쓰기 동기화가 가능한 한 최소한으로 이뤄지게끔 해야 한다.

▒ 쓰기 상태를 갖는 다량의 객체: 애플리케이션에서 수없이 사용하는 빈이 있다면 빈 인스

턴스를 계속해서 수백 개 생성하는 대신 싱글턴을 유지하고 빈 상태에 대한 쓰기 접근을

모두 동기화해 성능을 개선하는 것이 좋다. 이 방식을 사용할 때는 일관성을 희생하지 않

으면서 가능한 한 쓰기 동기화를 최소 규모로 유지해야 한다. 애플리케이션이 오랜 시간

에 걸쳐 많은 양의 인스턴스를 생성하고, 공유 객체에 써야 하는 상태의 양이 적을 때, 또

는 새 인스턴스의 생성 비용이 클 때 이 접근 방식이 특히 효과적이다.

다음 상황에서는 비싱글턴 사용을 고려해야 한다.

▒ 쓰기 상태가 있는 객체: 다양한 쓰기 상태를 갖고 있는 빈이라면 의존 객체가 매번 요청할

때마다 새 인스턴스를 생성하는 비용보다 동기화 비용이 더 크지 않은지 고려해야 한다.

Page 160: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

132 l 프로 스프링 3

▒ 내부 상태를 갖는 객체: 때로는 의존 객체가 협력 객체 빈에 의존하는 다른 의존 객체와

는 구분된 작업을 처리하기 위해 내부 상태를 갖는 빈을 필요로 할 때가 있다. 이때는 싱

글턴이 적합하지 않으며 비싱글턴을 사용해야 한다.

스프링의 인스턴스화 관리 기능의 주된 장점은 애플리케이션이 싱글턴 사용으로 인해 메모리

부담을 덜 수 있고, 이 과정에서 개발자의 추가 노력은 거의 들지 않는다는 점이다. 또 애플리케

이션에 싱글턴이 적합하지 않다고 판단하면 간단히 설정을 수정해 비싱글턴 모드를 사용할 수

있다.

빈 스코프

singleton과 prototype 스코프 외에 스프링 빈을 구체적인 용도에 따라 정의할 수 있게 다른 스

코프도 제공된다. 또 커스텀 스코프를 구현하고 스프링의 ApplicationContext에 등록할 수도

있다. 다음은 버전 3.1에서 지원하는 빈 스코프다.

▒ Singleton: 기본 싱글턴 스코프

▒ Prototype: 애플리케이션에서 요청할 때마다 스프링이 새 인스턴스를 생성

▒ Request: 웹 애플리케이션 용도. 스프링 MVC를 웹 애플리케이션에서 사용할 때 빈 요청

스코프는 모든 HTTP 요청별로 인스턴스화되며 요청이 끝나면 소멸된다.

▒ Session: 웹 애플리케이션 용도. 스프링 MVC를 웹 애플리케이션에서 사용할 때 세션 스

코프를 갖고 있는 빈은 모든 HTTP 세션별로 인스턴스화되며 세션이 끝나면 소멸된다.

▒ Global session: 포틀릿 기반의 웹 애플리케이션 용도. 전역 세션 스코프의 빈은 같은 스

프링 MVC를 사용한 포탈 애플리케이션 내의 모든 포틀릿 사이에서 공유할 수 있다.

▒ �read: 새 스레드에서 요청하면 스프링이 새로운 빈 인스턴스를 생성하고, 같은 스레드

에 대해서는 항상 같은 빈 인스턴스가 반환된다. 이 스코프는 기본으로 등록되지 않는 데

주의한다.

▒ Custom: 커스텀 빈 스코프는 org.springframework.beans.factory.con�g.Scope를 구현

하고 커스텀 스코프를 스프링의 설정에 등록해 생성할 수 있다(예를 들어 XML의 경우

org.springframework.beans.factory.con�g.CustomScopeCon�gurer 클래스를 사용).

Page 161: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 133

의존성 리졸브

일반 작업에서 스프링은 설정 파일을 살펴보거나 클래스의 애노테이션을 참고해 의존성을 리졸

브할 수 있다. 이런 과정을 통해 스프링은 각 빈이 의존성을 정확히 설정되도록 올바른 순서대로

각 빈을 설정한다. 스프링이 이 작업을 수행하지 않고 그냥 빈을 생성하고 아무 순서로나 설정한

다면 의존성보다 앞서 빈이 생성되고 설정될 여지가 있다. 이는 당연히 우리가 원하는 결과가 아

니며 애플리케이션에서 여러 문제를 일으킬 수 있다.

아쉽게도 스프링은 코드 내에 존재하는 빈 사이의 의존성은 인식하지 못한다. 예를 들어 A라

는 빈이 생성자에서 getBean()을 호출해 또 다른 빈인 B의 인스턴스를 가져온다고 가정하자. 예

를 들어 BeanA의 생성자에서 ctx.getBean("beanB")를 호출해 BeanB 인스턴스를 가져오면서

스프링에게 의존성을 주입하라고 명령하지 않았다고 가정하자. 이 경우 스프링은 빈 A가 빈 B

에 의존한다는 사실을 모르므로 결과적으로 빈 B보다 빈 A를 먼저 생성하게 된다. <bean> 태그

의 depends-on 속성을 사용하면 빈 의존성에 대한 추가 정보를 스프링에 제공할 수 있다. 예제

4-73은 빈 A와 빈 B를 시나리오가 어떻게 설정됐는지를 보여준다.

예제 4-73 의존성의 수동 설정

<bean id="beanA" class="com.apress.prospring.ch4.BeanA" depends-on="beanB"/>

<bean id="beanB" class="com.apress.prospring.ch4.BeanB"/>

이 설정에서는 beanA가 beanB 빈에 의존하다고 명시적으로 알려준다. 스프링은 인스턴스화

시점에 이를 고려해 beanB를 beanA보다 항상 먼저 생성해준다.

애플리케이션을 개발할 때는 애플리케이션에서 이 기능을 사용하도록 설계하지 말아야 한다.

대신 세터와 생성자 주입 계약을 통해 의존성을 정의해야 한다. 하지만 스프링을 레거시 코드와

연계할 때는 코드에서 정의한 의존성에 따라 스프링 프레임워크에 추가 정보를 제공해야 하는

일이 필요할 수 있다.

빈 자동 연결

지금까지 살펴본 예제에서는 설정 파일을 통해 개별 빈이 어떻게 연결되는지 명시적으로 정의해

야 했다. 모든 컴포넌트를 직접 연결해야 하는 게 싫다면 스프링이 이 작업을 자동으로 수행하게

해도 된다. 기본적으로 자동 연결은 비활성화돼 있다. 이를 활성화하려면 자동 연결하려는 빈의

autowire 속성을 사용해 자동 연결 방식을 지정해야 한다.

Page 162: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

134 l 프로 스프링 3

자동 연결 모드

스프링은 byName, byType, constructor, default, no(이 값은 default와 같다)로 네 가지 자동 연

결 모드를 지원한다. byName 자동 연결을 사용하면 스프링은 프로퍼티를 같은 이름의 빈으로

자동 연결한다. 따라서 타깃 빈이 foo라는 프로퍼티가 있고 ApplicationContext에 foo 빈이 정

의돼 있다면 foo 빈은 타깃의 foo 프로퍼티로 대입된다.

byType 자동 연결을 사용하면 스프링은 타깃 빈의 각 프로퍼티를 ApplicationContext 내

의 동일 타입의 빈으로 자동으로 연결한다. 따라서 타깃 빈에 String 타입의 프로퍼티가 있고

ApplicationContext에 String 타입의 빈이 있다면 스프링은 String 빈을 타깃 빈의 String 프로

퍼티에 자동으로 연결한다. 같은 ApplicationContext에서 같은 타입(이 경우 String)의 빈이 둘

이상이라면 스프링은 둘 중 어떤 빈을 자동 연결해야 할지 결정을 못 내리고 예외를 던진다(org.

springframework.beans.factory.NoSuchBeanDe�nitionException 타입).

constructor 자동 연결 모드는 byType 자동 연결과 비슷하지만 주입할 때 세터 대신 생성자를

사용한다는 점이 다르다. 스프링은 생성자에서 가장 많은 인자를 사용하는 생성자에 자동 연결

하려고 시도한다. 따라서 생성자가 두 개이고, 한 생성자는 String을 받아들이고, 다른 생성자는

String과 Integer를 인자로 받으며, ApplicationContext 내에 String과 Integer 빈이 둘 다 있다

면 스프링은 두 인자가 있는 생성자를 사용한다.

default 모드에서 스프링은 constructor와 byType 모드 중 자동으로 선택한다. 빈이 기본(인자

없는) 생성자를 갖고 있다면 스프링은 byType을 사용한다. 그렇지 않은 경우에는 constructor를

사용한다.

예제 4-74는 각기 다른 이들 모드를 사용해 세 개의 빈을 자동 연결하는 예제 설정이다

(autowiring.xml).

예제 4-74 자동 연결 설정

<bean id="foo" class="com.apress.prospring3.ch4.autowiring.Foo"/>

<bean id="bar1" class="com.apress.prospring3.ch4.autowiring.Bar"/>

<bean id="targetByName" autowire="byName"

class="com.apress.prospring3.ch4.autowiring.Target"

lazy-init="true"/>

<bean id="targetByType" autowire="byType"

class="com.apress.prospring3.ch4.autowiring.Target"

lazy-init="true"/>

<bean id="targetConstructor" autowire="constructor"

class="com.apress.prospring3.ch4.autowiring.Target"

lazy-init="true"/>

Page 163: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 135

이 설정은 이제 매우 익숙할 것이다. Foo와 Bar는 빈 클래스다. 각 타깃 빈의 autowire 속성에

다른 값을 지정한 것을 주의해서 보자. 더불어 테스트 프로그램에서 올바른 위치에 결과를 출력

할 수 있게 시작 시점이 아니라 최초 요청 시점에 빈을 인스턴스화하게끔 스프링에 명령하기 위

해 lazy-init 속성을 설정했다. 예제 4-75는 ApplicationContext에서 타깃 빈을 각각 가져오는 예

제 자바 애플리케이션을 보여준다.

예제 4-75 협력 객체 자동 연결

package com.apress.prospring3.ch4.autowiring;

import org.springframework.context.support.GenericXmlApplicationContext;

public class Target {

private Foo foo;

private Foo foo2;

private Bar bar;

public Target() {

}

public Target(Foo foo) {

System.out.println("Target(Foo) called");

}

public Target(Foo foo, Bar bar) {

System.out.println("Target(Foo, Bar) called");

}

public void setFoo(Foo foo) {

this.foo = foo;

System.out.println("Property foo set");

}

public void setFoo2(Foo foo) {

this.foo2 = foo;

System.out.println("Property foo2 set");

}

public void setBar(Bar bar) {

this.bar = bar;

System.out.println("Property bar set");

}

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:autowiring.xml");

Page 164: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

136 l 프로 스프링 3

ctx.refresh();

Target t = null;

System.out.println("Using byName:\n");

t = (Target) ctx.getBean("targetByName");

System.out.println("\nUsing byType:\n");

t = (Target) ctx.getBean("targetByType");

System.out.println("\nUsing constructor:\n");

t = (Target) ctx.getBean("targetConstructor");

}

}

이 코드에서는 Target 클래스는 세 개의 생성자를 갖고 있다. 인자 없는 생성자, Foo 인스턴스

를 받는 생성자, Foo와 Bar 인스턴스를 받는 생성자다. 이들 생성자 외에 Target 빈은 세 프로퍼

티를 갖고 있다. 두 프로퍼티는 Foo 타입이고 한 프로퍼티는 Bar 타입이다. 이들 프로퍼티와 생성

자는 호출 시점에 콘솔에 메시지를 출력한다. main 메서드에서는 ApplicationContext에 선언된

Target 빈 각각을 가져와 자동 연결이 이뤄지게 한다. 다음은 이 예제를 실행한 결과다.

Using byName:

Property foo set

Using byType:

Property bar set

Property foo set

Property foo2 set

Using constructor:

Target(Foo, Bar) called

이 결과를 보면 스프링이 byName을 사용할 때는 설정 파일에서 대응되는 빈 항목이 이 프로

퍼티 뿐이므로 foo 프로퍼티만 설정되는 것을 볼 수 있다. 또 byType을 사용하면 스프링은 세 프

로퍼티를 모두 설정한다. foo와 foo2 프로퍼티는 foo으로 설정되고 bar 프로퍼티는 bar1 빈으로

설정된다. constructor를 사용하면 스프링은 두 개의 인자가 있는 생성자를 사용한다. 이렇게 되

는 이유는 스프링이 두 인자에 대한 빈을 제공할 수 있으므로 다른 생성자에 기대지 않아도 되

기 때문이다.

Page 165: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 137

자동 연결을 사용할 시점

대부분의 경우 자동 연결을 사용해야 하는가라는 물음에 대한 답은 ‘아니오!’다. 자동 연결은 작

은 애플리케이션에서는 시간을 절약해주지만 많은 경우 나쁜 개발 방식으로 이어질 수 있고 대

규모 애플리케이션에서는 유연성이 떨어진다. byName을 사용하는 게 좋은 생각처럼 보일 수 있

지만 이렇게 자동 연결을 활용하다 보면 클래스에 인위적인 프로퍼티명을 지정하는 결과로 이어

진다. 스프링의 모든 철학은 여러분이 원하는 대로 클래스를 만들고 스프링이 여러분의 작업을

도와주는 것이지, 그 반대가 아니다. 또 ApplicationContext 내에 각 타입별로 빈을 한 개만 가

질 수 있다는 사실을 깨닫기 전까지는 byType을 사용하고 싶은 유혹에 빠질 수 있다. 이런 제약

은 같은 타입의 다른 설정을 사용해 빈을 적용해야 할 때 큰 문제가 될 수 있다. 생성자 자동 연결

에 대해서도 같은 논리가 적용된다.

때로는 자동 연결이 시간을 덜어주기도 하지만 연결 방식을 명시적으로 지정하는 데 많은 노력

이 드는 게 아니고, 명시적으로 연결을 지정하면 프로퍼티 이름도 자유롭게 관리할 수 있고 관리

하는 동일 타입의 인스턴스 개수도 유연하게 관리할 수 있다. 따라서 규모가 큰 애플리케이션이

라면 어떤 비용을 치르더라도 자동 연결 방식을 사용하지 말아야 한다.

빈 상속

때로는 같은 타입의 빈 정의가 여러 개 필요하거나 공유 인터페이스를 구현하는 경우가 있다. 이

때 이들 빈이 일부 설정은 공유하고 다른 설정은 공유하지 않게 하는 게 어려울 수 있다. 공유 설

정을 동기화하는 일은 오류가 생기기 쉽고, 대규모 프로젝트에서는 많은 시간이 소요된다. 이런

문제를 피하기 위해 스프링은 같은 ApplicationContext 내의 다른 빈의 프로퍼티 설정을 상속

하는 <bean> 정의 기능을 제공한다. 자식 빈의 프로퍼티 값은 필요에 따라 바꿀 수 있으며 이로

인해 완전히 제어할 수 있고, 부모 빈은 각 빈에게 기본 설정을 제공해줄 수 있다. 예제 4-76은 두

빈의 정의를 보여주는데, 이 중 하나는 다른 빈의 자식 빈이다(app-context-xml.xml).

예제 4-76 빈 상속 설정

<bean id="inheritParent" class="com.apress.prospring3.ch4.inheritance.SimpleBean">

<property name="name">

<value>Clarence Ho</value>

</property>

<property name="age">

<value>22</value>

</property>

</bean>

Page 166: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

138 l 프로 스프링 3

<bean id="inheritChild" class="com.apress.prospring3.ch4.inheritance.SimpleBean"

parent="inheritParent">

<property name="age">

<value>35</value>

</property>

</bean>

이 코드에서는 inheritChild의 <bean> 태그에 parent라는 추가 속성이 있는 것을 볼 수 있

다. 이 속성은 스프링에게 inheritParent 빈을 부모 빈으로 간주해야 한다고 알려준다. 이 경우

부모 빈은 ApplicationContext에서 룩업하게 할 필요가 없으므로 부모 빈을 선언할 때 <bean>

태그에 abstract="true"를 추가해도 된다. inheritChild 빈은 age 프로퍼티에 고유 값을 갖고 있

으므로 스프링은 이 값을 빈으로 넘겨준다. 하지만 inheritChild는 name 프로퍼티에 값이 없

으므로 스프링은 inheritParent 빈에 주어진 값을 사용한다 예제 4-77은 앞의 설정에서 사용한

SimpleBean 클래스에 대한 코드다.

예제 4-77 SimpleBean 클래스

package com.apress.prospring3.ch4.inheritance;

import org.springframework.context.support.GenericXmlApplicationContext;

public class SimpleBean {

public String name;

public int age;

public static void main(String[] args) {

GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

ctx.load("classpath:app-context-xml.xml");

SimpleBean parent = (SimpleBean) ctx.getBean("inheritParent");

SimpleBean child = (SimpleBean) ctx.getBean("inheritChild");

System.out.println("Parent:\n" + parent);

System.out.println("Child:\n" + child);

}

public void setName(String name) {

this.name = name;

}

public void setAge(int age) {

this.age = age;

}

Page 167: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

4장 스프링의 IoC 및 DI 소개 l 139

public String toString() {

return "Name: " + name + "\n"

+ "Age: " + age;

}

}

코드에서 보듯이 SimpleBean 클래스의 main() 메서드는 ApplicationContext에서

inheritChild와 inheritParent를 가져와 프로퍼티의 내용을 콘솔에 출력한다. 다음은 이 예제의

출력 결과다.

Parent:

Name: Clarence Ho

Age: 22

Child:

Name: Clarence Ho

Age: 35

예상한 대로 inheritChild 빈은 inheritParent 빈의 name 프로퍼티 값을 상속했지만 age 프로

퍼티 값으로는 자신의 값을 제공했다.

자식 빈은 부모 빈의 생성자 인자와 프로퍼티 값을 모두 상속하므로 빈 상속에도 두 주입 방식

을 모두 사용할 수 있다. 이러한 빈 상속은 여러 빈 정의를 사용해 애플리케이션을 개발할 때 매

우 유용하다. 같은 프로퍼티 값을 갖고 있는 빈을 여러 개 선언한다면 값을 공유하기 위해 값을

복사해서 붙여넣는 방식을 피해야 한다. 대신 설정에서 상속 계층구조를 설정해야 한다.

상속을 사용할 때는 빈 상속이 자바 상속 구조와 일치하지 않아도 된다는 점을 기억하자. 동일

타입에 대해 다섯 개의 빈 상속을 지정하더라도 전혀 상관없다. 빈 상속은 상속 기능보다는 템플

릿 기능이라고 생각하면 된다. 하지만 자식 빈의 타입을 바꿀 경우 타입이 부모 빈의 타입과 호환

돼야 한다는 점은 주의해야 한다.

정리

이 장에서는 스프링 코어와 IoC 전반에 대해 많은 내용을 다뤘다. 각기 다른 타입의 IoC 예제

를 소개하고 애플리케이션에서 각 매커니즘을 사용할 때의 장단점을 살펴봤다. 또 스프링이 제

공하는 IoC 메커니즘의 종류와 애플리케이션 내에서 언제 어떤 메커니즘을 사용해야 할지 알

아봤다. IoC를 살펴보면서 우리는 스프링의 IoC 기능의 핵심 컴포넌트인 스프링 BeanFactory

를 소개하고, 추가 기능을 제공하기 위해 BeanFactory를 상속한 ApplicationContext도 소개

Page 168: 프로 스프링 3 : 스프링 프레임워크의 원리와 활용

140 l 프로 스프링 3

했다. ApplicationContext와 관련해서는 스프링에서 XML을 사용해 외부 설정을 지원하는

GenericXmlApplicationContext를 중점적으로 살펴봤다. 더불어 ApplicationContext에서 DI

요건을 선언하는 또 다른 방법인 자바 애노테이션에 대해서도 자세히 알아봤다.

이 장에서는 세터 주입, 생성자 주입, 메서드 주입, 자동 연결, 빈 상속 같은 스프링의 기본 IoC

기능을 소개했다. 설정을 설명할 때는 다른 빈을 비롯해 다양한 값을 사용해 XML, 애노테이션,

GenericXmlApplicationContext를 통해 빈 프로퍼티를 설정하는 법을 보여줬다.

이 장에서 다룬 내용은 스프링 및 스프링 IoC 컨테이너의 빙산의 일각이다. 다음 장에서는 스

프링과 관련한 IoC 관련 기능을 살펴보고 스프링 코어에서 활용할 수 있는 다른 기능을 좀 더 자

세히 살펴본다.