[토비의 스프링1] 8 스프링이란 무엇인가?

2022. 6. 27. 22:18Spring/개념

1. 스프링의 정의

: 자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크

 

애플리케이션 프레임워크

특정 계층이나, 기술, 업무 분야에 국한되지 않고 애플리케이션의 전 영역을 포괄하는 범용적인 프레임워크. 애플리케이션 개발의 전 과정을 빠르고 편리하며 효율적으로 진행하는데 일차적인 목표를 두는 프레임워크이다.

스프링을 MVC 프레임워크 또는 JDBC/ORM 지원 프레임워크라고 생각하는 것은 스프링이 다루는 일부 영역만 봤기 때문이다. 또, 스프링을 IOC/DI프레임워크나 AOP 툴이라고 보는 이유는 스프링이 제공하는 핵심 기술에만 주목했기 때문이다. 스프링의 일차적인 존재 목적은 핵심 기술에 담긴 프로그래밍 모델을 일관되게 적용해서 엔터프라이즈 애플리케이션 전 계층과 전 영역에 전략 기능을 제공해줌으로써 애플리케이션을 편리하게 개발하게 해주는 애플리케이션 프레임워크로 사용되는 것을 기억해야한다.

 

경량급

스프링 자체가 가볍다거나 작은 규모의 코드로 이뤄졌다는 뜻은 아니다. 예전의 EJB 같은 과도한 엔지니어링이 적용된 기술과는 다르게 불필요하게 무겁지 않다는 의미이다.

경량급이라는 의미는 스프링을 기반으로 제작되는 코드가 기존의 EJB나 여타 프레임워크에서 동작하기 위해 만들어진 코드에 비해 상대적으로 작고 단순하다는 뜻이다. 같은 기능을 수행하던 코드인데도 스프링 기반의 코드가 가벼운 이유는 코드에 불필요하게 등장하던, 프레임워크와 서버환경에 의존적인 부분을 제거해주기 때문이다. EJB와 WAS같은 기술과 환경을 지원하기 위해 군더더기처럼 우겨넣어야 했던, 판에박힌 듯이 반복되던 코드가 제거되고 나니 가장 단순하고 가벼운 코드만 남게 됐다.

 

자바 엔터프라이즈 개발을 편하게

스프링은 근본적인 부분에서 엔터프라이즈 개발의 복잡함을 제거해내고 진정으로 개발을 편하게 해주는 개발책을 제시한다. 단순히 편리한 몇가지 도구나 기능을 제공해주는 차원이 아니다. 편리한 애플리케이션 개발이란 개발자가 복잡하고 실수하기 쉬운 로우레벨 기술에 많은 신경을 쓰지 않으면서도 애플리케이션의 핵심인 사용자의 요구사항, 즉 비지니스 로직을 빠르고 효과적으로 구현하는것을 말한다.

스프링은 엔터프라이즈 개발의 기술적인 복잡함과 그에 따른 수고를 제거해준다. 제거란 반드시 기술적인 필요를 무시한다는 의미는 아니다. 엔터프라이즈 개발에서 필연적으로 요구되는 기술적인 요구를 충족하면서도 개발을 복잡하게 만들지 안흔다는 점이 스프링의 뛰어난 면이다.

 

오픈소스

스프링은 오픈소스 프로젝트 방식으로 개발돼왔다. 지금도 여전히 오픈소스 개발 모델과 오픈소스 라이선스를 가지고 개발되는 중이며, 이 사실은 앞으로도 바뀌지 않을것이다.

대부분의 오픈소스 프로젝트처럼 온라인 커뮤니티가 있다. 커뮤니티를 통해 자유로운 토론이나 버그를 발견하고 신고할 수 있다. 요청이나 버그 신고가 들어오면 이슈 트래커를 통해 공개적으로 확인이 가능하며, 수정된 코드도 언제든지 살펴볼 수 있다. 개발 과정에 많은 사람이 자유롭게 참여할 수 있다.

 

오픈소스의 장점

  • 공개된 커뮤니티의 공간 안에서 투명한 방식으로 다양한 참여를 통해 개발해 매우 빠르고 유연한 개발이 가능하다.
  • 사용자는 소스코드를 다운받아서 품질과 기능을 얼마든지 검증하고 분석해 볼 수 있다.
  • 개발중인 경우에도 소스코드가 투명하게 공개되기 때문에 사용자의 피드백이 그만큼 빨리 전달되고 반영된다.
  • 인기있는 오픈소스 제품이라면 베타 버전임에도 전 세계의 수많은 개발자가 자율적으로 다운받아서 사용해보고, 다양한 방식으로 피드백을 주기도 한다.

 

단점

  • 지속적이고 안정적인 개발이 계속될지가 불확실하다.
  • 오픈소스 프로젝트는 개발자 개개인에게 극히 의존적이다.

 

2. 스프링의 목적

스프링의 개발 철학과 궁극적인 목표가 무엇인지를 생각해보자. 스프링은 개발 표준 샘플이 없어 잘 사용하고 있다는 확신 할 수가 없다.

자바를 공부해서 JDK나 문법이나 API사용법을 다 익혔다 치자.
그러면 자바로 개발을 잘 할수있을까?
자바로 개발을 잘 하려면 결국 근본적인 프로그래밍 실력이 필요하다.
자바의 근본적인 목적은 객체지향 프로그래밍을 통해 유연하고 확장성 좋은 애플리케이션을 빠르게 만드는 것이다.
자바를 가져다가 절차지향 언어처럼 사용한다면 자바를 사용하는 가치를 얻을 수 없다.

그렇다면 스프링의 목적은 무엇인가? 굳이 스프링을 사용해서 엔터프라이즈 개발을 편하게 하려는 이유는 뭘까? 원래 엔터프라이즈 개발이란 편하지 않기 때문이다.

 

2.1 엔터프라이즈 개발의 복잡함

2000년대 초반 IT 리서치 기업의 조사에 따르면 80% 이상의 자바 엔터프라이즈 프로젝트가 실패했다고 한다. 프로젝트가 아예 중단되거나 취소된것 까지는 아니더라도, 원래 정해진 기간과 계획된 예산을 맞추지 못한 경우가 그만큼 많다는 뜻이다. 개발이 실패하는 이유에 대해 많은 논의가 있었는데, 그 과정에서 밝혀진 원인중 하나는 '엔터프라이즈 시스템 개발이 너무 복잡해져서'였다.

 

복잡함의 근본적인 이유

엔터프라이즈 시스템 개발은 왜 복잡할까? 크게 두 가지 원인을 생각해볼 수 있다.

  • 첫 번째는 기술적인 제약조건과 요구사항이 늘어가기 때문이다

엔터프라이즈 시스템은 많은 사용자의 요청을 동시에 처리해야 하기 때문에 서버의 자원을 효율적으로 공유하고 분배해서 사용할 수 있어야 한다. 또 한 중요한 기업의 핵심 정보를 처리하거나 미션 크리티컬한 금융, 원자력, 항공, 국방 등의 시스템을 다루기도 하기 때문에 보안과 안정성, 확장성 면에서도 뛰어나야 한다. 따라서 뛰어난 성능과 서비스의 안정성이 요구되고 그런 점을 고려한 개발 기술이 필요하다. 즉 엔터프라이즈 시스템을 개발하는 데는 순수한 비즈니스 로직을 구현하는 것 외에도 기술적으로 고려할 사항이 많다는 뜻이다. 문제는 이러한 엔터프라이즈 시스템의 기술적인 요구사항은 단순힌 고가의 애플리케이션 서버나 툴을 사용한다고 충족될 수 있는 게 아니라는 점이다. 따라서 이런 종류의 기술적인 문제를 고려하면서 애플리케이션 개발해야 하는 부담을 안게 된다.

  • 두 번째는 엔터프라이즈 애플리케이션이 구현해야 할 핵심기능인 비즈니스 로직의 복잡함이 증가하기 때문이다

갈수록 엔터프라이즈 시스템을 이용해 기업의 핵심 업무를 처리하는 비율이 늘어나고, 점차 대부분의 업무 처리는 컴퓨터를 이용하지 않고는 아예 진행하기 힘들 만큼 엔터프라이즈 시스템에 대한 업무 의존도가 높아졌다. 그만큼 다양하고 복잡한 업무 처리 기능을 엔터프라이즈 시스템이 구현해야 했다는 뜻이다. 원래 기업 업무란 그 자체로 복잡한데다, 다양한 예외상황도 많고, 처리해야 하는 정보의 규모도 상당하다. 엔터프라이즈 시스템이 관여하는 업무의 비율이 급격하게 커지고 있으니 당연히 애플리케이션 개발도 힘들고 복잡해져 가는 것이다.

옛날의 기업과 달리 경제 흐름과 사회의 변화, 업계의 추이에 따라서 수시로 업무 프로세스를 변경하고 조종하는 것을 상시화할 만큼 변화의 속도가 빨라졌다. 결국 이런 업무 구조와 프로세스의 변화는 이를 뒷받침 해줘야 하는 엔터프라이즈 시스템의 변경을 요구할 수밖에 없다.
버그나 오류가 있어서가 아니라, 기능 요구사항과 업무 정책 등이 바뀌기 때문에 애플리케이션을 자주 수정해줘야 하는 시대가 된 것이다. 그만큼 이전과 다르게 시스템 개발과 유지보수, 추가 개발 등의 작업에 대한 부담은 커지고 그에 따른 개발의 난이도는 더욱 증가한 것이다.

 

복잡함을 가중시키는 원인

엔터프라이즈 애플리케이션 개발이 실패하는 주요 원인은 비즈니스 로직의 복잡함과 기술적인 복잡함이다. 복잡하다는 건 단지 양이 많고 어렵다는 뜻이 아니다. 세부 요소가 이해하기 힘든 방식으로 얽혀 있고, 그 때문에 쉽게 다루기 어렵다는 의미다. 자바 엔터프라이즈 개발이 어려운 가장 큰 이유는 근본적인 비즈니스 로직과 엔터프라이즈 기술이라는 두가지 복잡함이 한데 얽혀 있기 때문이다.

2.2 복잡함을 해결하려는 도전

엔터프라이즈 개발의 근본적인 복잡함의 원인은 제거할 대상은 아니다. 현실적으로는 불가능하기 때문이다.

가장 먼저 할 일은 성격이 다른 이 두 가지 복잡함을 분리해내는 것이다.

 

  • 실패한 해결책 : EJB
  • 비침투적인 방식을 통한 효과적인 해결책 : 스프링

 

침투적인 기술

EJB처럼 어떤 기술을 적용했을 때 그 기술과 관련된 코드나 규약 등이 코드에 등장하는 경우를 침투적인 기술이라 한다.

물론 꼭 필요한 기능을 사용해야 하기 때문에 특정 기술의 API를 이용하게 되는 건 어쩔 수 없다. 그런데 꼭 필요한 기능을 사용한느 것도 아니면서 단지 어떤 기술을 바탕으로 만들어진다고 해서 특정 클래스나 인터페이스, API 등의 코드에 마구 등장한다면 그것은 침투적인 기술이 되며 복잡함을 가중시키는 원인이 된다.

 

비침투적인 기술

기술의 적용 사실이 코드에 직접 반영되지 않는다는 특징이 있다. 어딘가에는 기술의 적용에 따라 필요한 작업을 해줘야 하겠지만, 애플리케이션 코드 여기저기에 불쑥 등장하거나, 코드의 설계와 구현 방식을 제한하지는 않는다는게 비침투적인 기술의 특징이다.

 

2.3 복잡함을 상대하는 스프링의 전략

스프링의 기본적인 전략은 비즈니스 로직을 담은 애플리케이션 코드와 엔터프라이즈 기술을 처리하는 코드를 분리시키는것이다. 이 분리를 통해 두가지 복잠함의 문제를 효과적으로 공략하게 해준다.

 

기술적인 복잡함을 상대하는 전략

스프링은 엔터프라이즈 기술을 적용했을 때 발생하는 복잡함의 문제를 두 가지로 분류하고 각각에 대한 적절한 대응방법을 제공한다.

  • 첫 번째 문제 : 기술에 대한 접근방식이 일관성이 없고, 특정 환경에 종속적이다.

환경, 서버, 적용되는 조건에 따라 코드가 바뀐다는건 심각한 문제다. 목적이 유사하지만 호환이 안되는 표준, 비표준, 오픈소스, 상용 제품 등이 제공하는 각기 다른 API를 사용하는 코드를 일일히 변경해야 하는 번거로움이 발생한다.

이렇게 일관성이 없는 기술과 서버 환경의 변화에 대한 스프링의 공략 방법은 바로 서비스 추상화다.

기술적인 복잡함은 일단 추상화를 통해 로우레벨의 기술 구현 부분과 기술을 사용하는 인터페이스로 분리하고, 환경과 세부 기술에 독립적인 접근 인터페이스를 제공하는 것이 가장 좋은 해결책이다.

스프링이 제공하는 템플릿 / 콜백 패턴은 판에 박힌 반복적인 작업 흐름과 API 사용 코드를 제거해준다.
이를 통해 기술을 사용하는 코드도 최적화된 핵심 로직에만 집중하도록 도와준다.

 

  • 두 번째 문제 : 기술적인 처리를 담당하는 코드가 성격이 다른 코드에 섞여서 등장한다.

책임에 따라 계층을 구분하고 그 사이에 서로의 기술과 특성에 의존적인 인터페이스나 예외처리 등을 최대한 제거한다고 할지라도 근본적으로 엔터프라이즈 서비스를 적용하는 한 이런 문제는 쉽게 해결할 수 없다. 이런 기술과 비즈니스 로직의 혼재로 발생하는 복잡함을 해결하기 위한 스프링의 접근 방법은 바로 AOP다.

AOP는 기술을 다루는 코드로 인한 복잡함이 기술 그 자체 이상으로 불필요하게 증대되지 않도록 도와주는 가장 강력한 수단이다.

 

비즈니스와 애플리케이션 로직의 복잡함을 상대하는 전략

비즈니스 로직의 복잡함을 상대하는 전략은 자바라는 객체지향 기술 그 자체다. 스프링은 단지 객체지향 언어의 장점을 제대로 살리지 못하게 방해했던 요소를 제거하도록 도와줄 뿐이다.

 

핵심도구 : 객체지향과 DI

스프링의 모토는 결국 "기본으로 돌아가자"이다. 자바의 기본인 객체지향에 충실한 설계가 가능하도록 단순한 오브젝트로 개발할 수 있고, 객체지향의 설계 기법을 잘 적용할 수 있는 구조를 만들기 위해 DI같은 유용한 기술을 편하게 적용하도록 도와주는 것이 스프링의 기본 전략이다. 서비스 추상화, 템플릿 / 콜백, AOP와 같은 스프링의 기술은 DI없이는 존재할 수 없는 것들이다.

그리고 DI는 객체지향 설계 기술 없이는 그 존재의미가 없다. DI란 특별한 기술이라기보다는 유연하게 확장할 수 있는 오브젝트 설계를 하다 보면 자연스럽게 적용하게 되는 객체지향 프로그래밍 기법일 뿐이다. 스프링은 단지 그것을 더욱 편하고 쉽게 사용하도록 도와줄 뿐이다. 기술적인 복잡함을 해결하는 문제나 기술적인 복잡함이 비즈니스 로직에 침범하지 못하도록 분리하는 경우에도 DI가 바탕이 된 여러 가지 기법이 활용된다. 반면에 비즈니스 로직 자체의 복잡함을 해결하려면 DI보다는 객체지향 설계 기법이 더 중요하다.

 

3. POJO 프로그래밍

"스프링의 정수는 엔터프라이즈 서비스 기능을 POJO에 제공하는 것" 엔터프라이즈 서비스라 함은 보안, 트랜잭션과 같은 엔터프라이즈 시스템에서 요구되는 기술을 말한다. 이런 기술을 POJO에 제공한다는 말은, 뒤집어 생각해보면 엔터프라이즈 서비스 기술과 POJO라는 애플리케이션 로직을 담은 코드를 분리했다는 뜻이기도 하다. '분리됐지만 반드시 필요한 엔터프라이즈 서비스 기술을 POJO방식으로 개발된 애플리케이션 핵심 로직을 담은 코드에 제공한다'는 것이 스프링의 가장 강력한 특징과 목표다.

 

3.1 스프링의 핵심 : POJO

스프링 애플리케이션은 POJO를 이용해서 만든 애플리케이션 코드와, POJO가 어떻게 관계를 맺고동작하는지를 정의해놓은 설계정보록 구분된다. DI의 기본 아이디어는 유연하게 확장 가능한 오브젝트를 만들어두고 그 관계는 외부에서 다이내믹하게 설정해주는 것이다. 이런 DI의 개념을 애플리케이션 전반에 걸쳐 적용하는 것이 스프링 프로그래밍 모델이다.

IOC/DI, AOP와 PSA는 애플리케이션을 POJO로 개발할 수 있게 해주는 가능기술 이라고 불린다.

 

3.2 POJO란 무엇인가?

POJO란 Plain Old Java Object이다. "자바의 간단한 자바 오브젝트"를 이용해 애플리케이션의 비즈니스 로직을 구현하는 것이다.

 

3.3 POJO의 조건

다음의 세 가지 조건을 충족해야 POJO라고 불릴 수 있다.

  • 특정 규약에 종속되지 않는다.

POJO는 자바언어와 꼭 필요한 API외에는 종속되지 않아야한다. 따라서 EJB2와 같이 특정 규약을 따라 비즈니스 컴포넌트를 만들어야 하는 경우는 POJO가 아니다. 별다른 가치를 주지도 못하는 규약 따위에 종속되지 않아야 하고, 객체지향 설계의 자유로운 적용이 가능한 오브젝트여야만 POJO라고 불릴 수 있다.

  • 특정 환경에 종속되지 않는다.

특정 환경에 종속적이어야만 동작하는 오브젝트도 POJO라고 할 수 없다. EJB3는 POJO에 가까운 설계와 구현이 가능해졌다. 하지만 여전히 JNDI라는 서버 서비스를 필요로 한다. EJB3 빈의 의존 오브젝트 정보는 JNDI를 통해 가져와야 하기 때문이다. 따라서 JNDI가 없는 환경에서는 그대로 사용하기가 힘들다. 이렇게 JNDI와 같은 특정 환경이 의존 대상 검색 방식에 종속적이라면 POJO라고 할 수 없다.

특히 비지니스 로직을 담고 있는 POJO클래스는 웹이라는 환경정보나 웹 기술을 담고 있는 클래스나 인터페이스를 사용해서는 안 된다. 설령 나중에 웹 컨트롤러와 연결돼서 사용될 것이 뻔하다고 할지라도 직접적으로 웹이라는 환경으로 제한해버리는 오브젝트나 API에 의존해선 안된다. 그렇게 하면 웹 외의 클라이언트가 사용하지 못하게 된다. 또 웹 서버에 올리지 않고 독립적으로 테스트하기도 힘들어진다.

진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다. 그런 POJO에 애플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO프로그래밍이라고 한다.

 

3.4 POJO의 장점

  • 특정과 기술과 환경에 종속되지 않는 오브젝트는 그만큼 깔끔한 코드가 될 수 있다.
  • POJO로 개발된 코드는 자동화된 테스트에 매우 유리하다.
  • 객체지향적인 설계를 자유롭게 적용할 수 있다.

 

3.5 POJO 프레임워크

스프링은 POJO를 이용한 엔터프라이즈 애플리케이션 개발을 목적으로 하는 프레임워크다. POJO프로그래밍이 가능하도록 기술적인 기반을 제공하는 프레임워크를 POJO프레임워크라고 한다.

스프링은 개발자들이 복잡한 엔터프라이즈 기술보다는 이런 객체지향적인 설계와 개발의 원리에 좀 더 집중할 수 있도록 기회를 준다.

 

4. 스프링의 기술

좀 이따가

 

4.1 제어의 역전 / 의존관계 주입

IOC/DI는 스프링의 가장 기본이 되는 기술이자 스프링의 핵심 개발 원칙이기도 하다. 나머지 두 가지 기술인 AOP와 PSA도 IOC/DI에 바탕을 두고 있다. 3대 기술은 아니지만 자주 등장하는 템플릿 / 콜백 패턴이 적용된 부분도 IOC/DI가 그 핵심 원리다.

 

DI의 활용 방법

  • 핵심기능의 변경 

DI의 가장 대표적인 적용 방법은 바로 의존 대상의 구현을 바꾸는 것이다. 예를 들면 서비스 오브젝트가 사용하는 DAO가 있다고 할 때, DAO의 구현을 JDBC로 했다가, 그것을 JPA, 하이버네이트, JDO, IBATIS 등으로 변경하는 것을 생각할 수 있다. 구현 방식을 통째로 바꾸는 것이다.

사용자 관리 서비스라고 보자면 사용자의 등급을 결정하는 정책을 담은 코드를 DI로 분리할 수 있다. 만약 비지니스 로직이 변경돼서 새로운 등급결정 정책을 적용해야 한다면, DI를 이용해 새로운 정책을 담은 클래스로 통째로 변경해주면 된다.

  • 핵심기능의 동적인 변경

두번째 활용 방법은 첫 번째랑 비슷하게 의존 오브젝트의 핵심기능 자체를 바꾸는 것이다. 하지만 일반적인 DI를 이용한 변경 방법과는 달리, 동적으로 매번 다르게 변경할 수 있다. 

동적인 방식으로 핵심기능을 변경하는 건, 기술적으로 보자면 다이내믹 라우팅 프록시나 프록시 으보젝트 기법을 활용하는 것이다. 그런 기법을 적용할 수 있었던건 역시 DI가 있기 때문이다. DI의 원칙은 여전히 지켜지므로 확장과 재사용이라는 장점은 손상되지 않고 오히려 더 가치를 드러낸다.

  • 부가기능의 추가

세 번째 활용 방법은 핵심기능은 그대로 둔 채로 부가기능을 추가하는 것이다. 데코래이터 패턴을 생각해보면 된다. 인터페이스를 두고 사용하게 하고, 실제 사용할 오브젝트는 외부에서 주입하는 DI를 적용해두면 데코래이터 패턴을 쉽게 적용할 수 있다. 그래서 핵심기능과 클라이언트 코드에는 전혀 영향을 주지 않으면서 부가적인 기능을 얼마든지 추가할 수 있다.

  • 인터페이스의 변경

때로는 사용하려고 하는 오브젝트가 가진 인터페이스가 클라이언트와 호환되지 않는 경우가 있다. 또는 여러 종류의 인터페이스를 가졌지만 사실은 비슷한 기능을 담당하는 오브젝트를 바꿔가면서 사용하고 싶을 때도 있다. 이렇게 클라이언트가 사용하는 인터페이스와 실제 오브젝트 사이에 인터페이스가 일치하지 않는 경우에도 DI가 유용하다.

예를들어 A가 C오브젝트를 사용해야 한다. 그런데 원래 A는 B인터페이스를 사용하도록 만들어져있다. C는 B인터페이스를 구현하고있지 않다. 이때 A가 DI를 통해 B의 구현 오브젝트를 받도록 만들어져 있다면 B인터페이스를 구현해주면서 내부에서 C를 호출해주는 기능해주는 어댑터 오브젝트를 만들어서 A에 DI를 해준다. 이를 좀 더 일반화해서 인터페이스가 다른 다양한 구현을 같은 방식으로 사용하도록, 중간에 인터페이스 어댑터 역할을 해주는 레이어를 하나 추가하는 방법도 있다. 

  • 프록시

필요한 시점에서 실제 사용할 오브젝트를 초기화하고 리소스를 준비하게 해주는 지연된 로딩을 적용하려면 프록시가 필요하다. 원격 오브젝트를 호출할 때 마치 로컬에 존재하는 오브젝트처럼 사용할 수 있께 해주는 원격 프록시를 적용하려고 할 때도 프록시가 필요하다. 두 가지 방법 모두 DI를 필요로 한다. 스프링은 EJB원격 호출을 포함해서 웹 서비스, REST호출, HTTP방식의 호출 등 다양한 리모팅 기술을 지원한다. 당연히 모두 DI를 통해 이뤄진다.

  • 템플릿과 콜백

템플릿/콜백 패턴은 DI의 특별한 적용 방법이다. 반복적으로 등장하지만 항상 고정적인 작업 흐름과 그 사이에서 자주 바뀌는 부분을 분리해서 템플릿과 콜백으로 만들고 이를 DI원리를 응용해 적용하면 지저분하게 매번 만들어야 하는 코드를 간결하게 만들 수 있다. 스프링이 제공하는 20여 가지의 템플릿/콜백이 적용된 기능을 가져다 활용하는 것뿐 아니라 필요에 따라서는 DI원리를 따라 직접 응용할 수 있어야 한다. 콜백을 템플릿에 주입하는 방식으로 동작하게 하는것은 DI의 원리에 가장 충실한 응용 방법이다. 콜백을 얼마든지 만들어서 사용할 수 있다는 건 개방을 통한 유연한 확장을 보여주는 것이며, 템플릿은 한번 만들어두면 계속 재사용할 수 있다는 건 기능의 확장에도 변하지 않는다는 OCP의 폐쇄 원칙에 가장 잘 들어맞는 것이다. 

  • 싱글톤과 오브젝트 스코프

DI가 필요한 중요한 이유 중 한가지는 DI할 오브젝트의 생명주기를 제어할 수 있다는 것이다. DI를 프레임워크로 이용한다는건 DI대상 오브젝트를 컨테이너가 관리한다는 의미다. 오브젝트의 생성부터 관계설정, 이용, 소멸에 이르기까지의 모든 과정을 DI 컨테이너가 주관하기 때문에 그 오브젝트의 스코프를 자유롭게 제어할 수 있다.

단일 싱글톤이 아니라 임의의 생명주기를 갖는 오브젝트가 필요할 때도 있다. 스프링에서는 싱글톤 외에도 다양한 스코프를 갖는 오브젝트를 만들어 DI에 사용할 수도 있다. HTTP 요청당 하나의 오브젝트가 만들어지거나, HTTP 세션당 하나씩 오브젝트가 만들어지게 할 수 있다. 개발자 스스로 인정한 스코프를 갖는 오브젝트를 만들고 이를 DI에 적용하는것도 가능하다. 이것 또 한 DI를 적용했기 때문에 가능한 활용 방법이다.

  • 테스트

마지막으로 DI의 중요한 용도는 바로 테스트다. 여타 오브젝트와 협력해서 동작하는 오브젝트를 효과적으로 테스트하는 방법은 가능한 한 고립시키는 것이다. 즉 다른 오브젝트와의 사이에서 일어나는 일을 테스트를 위해 조작할 수 있도록 만든다. 그래야만 테스트 대상인 오브젝트의 기능에 충실하게 테스트가 가능하다. 

잘 살펴보면 DI의 용도는 디자인 패턴 중에서 오브젝트 합성 방식을 따르는 패턴과 관련이 있음을 알 수 있다. GOF의 디자인 패턴 중에서 인터페이스를 두고 오브젝트를 분리하는 구조를 가진 오브젝트 스코프의 패턴은 DI의 구조에 대부분 잘 들어맞는다. 그런 패턴의 장점들을 애플리케이션 전 영역에서 간단한 설정만으로 자연스럽게 적용할 수 있게 만들어주는 것이 바로 DI다.

이런 활용 방법은 한 번에 한 가지만 선택적으로 사용해야 하는 건 아니다. 여러가지 활용 방법을 한 번에 적용할 수도 있다. 예를 들면 하나의 DI 대상에 대해 핵심기능도 업무 변화에 따라서 바꾸면서, 부가기능도 여러 개 추가해넣고, 테스트에서도 활용하는 식으로 사용해도 된다.

 

4.2 애스펙트 지향 프로그래밍(AOP)

AOP와 OOP는 서로 배타적이 아니다. 객체지향 기술의 복잡해져가는 애프리케이션의 요구조건과 기술적인 난해함을 모두 해결하는데 한계가있어, OOP의 한계와 단점을 극복하기 위해 도와주는 보조적인 패러다임이 AOP다. AOP를 사용하면 그 결과로 OOP를 더욱 OOP답게 만들 수 있다.

스프링의 목적인, POJO만으로 엔터프라이즈 애플리케이션을 개발하면서도 엔터프라이즈 서비스를 선언적으로 제공하는 데 반드시 필요한 것이 바로 이 AOP 기술이다. IOC/DI를 이용해서 POJO에 선언적인 엔터프라이즈 서비스를 제공할 수 있지만 일부 서비스는 순수한 객체지향 기법만으로는 POJO의 조건을 유지한 채로 적용하기 힘들다.

AOP의 적용 기법

  • 스프링과 같이 다이내믹 프록시를 사용하는 방법

이 방법은 기존 코드에 영향을 주지 않고 부가기능을 적용하게 해주는 데코레이터 패턴을 응용한 것이다. 자바의 객체지향 패턴을 활용한 방법이기 때문에 만들기 쉽고 적용이 간편하다. 대신 부가기능을 부여할 수 있는 곳은 메소드의 호출이 일어나는 지점뿐이라는 제약이 있다. 스프링의 기본적인 AOP구현 방법은 다이내믹 프록시를 이용하는 프록시 AOP 방식이다.

  • 자바 언어의 한계를 넘어서는 언어의 확장을 이용하는 방법

AspectJ라는 유명한 오픈소스 AOP툴이 있다. AspectJ는 강력한 고급 기능을 가진 AOP를 제공한다. AspectJ는 프록시 방식의 AOP에서는 불가능한 다양한 조인 포인트를 제공한다. 메소드 호출뿐 아니라 인스턴스 생성, 필드 액세스, 특정 호출 경로를 가진 메소드 호출 등에도 부가기능을 제공할 수 있다. 사용하기 까다롭고 번잡하지만 경우에 따라서는 프록시 방식의 AOP로는 할 수 없는 작업을 위해 AspectJ를 사용해야 한다. 스프링은 프록시 방식의 AOP를 기본으로 하고 있지만, 원한다면 AspectJ를 이용한 AOP로 바꿔서 사용할 수 있다. 스프링의 특별한 기능 중에는 AspectJ를 꼭 사용해야 하는 것도 있다.

 

AOP의 적용 단계

 

AOP는 객체지향 개발 방법에서 착안했지만 그 성격이 자바의 일반적인 개발 방법과는 상당히 다르기 때문에 본격적으로 적용하기에는 충분히 시간과 노력이 필요하다. 개발자 개개인이 아무렇게나 AOP를 남발해서 사용하다 보면 다른 개발자가 만든 코드가 예상하지 않은 방식으로 돌아가는 등의 혼란을 초래할 수 있다. AOP는 하나의 모듈이 수 많은 오브젝트에 보이지 않게 적용되기 떄문에 매우 주의해서 사용해야 한다.

  • 1. 미리 준비된 AOP 이용

스프링이 미리 만들어서 제공하는 AOP를 사용한다. 대표적으로는 트랜잭션이다. DB를 사용하는 애플리케이션이라면 트랜잭션이 필요할테니 이 트랜잭션 적용을 스프링 AOP 도입의 첫 번째 단계로 이용한다. AOP설정을 통해서 트랜잭션이 어떻게 많은 오브젝트에 투명하게 적용되는지 관찰해보고, AOP의 특성과 동작원리를 이해해보자.

트랜잭션만큼 자주 적용되지는 않지만 특정 아키텍처를 선택했을 때 사용할 수 있도록 준비된 AOP기능이 하나 더 있다. @Configurable 애노테이션을 이용해서 도메인 오브젝트에 DI를 자동적용해주는 AOP기능이다. 도메인 오브젝트를 전용 계층에 두고 접근하는 아키텍처 방식을 따를 때 반드시 필요하다. 프록시 AOP면 충분한 트랜잭션과 달리, @Configurable을 위해서는 AspectJ를 이용한 AOP가 반드시 필요하다.

  • 2. 전담팀을 통한 AOP 적용

아직까지는 개발자 개개인이 AOP기능을 직접 이용하게 해서는 안 된다. 대신 애플리케이션 전체적으로 이용 가능한 것을 소수의 AOP 담당자 관리하에 적용해볼 수 있다. 대표적으로 비즈니스 로직을 가진 오브젝트에 대한 보안, 특정 계층의 오브젝트 이용 전후의 작업 기록을 남기는 로깅, 데이터 추적을 위한 트레이싱, 특정 구간의 실시간 성능 모니터링과 같은 정책적으로 적용할 만한 기능에 AOP를 이용하는 것이다.

이런 기능을 개발자가 직접 자신이 만든 코드에 추가하려면, 개발 표준이나 가이드라인이 미리 완벽하게 준비되어 있어서 이를 따라 개발하게 해야 한다. 하지만 개발자가 실수로 빼먹을 수도 있고, 가이드라인을 제대로 따르지 못하고 엉뚱하게 적용할 수도 있다. 하지만 이런 일을 AOP를 이용해 한 번에 적용한다면 일반 개발자의 작업에는 전혀 영향을 주지 않을 수 있다. AOP를 책임지는 소수의 팀만 수고하면 그만이다.

  • 3. AOP의 자유로윤 이용

이전 단계에서는 애플리케이션 전체적으로 적용되는 정책 AOP를 위주로 했다면 잊데는 개발자가 구현하는 기능에 적용하면 유용한 세부적인 AOP를 이용할 수 있다. 큰 범위에 걸쳐서 적용되는 기능은 아니지만 한 모듈 또는 특정 기능 안에서도 AOP로 분리하면 유용한 것들이 있다. 다른 팀이나 개발자가 만든 코드에 몰래 적용되는 AOP기능은 만들어선 안 된다. 그런 위험만 주의한다면 얼마든지 개발자가 지신이 다루는 코드에 AOP를 적극 활용할 수 있다.

 

4.3 포터블 서비스 추상화(PSA)

환경과 세부 기술의 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 PSA다. POJO로 개발된 코드는 특정 환경이나 구현 방식에 종속적이지 않아야 한다. 스프링은 JavaEE를 기본 플랫폼으로 하는 자바 엔터프라이즈 개발에 주로 사용된다. 따라서 다양한 JavaEE기술에 의존적일 수밖에없다. 특정 환경과 기술에 종속적이지 않다는 게 그런 기술을 사용하지 않는다는 뜻은 아니다. 다만 POJO코드가 그런 기술에 직접 노출되어 만들어지지 않는다는 말이다. 이를 위해 스프링이 제공하는 대표적인 기술이 바로 일관성 있는 서비스 추상화 기술이다.

스프링의 서비스 추상화의 개념과 장점을 잘 이해한다면 때에 따라 직접 서비스 추상화 기법을 적용할 필요도 있다. 표준 기술뿐 아니라 오픈소스 라이브러리, 상용 프레임워크 형태로도 하루가 멀다 하고 새로운 기술이 등장한다. 보편적으로 사용되는 기술이라면 아마도 다음 버전의 스프링에서 서비스 추상화 대상으로 포함시킬 가능성이 있다. 하지만 그것을 굳이 기다려야 할 이유는 없다. 필요하면 스프링이 그랬던 것 처럼 직접 추상 레이어를 도입하고 일관성 있는 API를 정의해서 사용하면 된다.

 

서비스 추상화를 위해 필요한 기술은 DI뿐이다. 결국 DI 응용 방법의 한 가지이므로 DI를 적극 활용해서 개발한다면 서비스  추상화는 자연스럽게 만들어 쓸 수 있다. 서비스 추상화는 단지 구체적인 기술에 종속되지 않게 하기 위해서만 사용되는 건 아니다. 테스트가 어렵게 만들어진 API나 설정을 통해 주요 기능을 외부에서 제어하게 만들고 싶을 때도 이용할 수 있다.

 

스프링이 어떻게 해서 엔터프라이즈 개발이 주는 복잡함을 제거하고, POJO프로그래밍이라는 효과적인 방법을 사용할 수 있게 하는지에 관심을 갖는것이 스프링을 가장 빠르게 이해하고 적용할 수 있는 지름길이다.