스프링이 어떻게 해서 엔터프라이즈 개발이 주는 복잡함을 제거하고,
POJO 프로그래밍이라는 효과적인 방법을 사용할 수 있게 하는지에 관심을 갖는 것이 스프링을 가장 빠르게 이해하고 적용할 수 있는 지름길이다.
"스프링은 단지 IoC/DI 프레임워크다."라고는 말할 수 없다.
엔터프라이즈 어플리케이션 개발의 전 영역에 걸쳐 다양한 종류의 기술에 관여한다.
스프링 프레임워크가 만들어진 이유와 존재 목적, 추구하는 가치는 무엇일까?
스프링은 더더욱 그 목표를 분명히 알고 사용하지 않으면 그 가치를 제대로 얻기 힘들다.
그저 스프링을 가져다가 어떻게든 사용해서 개발만 하면 스프링을 적용한 것이고, 스프링의 장점이 개발에 반영됐다고 할 수 있을까?
스프링은 그 기능과 API 사용 방법을 잘 안다고 해서 잘 쓸 수 있는게 아니다.
어떤 기술이든 그 자체로는 "도구"에 불과하다.
그것을 용도에 맞게 잘 활용해서 궁극적으로 이루고자 하는 목표를 이루는 것이 중요하지, 도구의 사용법만 열심히 익힌다고 결과를 저절로 얻을 수 있는 건 아니다.
DI를 의식하다 보면 오브젝트를 설계할 때 DI를 적용할 후보가 더 이상 없을까를 생각해보게 된다.
1. 여기서 바뀔 수 있는 것은 무엇일까?
2. 여기서 성격이 다르고, 변경의 이유가 다른 기능은 무엇일까?
그런 후보를 찾을 수 있다면 DI를 적용해서 오브젝트룰 분리하고, 인터페이스를 도입하고, DI로 관계를 연결해줄것이다.
스프링만 잘 공부하면 자바 언어 자체나 객체지향 설계와 개발 실력따윈 별로 신경쓰지 않아도 복잡한 엔터프라이즈 시스템 개발을 잘할 수 있을 거라고 생각하면 오산이다.
스프링은 간단한 몇 단어로 규정하기에는 쉽지 않은 독특한 특징이 있다.
스프링을 그떄그때 필요한 API 사용 방법 위주로만 공부하면 스프링을 오해하거나 그 가치를 충분히 누리지 못할 수 있다.
스프링에 대해 가장 잘 알려진 정의는 자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크이다.
라이브러리나 프레임워크는 특정 업무 분야나 한 가지 기술에 특화된 목표를 가지고 만들어진다.
- 애플리케이션의 특정 계층에서 주로 동작하는 한 가지 기술 분야에 집중된다.
하지만 애플리케이션 프레임워크는 특정 계층이나, 기술, 업무 분야에 국한되지 않고 애플리케이션의 전 영역을 포괄하는 범용적인 프레임워크
를 말한다.
그리고 애플리케이션 개발의 전 과정을 빠르고 편리하며 효율적으로 진행하는데 일차적인 목표를 두는 프레임워크
다.
스프링의 기원은 J2EE 기술서적에 딸린 예제코드다.
단지 여러 계층의 다양한 기술을 그저 한데 모아뒀기 때문에 애플리케이션 프레임워크라고 불리는 건 아니다.
애플리케이션의 전 영역을 관통하는 일관된 프로그래밍 모델
과 핵심 기술
을 바탕으로 해서 각 분야의 특성에 맞는 필요을 채워주고 있기 때문에, 애플리케이션을 빠르고 효과적으로 개발할 수가 있다.
바로 이것이 스프링이 애플리케이션 프레임워크라고 불리는 이유다.
스프링이 경량급이라는 건 스프링 자체가 아주 가볍다거나 작은 규모의 코드로 이뤄졌다는 뜻은 아니다.
오히여 스프링은 방대한 프레임워크다.
그럼에도 스프링이 불필요하게 무겁지 않기 때문에 가볍다고 한다.
- 스프링의 기원이 된 책에서 비판하는 자바 엔터프라이즈 기술의 불필요한 복잡함에 반대되는 개념이다.
스프링은 가장 단순한 서버환경인 톰캣이나 제티에서 완벽하게 동작한다.
서블릿 컨테이너만으로 충분하니 EJB 컨테이너를 비롯해 복잡한 기능이 잔뜩 포함된 고급 WAS를 굳이 사용하지 않아도 된다.
그런 면에서 스프링은 군더더기 없이 깔끔한 기술을 가진 "경량급" 프레임워크
라고 불린 것이다.
"편리한 애플리케이션 개발"이란?
개발자가 복잡하고 실수하기 쉬운
로우레벨 기술에 많은 신경을 쓰지 않으면서
도 애플리케이션의 핵심인 사용자의 요구사항,즉
비즈니스 로직을 빠르고 효과적으로 구현하는 것
을 말한다.
엔터프라이즈 개발의 근본적인 문제점에 도전
해서 해결책을 제시한다는 것이 기존 기술의 접근 방법과 스프링의 접근 방법의 차이점이다.
엔터프라이즈 개발에서 필연적으로 요구되는 기술적인 요구를 충족하면서도 개발을 복잡하게 만들지 않는다는 점이 스프링의 뛰어난 면이다.
스프링에 적용된 오픈소스 라이선스는 아파치 라이선스 버전 2.0
스프링 개발업체인 스프링소스는 2009년에 세계적인 IT기업인 VMWare
에 전략적으로 합병됐다.
이번에는 좀 더 구체적으로 스프링의 개발 철학과 궁극적인 목표가 무엇인지 알아보자
스프링은 더더욱 그 목표를 분명히 알고 사용하지 않으면 그 가치를 제대로 얻기 힘들다.
"엔터프라이즈 시스템"이란?
서버에서 동작하며 기업과 조직의 업무를 처리해주는 시스템
많은 사용자의 요청을 동시에 처리해야 하기 때문에 서버의 자원을 효율적으로 공유하고 분배해서 사용할 수 있어야 한다.
엔터프라이즈 시스템 개발이 복잡한 근본적인 이유는 비즈니스 로직과 엔터프라이즈 기술이라는 두 가지 복잡함이 한데 얽혀 있기 때문
이다.
근본적으로 엔터프라이즈 개발에 나타나는 복잡함의 원인은 제거 대상이 아니다.
대신 그 복잡함을 효과적으로 상대할 수 있는 전략과 기법이 필요하다.
가장 먼저 할 일은 성격이 다른 이 두 가지의 복잡함을 분리 해내는 것이다.
스프링이 성공할 수 있었던 비결은 바로 비침투적인 기술이라는 전략을 택했기 때문이다.
스프링을 이용하면 기술적인 복잡함
과 비즈니스 로직
을 다루는 코드를 깔끔하게 분리할 수 있다.
- 중요한 점은 그 과정에서 스프링 스스로가 애플리케이션 코드에 불필요하게 나타나지 않도록 하는 것
스프링의 기본적인 전략은 비즈니스 로직을 담은 애플리케이션 코드와 엔터프라이즈 기술을 처리하는 코드를 분리시키는 것
- 스프링은 서비스 추상화를 통해 일관성 없는 기술과 서버환경의 변화에 대한 문제를 해결했다
- 스프링은 AOP를 통해 애플리케이션 로직을 담당하는 부분과 기술 관련 로직을 분리했다
비즈니스 로직의 복잡함을 상대하는 전략은 자바라는 객체지향 기술 그 자체이다.
스프링은 단지 객체지향 언어의 장점을 제대로 살리지 못하게 방해했던 요소를 제거하도록 도와줄 뿐이다.
스프링의 모토는 결국 "기본으로 돌아가자"이다.
자바의 기본인 객체지향에 충실한 설계가 가능하도록 단순한 오브젝트로 개발할 수 있고,
객체지향의 설계 기법을 잘 적용할 수 있는 구조를 만들기 위해 DI 같은 유용한 기술을 편하게 적용하도록 도와주는 것이 스프링의 기본 전략
이다.
- 객체지향적인 특성을 잘 살린 설계는 상속과 다형성, 위임을 포함해서 많은 객체지향 디자인 패턴과 설계 기법이 잘 녹아들어갈 수 있다.
서비스 추상화, 템플릿/콜백, AOP와 같은 스프링의 기술은 DI 없이는 존재할 수 없는 것들이다!!
DI란 특별한 기술이라기보다는 객체지향 프로그래밍 기법일 뿐이다.
기술적으로 스프링이 지향하는 목적이 무엇인지 정의해보자.
스프링의 핵심 개발자들은 "스프링의 정수
는 엔터프라이즈 서비스 기능을 POJO에 제공하는 것"이라고 했다.
- 엔터프라이즈 서비스라고 하는 것은 보안, 트랜잭션과 같은 엔터프라이즈 시스템에서 요구되는 기술을 말한다.
엔터프라이즈 서비스 기술과 POJO라는 애플리케이션 로직을 담은 코드를 분리했다는 뜻이기도 하다.
"분리됐지만 반드시 필요한 엔터프라이즈 서비스 기술을 POJO 방식으로 개발된 애플리케이션 핵심 로직을 담은 코드에 제공한다"
는 것이 스프링의 가장 강력한 특징과 목표다.
스프링의 주요 기술인 IoC/DI, AOP와 PSA는 애플리케이션을 POJO로 개발할 수 있게 해주는 가능기술이라고 불린다.
마틴 파울러가 평범한 자바 오브젝트
에는 그럴싸한 이름이 없어서 POJO라는 단어를 만들어냈다고 한다.
그렇다면 이 POJO 프로그래밍이란 무엇인가?
스프링 사용자라면 스프링이 직접 제공하지 않는 기술에 대해서도 PSA를 적용할 줄 알아야 한다.
그것이 스프링의 목적과 개발 철학에 부합하는 스프링의 사용법이다.
그래서 스프링의 기술들은 스프링의 목적과 핵심 가치를 기준으로 살펴보고 이해하는 것이 중요하다.
AOP와 PSA도 IoC/DI에 바탕을 두고 있다.
"왜 두 개의 오브젝트를 분리해서 만들고, 인터페이스를 두고 느슨하게 연결한 뒤, 실제 사용할 대상은 DI를 통해 외부에서 지정하는 것일까?"
즉 직접 자신이 사용할 오브젝트를 new
키워드로 생성해서 사용하는 강한 결합을 쓰는 방법보다 나은 점은 무엇일까?
간단한 답변은 유연한 확장이 가능하게 하기 위해서
라고 할 수 있다.
DI의 활용 방법
- 핵심기능의 변경
- A → B A의 기능 일부를 B에게 위임한다고 했을 때 B의 핵심기능이 변경 되어도 A는 신경안써도 된다.
- B와 같은 타입이기만 하면 새로운 대상도 의존관계에 참여할 수 있다.
- 핵심기능의 동적인 변경
- 동적인 방식으로 핵심기능을 변경하는 건, 기술적으로 보자면
다이내믹 라우팅 프록시
나프록시 오브젝트
기법을 활용한 것 - 이런 기법은 DI 없이는 불가능하다
- 동적인 방식으로 핵심기능을 변경하는 건, 기술적으로 보자면
- 부가기능의 추가
- 핵심기능은 그대로 둔 채로 부가기능을 추가하는 것
데코레이터 패턴
- 부가기능의 추가 방식을 특정 오브젝트가 아니라 좀 더 많은 대상으로 일반화 해서 적용하면
AOP
가 된다 OCP
가 말하는 확장에 열려있고 변경에는 닫혀있다는 것은 전략 패턴에서처럼 핵심기능을 변경해서 쓰는 수준만을 말하는게 아님을 기억해야 한다.
- 인터페이스의 변경
- 클라이언트가 사용하는 인터페이스와 실제 오브젝트 사이에 인터페이스가 일치하지 않는 경우에도 DI가 유용하다.
- 어댑터 오브젝트를 만들면 된다
- DI의 응용 방법 중 하나이자 스프링의 대표적인 기술로도 분류되는 일관성 있는
서비스 추상화 PSA
가 그런 방법이다. - PSA는 클라이언트가 일관성 있게 사용할 수 있는 인터페이스를 정의해주고 DI를 통해 어댑터 역할을 하는 오브젝트를 이용하게 해준다.
- 이를 통해서 다른 인터페이스를 가진 로우레벨의 기술을 변경하거나 확장해가면서 사용할 수 있는 것이다.
- 프록시
- 지연로딩
lazy loading
을 적용하려면 프록시가 필요하다. - 원격 오브젝트를 호출할 때 마치 로컬에 존재하는 오브젝트처럼 사용할 수 있게 해주는 원격 프록시를 적용할 때도 프록시가 필요하다.
- 두 가지 방법 모두 DI를 필요로 한다.
- 지연로딩
- 템플릿과 콜백
- 반복적으로 등장하지만 항상 고정적인 작업 흐름과 그 사이에서 자주 바뀌는 부분을 분리해서 템플릿과 콜백으로 만든다.
스프링이 제공하는 20여 가지의 템플릿/콜백
이 적용된 기능을 가져다 활용하는 것뿐 아니라 필요에 따라서는 DI 원리를 따라 직접 응용할 수 있어야 한다.
- 싱글톤과 오브젝트 스코프
- DI할 오브젝트의 생명주기를 제어할 수 있다는 것, DI 대상 오브젝트를 컨테이너가 관리한다는 것
- 컨테이너가 알아서 싱글톤을 만들고 관리하기 때문에 클래스 자체는 싱글톤을 고려하지 않고 자유롭게 설계해도 된다는 장점이 있다.
- HTTP 요청당 하나의 오브젝트가 만들어지거나, HTTP 세션당 하나씩 오브젝트가 만들어지게 할 수 있다.
- 개발자 스스로 일정한 스코프를 갖는 오브젝트를 만들고 이를 DI에 적용하는 것도 가능하다.
- 테스트
- 테스트 대상 오브젝트의 기능을 충실하게 테스트 하기 위해 대상을 고립시킬 수 있다.
- 의존 오브젝트를 대신해서 DI를 사용하여 스텁 또는 목 오브젝트 같은 테스트 대역을 활용해야 한다.
잘 살펴보면 DI의 용도는 디자인 패턴 중에서 오브젝트 합성 방식
을 따르는 패턴과 관련이 있음을 알 수 있다.
인터페이스를 두고 오브젝트를 분리하는 구조를 가진 오브젝트 스코프의 패턴은 DI
의 구조에 대부분 잘 들어맞는다.
AOP는 객체지향 기술의 한계와 단점을 극복하도록 도와주는 보조적인 프로그래밍 기술이다.
스프링의 AOP는 스프링이 POJO 프로그래밍을 지원하려는 그 핵심 목적을 위해 중요한 역할을 하고 있다.
- 첫번째는 스프링과 같이 다이내믹 프록시를 사용하는 방법이다.
- 데코레이터 패턴을 응용한 것
- 자바의 객체지향 패턴을 활용한 방법이기 때문에 만들기 쉽고 적용하기 간편하다.
- 대신 부가기능을 부여할 수 있는 곳은 메소드의 호출이 일어나는 지점뿐 이라는 제약이 있다.
- 스프링의 기본적인 AOP 구현 방법은 다이내믹 프록시를 이용하는
프록시 AOP 방식
이다.
- 자바 언어의 한계를 넘어서는 언어의 확장을 이용하는 방법이다.
AspectJ
라는 유명한 오픈소스 AOP 툴이 있다.- 프록시 방식의 AOP 에서는 불가능한 다양한 조인 포인트를 제공한다.
- 메소드 호출뿐 아니라 인스턴스 생성, 필드 액세스, 특정 호출 경로를 가진 메소드 호출등에도 부가기능을 제공할 수 있다.
- 이런 고급 AOP 기능을 적용하려면 자바 언어와 JDK의 지원만으로는 불가능하다.
- 그 대신 별도의
AOP 컴파일러를 이용한 빌드 과정
을 거치거나, 클래스가 메모리로 로딩될 때 그 바이트 코드를 조작하는 위빙
과 같은 별도의 방법을 이용해야 한다.
AOP의 성격이 자바의 일반적인 개발 방법과는 상당히 다르기 때문에 제대로 적용하려면 충분한 시간과 노력이 필요하다.
AOP는 하나의 모듈이 수많은 오브젝트에 보이지 않게 적용되기 때문에 매우 주의해서 사용해야 한다.
차근차근 단계를 밟아 AOP를 도입하는 접근 방법이 좋다.
- 1단계 : 미리 준비된 AOP 이용
- 스프링이 직접 제공하는 대표적인 AOP는 트랜잭션이다.
- 도메인 오브젝트를 전용 계층에 두고 접근하는 아키텍처 방식을 따를 때 필요한 AOP 기능
@Configurable
이 있다.- 도메인 오브젝트에 DI를 자동적용해주는 AOP 기능이다.
AspectJ
의 AOP가 반드시 필요하다
- 위의 두 가지가 스프링이 미리 준비해서 제공하는 대표적인 AOP 기능이다.
- 2단계 : 전담팀을 통한 정책 AOP 적용
- 정책적으로 적용할 만한 기능에 AOP를 이용하는 것이다.
- 3단계 : AOP의 자유로운 이용
- 1,2단계를 거쳐 AOP에 어느 정도 친숙해지고, 그 장단점과 응용전략, 위험성등을 어느 정도 이해했다면 이제는 개발자 스스로가 AOP를 활용할 수 있는 단계로 넘어갈 수 있다.
환경과 세부 기술의 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 PSA
다.
POJO 로 개발된 코드는 특정 환경이나 구현 방식에 종속적이지 않아야 한다.
스프링은 엔터프라이즈 개발에 사용되는 다양한 기술에 대한 서비스 추상화 기능을 제공한다.
스프링의 서비스 추상화의 개념과 장점을 잘 이해한다면 때에 따라 직접 서비스 추상화 기법을 적용할 필요도 있다.