8.1 스프링의 정의
자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크
애플리케이션 프레임워크
- 특정 계층이나 기술, 업무 분야에 국한되지 않고 애플리케이션의 전 영역을 포괄하는 범용적인 프레임워크
- 애플리케이션 개발의 전 과정을 빠르고 편리하며 효율적으로 진행하는 데 일차적인 목표를 두는 프레임워크
스프링의 존재 목적은 핵심 기술에 담긴 프로그래밍 모델을 일관되게 적용해서 엔터프라이즈 애플리케이션 전 계층과 전 영역에 전략과 기능을 제공해줌으로써 애플리케이션을 편리하게 개발하게 해주는 애플리케이션 프레임워크로 사용된다.
경량급
스프링이 가볍다 = 불필요하게 무겁지 않다
단순한 개발툴과 기본적인 개발 환경으로도 엔터프라이즈 개발에서 필요로하는 주요한 기능을 갖춘 애플리케이션을 개발하기에 충분하다.
자바 엔터프라이즈 개발을 편하게
편리한 애플리케이션 개발이란?
- 개발자가 복잡하고 실수하기 쉬운 로우레벨 기술에 많은 신경을 쓰지 않음
- 애플리케이션의 핵심인 사용자의 요구사항을 빠르고 효과적으로 구현하는 것
스프링 애플리케이션은 개발자들이 스프링이라는 프레임워크가 제공하는 기술이 아니라 자신이 작성하는 애플리케이션 로직에 더 많은 관심과 시간을 쏟게 해준다.
엔터프라이즈 개발에서 필연적으로 요구되는 기술적인 요구를 충족하면서도 개발을 복잡하게 만들지 않는다.
오픈소스
개발 과정에 많은 사람이 자유롭게 참여한다는 점이 오픈소스 프로젝트로서 스프링이 가진 장점이다. 공개된 커뮤니티 공간 안에서 투명한 방식으로 다양한 참여를 통해 개발되기 때문에 매우 빠르고 유연한 개발이 가능하다.
8.2 스프링의 목적
자바의 근본적인 목적 : 객체지향 프로그래밍을 통해 유연하고 확장성 좋은 애플리케이션을 빠르게 만드는 것
8.2.1 엔터프라이즈 개발의 복잡함
- 기술적인 제약조건과 요구사항이 늘어가기 때문이다.
엔터프라이즈 시스템 : 서버에서 동작하며 기업과 조직의 업무를 처리해주는 시스템, 서버의 자원을 효율적으로 공유하고 분배해서 사용할 수 있어야 하며 보안과 안정성, 확장성 면에서도 뛰어나야 한다.
엔터프라이즈 시스템을 개발한다 = 순수 비즈니스 로직 + 기술적으로 고려할 사항이 많다.
- 엔터프라이즈 애플리케이션이 구현해야 할 핵심기능인 비즈니스 로직의 복잡함이 증가하기 때문이다.
다양하고 복잡한 업무 처리 기능을 엔터프라이즈 시스템이 구현해야 한다.
복잡함을 가중시키는 원인 = 근본적인 비즈니스 로직과 엔터프라이즈 기술의 복잡함이 얽혀있음
8.2.2 복잡함을 해결하려는 도전
엔터프라이즈 개발에 나타나는 복잡함의 원인은 제거대상이 아니라 그 복잡함을 효과적으로 상대할 수 있는 전략과 기법이 필요하다.
= 두 가지 복잡함을 분리해내는 것
실패한 해결책 : EJB
애플리케이션 로직을 담은 핵심 코드에서 일부 기술적인 코드가 제거되었지만, EJB라는 환경과 스펙에 종속되는 코드로 만들어져야하는 부담을 안게 됐다.
비침투적인 방식을 통한 효과적인 해결책 : 스프링
침투적인 기술 : 어떤 기술을 적용했을 때 그 기술과 관련된 코드나 규약 등이 코드에 등장하는 경우 비침투적인 기술 : 기술의 적용 사실이 코드에 직접 반영되지 않는다는 특징
스프링이 성공할 수 있었던 이유 = 비침투적인 기술 전략 선택
기술적인 복잡함과 비즈니스 로직을 다루는 코드를 깔끔하게 분리할 수 있고 그 과정에서 스프링 스스로가 애플리케이션 코드에 불필요하게 나타나지 않도록 했다.
스프링을 통해 성격이 다른 복잡함들을 깔끔하게 분리해줬기 때문에 각각을 효과적으로 상대할 수 있는 기반이 마련됐다.
8.2.3 복잡함을 상대하는 스프링의 전략
기술적 복잡함을 상대하는 전략
- 기술에 대한 접근 방식이 일관성이 없고, 특정 환경에 종속적이다.
일관성없는 기술과 서버환경의 변화에 대한 방법 = 서비스 추상화
기술의 복잡함은 일단 추상화를 통해 로우레벨의 기술 구현 부분과 기술을 사용하는 인터페이스를 분리하고, 환경과 세부 기술에 독립적인 접근 인터페이스를 제공하는것이 가장 좋은 해결책이다.
스프링이 제공하는 템플릿/콜백 패턴은 판에 박힌 반복적인 작업 흐름과 API 사용 코드를 제거해 기술을 사용하는 코드도 최적화된 핵심 로직에만 집중하도록 도와준다.
- 기술적인 처리를 담당하는 코드가 성격이 다른 코드에 섞여서 등장한다.
기술과 비즈니스 로직의 혼재로 발생하는 복잡함을 해결하기 위한 방법 = AOP
AOP는 기술을 다루는 코드로 인한 복잡함이 기술 그 자체 이상으로 불필요하게 증대되지 않도록 도와주는 수단
비즈니스 애플리케이션 로직의 복잡함을 상대하는 전략
엔터프라이즈 시스템 개발의 흐름 = 비즈니스 로직은 애플리케이션 안에서 처리
DB는 영구적인 저장과 복잡한 조건을 가진 검색과 같은 특화된 기능에만 활용하고, 데이터를 분석하고 가공하고 그에 따라 로직을 처리하는 부분은 확장하기 쉽고, 비용이 싼 애플리케이션 서버 쪽으로 이동
객체지향 프로그래밍 기법과 언어가 주는 장점인 유연한 설계가 가능하고 재사용성이 높다는 점을 활용하면 비즈니스 로직을 효과적으로 구현해 낼 수 있다.
객체지향 분석과 설계를 통해 작성된 모델을 코드로 구현하고 지속적으로 발전시킬수도 있다.
핵심도구 : 객체지향과 DI
기술적인 복잡함을 효과적으로 다루게 해주는 기법은 모두 DI를 바탕으로 하고있다.
순수한 비즈니스 로직만을 담고 있는 코드에는 객체지향 분석과 설계에서 나온 도메인 모델을 쉽게 적용할 수 있기 때문이다.
스프링의 기술과 전략은 객체지향이라는 자바 언어가 가진 강력한 도구를 극대화해서 사용할 수 있도록 돕는 것이라고 볼 수 있다.
8.3 POJO 프로그래밍
스프링의 정수는 엔터프라이즈 기능을 POJO에 제공하는 것 = 엔터프라이즈 서비스 기술과 POJO라는 애플리케이션 로직을 담은 코드를 분리했다
8.3.1 스프링의 핵심 : POJO
스프링 애플리케이션은 POJO를 이용해서 만든 애플리케이션 코드와 POJO가 어떻게 관계를 맺고 동작하는지 정의해놓은 설계 정보로 구분된다.
8.3.2 POJO란 무엇인가?
POJO : Plain Old Java Object
8.3.3 POJO의 조건
- 특정 규약에 종속되지 않는다.
POJO는 자바 언어와 꼭 필요한 API 외에는 종속되지 않아야 한다.
특정 규약을 따라 만들게 하는 경우는 대부분 규약에서 제시하는 특정 클래스를 상속하도록 요구한다. 그럴 경우 자바의 단일 상속 제한 때문에 더 이상 해당 클래스에 객체지향적인 설계 기법을 적용하기가 어려워지고, 규약이 적용된 환경에 종속적이 되기 때문에 다른 환경으로 이전이 힘들다는 문제점이 있다.
- 특정 환경에 종속되지 않는다.
특정 환경이 의존 대상 검색방식에 종속적이라면 POJO라고 할 수 없다.
순수한 애플리케이션 로직을 담고있는 오브젝트 코드가 특정 환경에 종속되게 만드는 경우라면 POJO라고 할 수 없다.
POJO는 환경에 독립적이어야 한다.
웹이라는 환경 정보나 웹 기술을 담고 있는 클래스나 인터페이스를 사용해서는 안된다. 웹이라는 환경으로 제한해버리는 오브젝트나 API에 의존해서는 안된다.
비즈니스 로직을 담은 코드에 HttpServletRequest나 HttpSesison, 캐시와 관련된 API가 등장하거나 웹 프레임워크의 클래스를 직접 이용하는 부분이 있다면 POJO라고 볼 수 없다.
애노테이션이 단지 코드로 표현하기 적절치 않는 부가정보를 담고 있고, 그 때문에 환경에 종속되지만 않는다면 POJO라고 할 수 있다.
진정한 POJO란 객체지향 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다. 그런 POJO에 애플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라 할 수 있다.
8.3.4 POJO의 장점
특정한 기술과 환경에 종속되지 않는 오브젝트는 그만큼 깔끔한 코드가 될 수 있다.
POJO로 개발된 코드는 자동화된 테스트에 매우 유리하다.
객체지향적인 설계를 자유롭게 적용할 수 있다.
8.3.5 POJO 프레임워크
스프링을 이용하면 POJO 프로그래밍의 장점을 그대로 살려 엔터프라이즈 애플리케이션의 핵심 로직을 객체지향적인 POJO를 기반으로 깔끔하게 구현하고, 엔터프라이즈 환경의 각종 서비스와 기술적인 필요를 POJO 방식으로 만들어진 코드에 적용할 수 있다.
POJO 프레임워크로서 스프링은 자신을 직접 노출하지 않으면서 애플리케이션을 POJO로 쉽게 개발할 수 있게 지원해준다.
8.4 스프링의 기술
POJO 개발이 가능하려면 IoC/DI, AOP, PSA 기술이 뒷받침돼야한다.
스프링의 기술들은 스프링 프레임워크가 만들어진 진정한 목표인 POJO 기반의 엔터프라이즈 개발을 편리하게 해주는 도구일 뿐이다.
8.4.1 제어의 역전(IoC) / 의존관계 주입(DI)
DI 방식으로 하는 이유 = 유연한 확장이 가능하게 하기 위해서
유연한 확장이라는 장점은 OCP의 확장에는 열려있다에 해당한다. 폐쇄 관점에서 볼 때 장점은 재사용이 가능하다 라고 볼 수 있다.
DI의 활용 방법
- 핵심 기능의 변경
DI의 가장 대표적인 적용 방법은 의존 대상의 구현을 바꾸는 것이다. 실제 의존하는 대상이 가진 핵심기능을 DI 설정을 통해 변경하는 것이 대표적인 DI 활용 방법이다.
- 핵심 기능의 동적인 변경
일반적인 DI를 이용한 변경 방법과는 달리 동적으로 매번 다르게 변경할 수 있다. 애플리케이션이 동작하는 중간에 그 의존 대상을 변경할 수 있다.
기술적으로 다이나믹 라우팅 프록시나 프록시 오브젝트 기법을 활용한 것이다.
- 부가기능의 추가
핵심기능은 그대로 둔 채로 부가 기능을 추가하는 것이다.
인터페이스를 두고 사용하게 하고, 실제 사용할 오브젝트는 외부에서 주입하는 DI를 적용해두면 데코레이터 패턴을 쉽게 적용할 수 있다. 그래서 핵심기능과 클라이언트 코드에는 전혀 영향을 주지 않으면서 부가적인 기능을 얼마든지 추가할 수 있다.
- 인터페이스의 변경
클라이언트가 사용하는 인터페이스와 실제 오브젝트 사이에 인터페이스가 일치하지 않는 경우에도 유용하다.
인터페이스가 다른 오브젝트를 클라이언트가 사용하는 인터페이스로 바꿔주는 기능을 이용한다.
중간에 인터페이스 어댑터 역할을 해주는 레이어를 하나 추가하는 방법도 있다.
PSA는 클라이언트가 일관성 잇게 사용할 수 있는 인터페이스를 정의해주고 DI를 통해 어댑터 역할을 하는 오브젝트를 이용하게 해준다. 이를 통해 다른 인터페이스를 가진 로우레벨의 기술을 변경하거나 확장해가면서 사용할 수 있다.
- 프록시
필요한 시점에서 실제 사용할 오브젝트를 초기화하고 리소스를 준비하게 해주는 지연된 로딩을 적용하려 할 때 원격 오브젝트를 호출할 때 로컬에 존재하는 오브젝트처럼 사용할 수 있게 해주는 원격 프록시를 적용할 때
모두 DI를 필요로 한다.
스프링은 EJB 원격 호출을 포함해서 웹 서비스, REST 호출, HTTP 방식의 호출 등 다양한 리모팅 기술을 지원한다.
- 템플릿과 콜백
반복적으로 등장하지만 항상 고정적인 작업 흐름과 그 사이에 자주 바뀌는 부분을 분리해 템플릿과 콜백으로 만들고, 이를 DI원리를 응용해 적용하면 코드를 간결하게 만들 수 있다.
콜백을 템플릿에 주입하는 방식으로 동작하게 하는 것 = DI원리에 가장 충실한 응용
콜백을 만들어서 사용할 수 있다는 건 개방을 통한 유연한 확장성을 보여주는 것이며, 템플릿은 한번 만들어두면 계속 재사용할 수 있다는 건 기능의 확장에도 변하지 않는다는 OCP의 폐쇄 원칙에 가장 잘 들어맞는다.
- 싱글톤과 오브젝트 스코프
DI 할 오브젝트의 생명주기를 제어할 수 있다.
DI를 프레임워크로 이용한다 = DI 대상 오브젝트를 컨테이너가 관리한다.
오브젝트의 생성부터 관계 설정, 이용, 소멸까지 모든 과정을 DI 컨테이너가 주관해 그 오브젝트의 스코프를 자유롭게 제어할 수 있다.
가장 기본이 되는 스코프 = 싱글톤
스프링의 DI는 기본적으로 싱글톤으로 오브젝트를 만들어 사용하게 한다. 컨테이너가 알아서 싱글톤을 만들고 관리하기 때문에 클래스 자체는 싱글톤을 고려하지 않고 자유롭게 설계해도 된다는 장점이 있다.
싱글톤 외에도 다양한 스코프를 갖는 오브젝트를 만들어 DI에 사용할 수 있다.
- 테스트
테스트 할 대상이 의존하는 오브젝트를 테스트를 목적으로 만들어진 목 오브젝트로 대체하면 유용하다.
DI를 위해 만든 수정자 메소드를 사용하면 테스트 코드 안에서 수동으로 목 오브젝트를 주입할 수 있고, 테스트 용으로 별도로 만든느 방법도 있다.
8.4.2 애스펙트 지향 프로그래밍(AOP)
IoC/DI를 이용해서 POJO에 선언적인 엔터프라이즈 서비스를 제공할 수 있지만 일부 서비스는 순수 객체지향 기법만으로는 POJO의 조건을 유지한 채로 적용하기 힘들다
이런 문제를 해결하기 위해 AOP가 필요하다.
AOP의 적용 기법
- 스프링과 같이 다이내믹 프록시를 사용하는 방법이다.
기존 코드에 영향을 주지 않고 부가기능을 적용하게 해주는 데코레이터 패턴을 응용한 것이다. 스프링의 기본적인 AOP 구현 방법은 다이내믹 프록시를 이용하는 프록시 AOP 방식이다.
- 자바 언어의 한계를 넘어서는 언어의 확장을 이용하는 방법이다.
프록시 방식의 AOP로는 할 수 없는 작업을 위해 AspectJ를 사용해야 한다.
AOP의 적용 단계
- AOP 적용 1단계 : 미리 준비된 AOP 이용
스프링이 미리 만들어서 제공하는 AOP 기능을 그대로 가져다 적용하는 것으로 시작한다.
스프링이 직접 제공하는 대표적인 AOP = 트랜잭션 트랜잭션 적용을 스프링 AOP도입의 첫번째 단계로 이용한다.
@Configurable
애노테이션을 이용해서 도메인 오브젝트에 DI를 자동 적용해주는 AOP 기능
도메인 오브젝트를 적용 계층에 두고 접근하는 아키텍쳐 방식을 따를 때 반드시 필요하다.
- AOP 적용 2단계 : 전담팀을 통한 정책 AOP 적용
애플리케이션 전체적으로 이용 가능한 것을 소수의 AOP 담당자 관리하에 적용해 볼 수 있다. 비즈니스 로직을 가진 오브젝트에 대한 보안, 특정 계층의 오브젝트 이용 전후의 작업 기록을 남기는 로깅, 데이터 추적을 위한 트레이싱, 특정 구간의 실시간 성능 모니터링과 같은 정책적으로 적용할 만한 기능에 AOP를 이용
AOP를 이용해 한 번에 적용한다면 일반 개발자의 작업에는 전혀 영향을 주지 않을 수 있다.
또한 AOP는 기존 코드에 영향을 주지 않고 언제든지 기능을 추가하거나 제거할 수 있다.
- AOP 적용 3단계 : AOP의 자유로운 이용
개발자 스스로가 AOP를 활용할 수 있는 단계로 넘어갈 수 있다. 개발자가 구현하는 기능에 적용하면 유용한 세부적인 AOP를 이용할 수 있다.
8.4.3 포터블 서비스 추상화(PSA)
PSA : 환경과 세부 기술의 변화에 관계 없이 일관된 방식으로 기술에 접근할 수 있게 해주는 기술
특정 환경과 기술에 종속적이지 않다 =/= 그런 기술을 사용하지 않는다
이를 위해 스프링이 제공하는 대표적인 기술이 일관성 있는 서비스 추상화 기술이다.
스프링은 엔터프라이즈 개발에 사용되는 기술에 대한 서비스 추상화 기능을 제공한다.
서비스 추상화를 위해 필요한 기술 = DI
DI를 활용해 개발한다면 서비스 추상화는 자연스럽게 만들어 쓸 수 있다. 테스트가 어렵게 만들어진 API나 설정을 통해 주요 기능을 외부에서 제어하게 만들고 싶을 때도 이용할 수 있다.
이일민, [토비의 스프링 3.1], 에이콘 출판사(2012)