애플리케이션의 기본 틀 - 스프링 컨테이너

  • 스프링 컨테이너 또는 어플리케이션 컨택스트라 불리는 스프링 런타임 엔진
  • 스프링 컨테이너는 설정정보를 참고로 해서 애플리케이션을 구성하는 오브젝트를 생성및 관리
  • 독립적으로 동작이 가능하지만 보통 웹 모듈에서 동작하는 서비스나 서블릿으로 등록해서 사용
  • 컨테이너를 다루는 방법과 설정정보를 작성하는 방법을 알아야 스프링을 사용할 수 있다.

프레임워크

  • 애플리케이션을 구성하는 오브젝트가 생성되고 동작하는 방식에 대한 틀을 제공
  • 애플리케이션 코드가 어떻게 작성돼야 하는지에 대한 기준 제시
  • 위의 틀을 보통 프로그래밍 모델이라고 한다.

스프링의 핵심 프로그래밍 모델

  1. IoC/DI
    • 오브젝트의 생명주기와 의존관계에 대한 프로그래밍 모델
  2. 서비스 추상화
    • 환경이나 서버, 특정 기술에 종속되지 않고 이식성이 뛰어나며 유연한 애플리케이션을 만들 수 있다.
    • 구체적인 기술과 환경에 종속되지 않는 유연한 추상 계층을 두는 방법
  3. AOP
    • 애플리케이션에 산재해서 나타나는 부가적인 기능을 독립적으로 모듈화하는 프로그래밍 모델
    • 다양한 엔터프라이즈 서비스를 적용하고도 깔끔한 코드를 유지할 수 있게 해준다.

기술 API

  • 스프링은 엔터프라이즈 애플리케이션을 개발의 다양한 영역에 바로 활용할 수 있는 많은 양의 기술 API를 제공한다.
  • 스프링에서 일관된 방식으로 사용할 수 있도록 지원해주는 기능과 전략 클래스 등을 제공
  • 엔터프라이즈 기술을 사용할 때는 스프링이 제공하는 기술 API와 서비스를 활용한다.

스프링 핵심가치

  • 단순함
    • 객체지향 언어의 장점을 개발자들이 살릴 수 있도록 도와주는 도구
    • 가장 단순한 객체지향적인 개발 모델인 POJO 프로그래밍을 강력히 주장한다.
  • 유연성
    • 다른 프레임워크와 편리하게 접목돼서 사용
    • 접착(glue)프레임워크라고 불린다.

항상 프레임워크 기반은 접근 방법을 사용해라

스프링 공부법

  • 스프링의 핵심 가치와 원리에 대한 이해

    핵심가치를 이해하고 스프링 스스로가 그 가치를 어떻게 적용해서 만들어져 있는지를 이해

    세 가지 핵심기술(컨테이너, 공통 프로그래밍 모델, 기술API)이 어떻게 핵심가치를 실현하는지?

    스프링이 강조하는 프로그래밍 모델(IoC/DI, 서비스 추상화, AOP)이 무엇인가?

  • 스프링의 기술에 대한 지식과 선택 기준 정립

    어떻게 다양한 방법으로 확장하고 적용했는지 알아야 한다.

    다양한 선택의 문제를 각 기술영역별로 효과적으로 다루는 법을 배워야 한다.

    스프링이 제공하는 기술과 종류와 접근 방법에는 어떤 것이 있는지 충분히 살펴보고, 선택의 기준을 마련해서 상황에 맞는 최선의 기술과 접근 방법을 선택할 수 있어야 한다.

  • 스프링의 적용과 확장

    스프링의 다양한 기술을 어떻게 실제 애플리케이션 개발에 어떤 식으로 적용해야 하는지를 공부

스프링의 정의

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

애플리케이션 프레임워크

일반적으로 라이브러리나 프레임워크는 특정 업무 분야나 한 가지 기술에 특화된 목표를 가지고 만들어진다.

프레임워크는 애플리케이션의 특정 계층에서 주로 동작하는 한 가지 기술 분야에 집중된다. 하지만 스프링은 이와 다르게 ‘애플리케이션 프레임워크’라는 특징을 갖고 있다.

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

여러 계층의 다양한 기술을 한데 모아뒀기 때문에 프레임워크라고 불리는 건 아니다. 애플리케이션의 전 영역을 관통하는 일관된 프로그래밍 모델과 핵심 기술을 바탕으로 해서 각 분야의 특성에 맞는 필요를 채워주고 있기 때문에 애플리케이션을 빠르고 효과적으로 개발 할 수가 있다.

경량급

스프링이 경량급이라는 건 스프링 자체가 가볍다거나 작은 규모의 코드로 이뤄졌다는 뜻은 아니다. 불필요하게 무겁지 않다는 의미다. 과도한 엔지니어링이 적용된 기술을 사용하지 않았다는 의미다. 무겁고 복잡한 EJB와 대비하여 스프링은 가볍고 사용하기 쉽다. EJB의 고급기능을 스프링은 쉽게 사용한다. EJB와 WAS 같은 기술과 환경을 지원하기 위해 군더더기처럼 존재하던 boilerplate 코드가 제거되고 단순하고 가벼운 코드를 남긴다.

스프링에 사용된 경량의 의미는 만들어진 코드가 지원하는 기술수준은 비슷하더라도 그것을 훨씬 빠르고 간편하게 작성하게 해줌으로써 생산성과 품질면에서 유리하는 것을 뜻한다.

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

스프링은 엔터프라이즈 개발의 근본적인 문제점에 도전해서 해결책을 제시한다. 편리한 애플리케이션 개발이란 개발자가 복잡하고 실수하기 쉬운 로우레벨 기술에 많은 신경을 쓰지 않으면서 비즈니스 로직을 빠르고 효과적으로 구현하는 것을 말한다.

오픈소스

스프링은 오픈소스 프로젝트 방식으로 개발돼왔다. 아파치 2.0 오픈소스 라이선스를 적용해 상업적인 목적과 비공개 프로젝트에 자유롭게 이용해도 된다. 다만 스프링을 사용한다는 점과 원 저작자를 밝히고 제품을 패키지할 때 라이선스 정보를 포함시켜야 한다. 수정하여 사용하더라도 공개할 의무는 없다.

스프링의 목적

스프링의 목적, 만들어진 이유, 궁극적으로 이루고자 하는 목표는 엔터프라이즈 애플리케이션의 개발을 편하게한다.

원래 엔터프라이즈 개발이란 편하지 않다.

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

엔터프라이즈 시스템 개발이 너무 복잡하여 자바 엔터프라이즈 프로젝트는 80%이상이 실패해왔다.

복잡함의 근본적인 이유

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

엔터프라이즈 시스템이란 서버에서 동작하며 기업과 조직의 업무를 처리해주는 시스템을 말한다. 엔터프라이즈 시스템은 많은 사용자의 요청을 동시에 처리해야하기 때문에 서버의 자원을 효율적으로 분배해서 사용할 수 있어야 한다. 중요한 기업의 핵심정보를 처리하기 때문에 보안성과 안전성, 확장성 면에서 뛰어나야 한다. 종합하면 뛰어난 성능과 서비스의 안전성이 보장되고 그런 점을 고려한 개발 기술이 필요하다.

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

엔터프라이즈 시스템에 대부분의 업무 처리를 진행하며 의존도가 높아졌다. 이에 따라 다양하고 복잡한 업무 처리 기능을 엔터프라이즈 시스템이 구현해야 했다.

수시로 업무 프로세스를 변경하고 조종하는 것을 상시화하 만큼 변화의 속도가 빨라짐. 기능 요구사항과 정책 등이 바뀌기 때문에 애플리케이션을 자주 수정해줘야 한다.

복잡함을 가중시키는 원인

앤터프라이즈 애플리케이션 개발이 실패하는 주요 원인은 비즈니스 로직의 복잡함기술적인 복잡함이다. 복잡함이란 단지 양이 많고 어렵다는 뜻이 아니다. 세부 요소가 이해하기 힘든 방식으로 얽혀 있고, 그 때문에 다루기가 어렵다는 의미다.

근본적인 비즈니스 로직과 엔터프라이즈 기술이라는 두 가지 복잡함이 한데 얽혀 있다.

전통적인 자바 엔터프라이즈 개발 기법은 대부분 비즈니스 로직의 복잡한 구현 코드와 엔터프라이즈 서비스를 이용하는 기술적인 코드가 혼재될 수밖에 없는 방식이다. 개발자가 두 가지를 모두 신경 써서 개발해야 하는 과도한 부담을 준다.

복잡함을 해결하려는 도전

제거될 수 없는 근본적인 복잡함

복잡함을 해결하고자 비즈니스 적용 범위를 줄인 반쪽짜리 시스템을 만든다거나 기술적인 복잡함 덜기 위해 보안이 취약하거나, 확장이 불가능한 시스템을 만들 수는 없다.

따라서, 엔터프라이즈 개발에 나타나는 복잡함의 원인을 효과적으로 상대할 수 있는 전략과 기법이 필요하다. 비즈니스 로직의 복잡함을 효과적으로 다루기 위한 방법과 기술적인 복잡함을 효과적으로 처리하는 데 적용되는 방법은 다르다. 두 가지 복잡함이 혼재하는 전통적인 개발 방식은 복잡함을 다루는데 적합하지 않다. 따라서 성격이 다른 두 가지를 분리하는 것이다.

실패한 EJB

EJB 틀 안에서 자바 코드를 만들게 강제함으로써 자바 언어가 갖고 있던 장점을 잃어버렸다. EJB의 특정 클래스를 상속하게 함으로써 더 이상 상속구조를 적용하지 못하게 만들거나, 다형성을 제한하는 등 자바의 장점을 잃게 되었다.

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

스프링은 EJB의 실패를 교훈으로 삼아서 출발했다. 기술적인 복잡함을 애플리케이션의 핵심 로직의 복잡함에서 분리하는 데 목표를 뒀다.

어떤 기술을 적용하였을 때 그 기술과 관련된 코드나 규약 등이 코드에 등장하는 경우를 침투적인(invasive) 기술이라고 한다. 꼭 필요한 기능을 사용해야 하기 때문에 특정 기술 API를 이용하게 되는 건 어쩔 수 없다.

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

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

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

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

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

    환경이 바뀌고, 서버가 바뀌고, 적용되는 조건이 바뀌면 적용하는 기술이 달라지고 그에 따라 코드도 바뀐다는 건 심각한 문제다. 기술이 변경되면 목적이 유사하지만 사용 방법, 접근 방식이 달라 호환이 불가능한 코드를 일일이 변경하는 번거로움이 발생한다.

    스프링은 서비스를 추상화를 통해 일관성 없는 기술과 서버환경의 변화에 대응한다.

    트랜잭션, OXM, 데이터 액세스에 관한 일관된 예외 변환기능, 데이터 액세스 기술에 독립적인 트랜잭션 동기화 기법

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

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

    비즈니스 로직 전후로 경계가 설정돼야 하는 트랜잭션, 비즈니스 로직에 대한 보안 적용, 계층 간 데이터 이동과 예외의 일괄 변환이나 로깅과 감사가 대표적인 예다. 기술과 비즈니스 로직의 혼재로 발생하는 복잡을 해결하는 AOP

    AOP는 기술 관련 코드를 분리해서 별도의 모듈로 관리하게 해주는 강력한 기술이다. 기술과 비즈니스 로직의 혼재와 기술 코드가 여기저기 중복돼서 나타나는 문제를 해결한다.

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

엔터프라이즈 시스템 개발에서 비즈니스 로직은 SQL이 아닌 애플리케이션 안에서 처리하도록 만든다. DB는 단지 데이터의 영구적인 저장과 복잡한 조건을 가진 검색과 같은 자체적으로 특화된 기능에만 활용하고, 데이터를 분석하고 가공하고 그에 따라 로직을 처리하는 부분은 확장하기 쉽고, 비용도 싼 애플리케이션 서버 쪽으로 이동하는 것이다.

자바라는 객체지향 언어를 통해 객체지향 프로그래밍 기법과 언어가 주는 장점인 유연한 설계와 높은 재사용성을 활용하면 변경이 잦고 조건이 까다로운 비즈니스 로직을 효과적으로 구현할 수 있다.객체지향 분석과 설계(OOAD)를 통해서 작성된 모델을 코드로 구현하고 지속적으로 발전시킬 수도 있다.

스프링은 뒤에서 비즈니스 로직을 담당하는 오브젝트들에게 적절한 엔터프라이즈 기술 서비스가 제공되도록 변태처럼 은밀하게 도와준다.

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

핵심 도구: 객체지향과 DI

기술과 비즈니스 로직의 복잡함을 해결하는 데 스프링은 공통적으로 사용한 도구는 객체지향이다. 하지만 EJB로 인해 객체지향 프로그래밍의 장점을 잃어버리게 되었다.

스프링의 모토는 기본으로 돌아가자[POJO(Plain Old Java Object)]다. 자바의 기본인 객체지향에 충실한 설계가 가능하도록 단순한 오브젝트로 개발할 수 있고, 객체지향의 설계 기법을 잘 적용할 수 있는 구조를 만들기 위해 DI 같은 유용한 기술을 편하게 적용하도록 도와주는 것이 스프링의 기본 전략이다.

서비스 추상화, 템플릿/콜백, AOP와 같은 스프링의 기술은 DI없이는 존재할 수 없다.

객체지향적인 특성을 잘 살린 설계는 상속과 다형성, 위임을 포함해서 많은 객체지향 디자인 패턴과 설계 기법이 잘 녹아 들어갈 수 있다.

스프링의 기술과 전략은 객체지향이라는 자바 언어가 가진 강력한 도구를 극대화해서 사용할 수 있도록 돕는 것이다.

POJO 프로그래밍

스프링의 정수(essence)는 엔터프라이즈 서비스 기능을 POJO에 제공하는 것

엔터프라이즈 서비스 기술과 POJO라는 애플리케이션 로직을 담은 코드를 분리함

스프링의 핵심: POJO

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

스프링의 주요기술 IoC, DI, AOP, PSA(Portable Service Abstration)는 애플리케이션을 POJO로 개발할 수 있게 해주는 가능기술(enabling technology)라고 부른다.

POJO란 무엇인가?

POJO는 Plain Old Java Object의 첫 글자를 따서 만든 약자다. 마틴 파울러가 2000년에 컨퍼런스를 준비하다가 EJB와 같이 세련된 용어를 사람들이 선호하는 것을 알고 간단한 자바 오브젝트를 POJO라고 부르기 시작했다.

POJO의 조건

적어도 세 가지 조건을 충족해야 POJO라고 불릴 수 있다.

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

    POJO는 자바 언어와 꼭 필요한 API 외에는 종속되지 않아야 한다. 특정 규약에 따라 비즈니스 컴포넌트를 만들어야 하는 경우는 POJO가 아니다. 특정 규약을 따라 만들게 하는 경우는 대부분 규약에서 제시하는 클래스를 상속하도록 요구한다. 그럴 경우 자바의 단일 상속 제한 때문에 더 이상 해당 클래스에 객체지향적인 설계 기법을 적용하기가 어렵다. 또 규약이 적용된 환경에 종속적되기 때문에 다른 환경으로 이전이 힘들다는 문제점이 있다.

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

    특정 환경이 의존 대상 검색 방식에 종속적이라면 POJO라고 할 수 없다. 특정 서버나 특정 OS의 시스템 콜 등 환경에 종속적인 클래스나 API를 사용한 오브젝트는 POJO라고 할 수 없다. POJO는 환경에 독립적이어야 한다.

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

  • 객체지향적 원리에 충실해야 한다.

    애노테이션을 사용한 경우에는 POJO일까 아닐까? 애노테이션이 단지 코드로 표현하기는 적절치 않은 부가적인 정보를 담고 있고, 그 때문에 환경에 종속되지만 않는다면 여전히 POJO라고 할 수 있다. 하지만 애노테이션이나 엘리먼트 값에 특정 기술과 환경에 종속적인 정보를 담고 있다면 그떄는 POJO로서의 가치를 잃어버린다고 할 수 있다.

    특정 기술규약과 환경에 종속되지 않는다고 모두 POJO는 아니다. POJO는 객체지향적인 자바 언어의 기본에 충실하게 만들어져야 한다. 책임과 역할이 알맞게 분리되고 재사용이 가능하며 객체간에는 느슨한 결합을 가져야 한다.

진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트를 말한다.

POJO의 장점

특정한 기술과 환경에 종속되지 않은 오브젝트는 그만큼 깔끔한 코드가 될 수 있다. 그에 따라 코드의 가독성을 높이고 테스트 작성의 용이성, 유지보수의 적은 부담을 느낄 수 있다.

서버의 구동 및 빌드와 배치가 불필요하여 자동화된 테스트를 쉽게한다.

객체지향적인 설계를 자유롭게 적용할 수 있다. 도메인 모델, 디자인 패턴 등은 POJO가 아니고는 적용하기 힘들다. 객체지향적인 설계와 구현 방식이 실제 프로젝트를 성공시키는 데 중요한 요소이다.

POJO 프레임워크

POJO 프로그래밍이 가능하도록 기술적인 기반을 제공하는 프레임워크를 POJO 프레임워크라고 한다. 스프링과 하이버네이트를 대표적인 POJO 프레임워크라고 한다.

스프링은 엔터프라이즈 기술과 비즈니스 로직을 분리하여 개발자가 객체지향적인 설계와 개발의 원리에 좀 더 집중할 수 있도록 기회를 준다. 스프링의 기술과 API 및 확장 포인트를 이용하는 코드가 자연스럽게 객체지향 설계원리를 따라가도록 이끌어 준다.

스프링의 기술

스프링의 POJO 프로그래밍을 손쉽게 할 수 있도록 세 가지 가능기술(enabling technology)을 제공하는데 이는 IoC/DI, AOP, PSA다.

스프링을 특정 기술(IoC/DI, AOP, PSA)을 지원해주는 단순한 프레임워크로 여기기보단 POJO 기반의 엔터프라이즈 개발을 편리하게 해주는 도구로 이해하자. 세 가지 기술 자체가 이미 객체지향의 원리를 충실히 적용해서 나온 결과이기도 하다. 스프링이 제공하는 PSA만 달랑 사용하지 말고 직접 제공하지 않는 기술에 대해서도 PSA를 적용할 줄 알아야 한다. 그것이 스프링의 목적과 개발 철학에 부합하는 스프링의 사용법이다.

제어의 역전(IoC)/의존관계 주입(DI)

직접 사용할 오브젝트를 생성해서 사용하는 강한 결합보다 인터페이스를 두고 느슨하게 연결한 뒤 외부에서 주입받는 것이 나은 점은 무엇일까?

‘유연한 확장이 가능하게 하기 위해서’라고 할 수 있다. DI는 개방 폐쇄 원칙(OCP)이라는 객체지향 설계원리와 밀접한 관계가 있다. 유연한 확장=확장에는 열려있다, DI=변경에는 닫혀있다. 폐쇄는 재사용가능하다라고 볼 수 있다.

A→B라는 의존관계를 갖는 오브젝트 구조라고 생각해보자. 여기서 확장은 B가 자유롭게 변경될 수 있음을 의미한다. B가 변경돼도 A는 아무런 영향을 받지 않고 그대로 유지할 수 있다. B관점에서 유연한 확장이고 A관점으로 보면 변경 없이 재사용이 가능하다고 볼 수 있다. 의존 대상 B가 아무리 바뀌어도 A는 그대로 재사용 가능하다고 볼 수 있다.

DI의 활용 방법

  • 핵심기능의 변경

    DI의 가장 대표적인 적용 방법은 바로 의존 대상의 구현을 바꾸는 것이다. A→B구조에서 A의 기능 일부를 B에게 위임한다고 했을 때 B의 구현방식을 필오에 따라 통쨰로 B1, B2, B3로 바꾸는 것이다. 실존하는 대상이 가진 핵심기능을 DI 설정을 통해 변경하는 것이 대표적인 DI 활용

  • 핵심기능의 동적인 변경

    의존 오브젝트의 핵심기능 자체를 바꾸는 것이다. 일반적인 DI와 달리, 동적으로 매번 다르게 변경할 수 있다. 일단 DI 되고 나면 그 후로는 바뀌지 않는다. 하지만 DI를 잘 활용하면 애플리케이션 동작하는 중간에 그 의존 대상을 다이내믹하게 바꿀 수 있다.

    한 DAO가 여러 개의 DataSource에 의존하게 만들 수 있다. 현재 접속한 사용자의 등급별로 DataSource를 변경하여 빠른 처리 속도를 보장할 수 있다.

    사용자별 독립적인 의존 오브젝트를 두게 만들 수 있다. 한번 로그인한 사용자는 로그아웃하거나 다른 브라우저로 다시 들어오기 전에는 계속 자신만의 의존 오브젝트를 유지하게 하고 서비스 오브젝트가 이를 DI받아서 사용하게 할 수 있다. 이때는 핵심기능이 바뀌기 보다는 독립정인 상태 정보를 저장하는 오브젝트를 가질 수 있다.

  • 부가기능의 추가

    핵심기능은 그대로 둔 채로 부가기능을 추가할 수 있다. DI를 통해 데코레이션 패턴을 쉽게 적용할 수 있다. 실제 사용할 오브젝트는 외부에서 주입하는 DI를 적용해두어 핵심기능과 클라이언트 코드에 영향을 주지 않으면서 부가적인 기능을 얼마든지 추가할 수 있다.

    트랜잭션이 대표적인 예다. 핵심 기능은 그대로 둔 채로 결과나 전달 파라미터를 조작, 파라미터나 리턴 결과를 이용해 로깅과 보안 처리 같은 부가 작업을 수행할 수 있다.

    부가 기능이 추가 방식을 특정 오브젝트가 아니라 좀 더 많은 대상으로 일반화해서 적용하면 AOP가 된다.

  • 인터페이스의 변경

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

    A가 C 오브젝트를 사용하려 한다고 해보자. A는 원래 B 인터페이스를 사용하도록 만들어져 있고 C는 B인터페이스를 구현하지 않았다. 이때 A가 DI를 통해 B의 구현 오브젝트를 받도록 만들어져 있다면 B 인터페이스를 구현하면서 내부에서 C를 호출해주는 기능을 가진 어댑터 오브젝트를 만들어 A에게 DI해주면 된다.

    A는 DI덕분에 자신의 코드를 수정하지 않아도 된다. 디자인 패턴에서 말하는 오브젝트 방식의 어댑터 패턴의 응용이라고 볼 수 있다.

    이를 일반화해서 아예 인터페이스가 다른 다양한 구현을 같은 방식으로 사용하도록, 중간에 인터페이스 어댑터 역할을 해주는 레이어를 하나 추가하는 방법도 있다. DI의 응용 방법 중 하나이자 스프링의 대표적인 기술로도 분류되는 일관성 있는 서비스 추상화(portable service abstraction)이다. PSA는 클라이언트가 일관성 있게 사용할 수 있는 인터페이스를 정의해주고 DI를 통해 어댑터 역할을 하는 오브젝트를 이용하게해준다. 이를 통해서 다른 인터페이스를 가진 로우레벨의 기술을 변경하거나 확장해가면서 사용할 수 있는 것이다.

  • 프록시

    필요한 시점에서 실제 사용할 오브젝트를 초기화하고 리소스를 준비하게 해주는 지연된 로딩(lazy loading)을 적용하려면 프록시가 필요하다. 원격 오브젝트를 호출할 때 마치 로컬 오브젝트처럼 사용할 수 있게 해주는 원격 프록시를 적용하려고 할 때도 프록시가 필요하다. 두 가지 방법 모두 DI를 필요로 한다.

  • 템플릿과 콜백
  • 싱글톤과 오브젝트 스코프

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

    싱글톤은 가장 기본이 되는 스코프이다. 하나 또는 소수의 오브젝트가 수많은 클라이언트를 상대로 고성능 서비스를 제공하는 방식은 엔터프라이즈 개발에서 매우 중요하다. 상태를 갖지 않는 오브젝트가 동시에 여러 스레드의 요청을 처리하는 방식을 적용하려면, 만들어지는 오브젝트의 개수를 제어하는 일은 매우 중요하다.

    HTTP 요청당 하나, 혹은 세션당 하나의 오브젝트를 만들 수도 있다. 개발자 임의의 스코프를 갖는 오브젝트를 만들고 DI에 적용하는 것도 가능하다.

    오브젝트 스코프가 제어 가능한 이유도 DI덕분에 가능하다.

  • 테스트

    협력하여 동작하는 오브젝트를 효과적으로 테스트하기 위해 가능한 고립시켜야 한다. 즉 다른 오브젝트와의 사이에서 일어나는 일을 테스트를 위해 조작할 수 있도록 만든다. 테스트할 대상이 의존하는 오브젝트를 목 오브젝트로 대체하여 가볍게 테스트할 수 있게 한다.

    DI를 위해 만든 수정자 메소드를 이용하여 테스트 코드 안에서 수동으로 목 오브젝트를 주입할 수 있다. 또는 테스트용 설정을 별도로 만들어도 된다.

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

객체지향 기술은 복잡한 애플리케이션의 요구조건과 기술적 난해함을 모두 해결하는데 한계가 있다. AOP는 객체지향 기술의 한계와 단점을 극복하도록 도와주는 보조적인 프로그래밍 기술이다. AOP로 인해 OOP는 더욱 OOP답게 될 수 있다. POJO만으로 엔터프라이즈 애플리케이션을 개발하면서 동시에 엔터프라이즈 서비스를 선언적으로 사용할 때 반드시 AOP가 필요하다.

일부 서비스는 순수한 객체지향 기법만으로는 POJO의 조건을 유지한 채 적용하기 힘들다.

AOP의 적용기법

AOP를 자바에서 적용하는 방법은 두 가지가 있다.

  • 스프링과 같이 다이내믹 프록시 사용한다.

    기존의 코드에 영향을 주지 않고 부가기능을 적용하는 데코레이터 패턴을 응용한다. 자바의 객체지향 패턴을 활용하는 방법. 따라서 부가기능의 부여할 수 있는 곳이 메소드 호출이 일어나는 지점이라는 제약이 있다. 이는 인터페이스와 DI를 활용하는 데코레이터 패턴이 기반원리이기 때문임

  • 자바 언어의 한계를 넘어서는 언어의 확장을 이용한다.

    AspectJ라는 오픈소스 AOP 툴을 활용한다. AspectJ는 프록시 AOP에서 불가능한 다양한 곳에서 조인포인트를 제공한다. 메소드, 인스턴스 생성, 필드 액세스, 특정 호출 경로를 가진 메소드 호출 등에도 부가기능을 제공한다.

    JDK와 자바만으로 구현이 불가능하다. AOP 컴파이러나 클래스 메모리 로드시 바이트 코드를 조작하는 위빙 같은 기법이 필요하다.

AOP의 적용단계

AOP는 자바의 개발 방법과 상당히 다르기 때문에 제대로 적용하려면 충분한 시간과 노력이 필요함. 남발시 심각한 문제가 발생할 위험이 있다. 다른 개발자가 만든 코드가 예상하지 않은 방식으로 돌아가능 등의 혼란을 초래할 수 있다. AOP는 하나의 모듈이 수많은 오브젝트에 보이지 않게 적용되기 때문에 매우 주의해서 사용해야 한다.

  • AOP 적용 1단계: 미리 준비된 AOP 이용

    처음에는 스프링이 미리 만들어서 제공하는 AOP 기능을 그대로 가져다가 적용한다. 대표적인 AOP인 트랜잭션을 이용해 DB를 사용하는 애플리케이션에 적용해보자. AOP 설정을 통해 트랜잭션이 어떻게 많은 오브젝트에 투명하게 적용되는지 관찰하고, AOP의 특성과 동작원리를 이해하자.

    특정 아키텍처를 사용했을 때 사용하는 AOP 애노테이션 @Configurable 을 이용한다. 도메인 오브젝트에 DI를 자동적용해주는 AOP기능이다. 도메인 오브젝트를 전용 계층에 두고 접근하는 아키텍처 방식을 따를 때 반드시 필요하다. @Configurable 는 AspectJ를 이용한 AOP가 반드시 필요하다.

  • AOP 적용 2단계: 전담팀을 통한 정책 AOP 적용

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

    이런 기능을 개발자가 직접 자신의 코드에 추가하려면, 개발 표준이나 가이드라인이 미리 완벽하게 준비되어 있어서 이에 따라 개발하게 해야 한다. 하지만 개발자가 빼먹거나 가이드라인을 따르지 못하고 엉뚱하게 적용할 수도 있다. 개발 정책이나 기준이 바뀌면 모든 개발자 지금까지 작업한 것을 모두 수정하고 일일이 검증해야 하는 큰 부담을 지게 된다. 하지만 AOP를 이용해 한 번에 적용한다면 일반 개발자의 작업에는 전혀 영향을 주지 않을 수 있다.

    AOP는 언제든지 기능을 기존 코드에 영향을 주지 않으면서 추가하거나 제거할 수 있다. 이런 특징을 잘 이용하면 운영 중에 필요한 기능 외에 개발 가이드라인이나 표준을 따라서 코드가 작성되어 있는지를 검증하는 작업을 AOP를 이용해 할 수 있다.

    AOP는 동적으로 동작하면서 개발 정책을 위반한 코드를 잡아내는 데도 유용하다. AOP를 이용해 메소드 호출에 적용되는 AOP 모듈을 만든다. 메소드 호출이 일어났을 때 어드바이스를 통해 호출 경로를 추적할 수 있다. 허용된 서비스 계층이 아닌 경우 정책위반 예외를 던질 수 있다. 서비스 계층의 메소드에 대해 예외가 던져졌을 때만 동작하는 AOP 모듈을 만들 수 있다. 예외의 종류를 검사해 허용된 예외가 아니면 정책위반 예외를 던질 수 있다.

    개발이 끝나고 실전에 적용할 때는 정책 검증을 위한 AOP 설정을 간단히 제거하면 된다.

  • AOP 적용 3단계: AOP의 자유로운 이용

    1, 2단계를 거쳐 AOP에 익숙해지고, 장단점, 응용 전략, 위험성을 이해했다면 개발자가 스스로 AOP를 활용할 단계로 넘어갈 수 있다. 애플리케이션 전체적인 정책위주의 AOP를 넘어서 개발자가 직접 구현하는 기능에 적용하여 세부적인 AOP를 이용할 수 있다. 큰 범위에 걸쳐 적용되는 기능은 아니지만 한 모듈 또는 특정 기능 안에서 AOP로 분리하면 유용한 것들이 있다.

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

세부 기술의 변환에 관계없이 일관된 방식으로 기술에 접근할 수 있게 해주는 PSA(Portable Service Abstraction)다. POJO로 개발된 코드는 특정 환경이나 구현 방식에 종속적이지 않아야 한다. POJO 코드는 JavaEE 기술을 사용하지만 코드에 직접 노출되어 만들어지지 않아야 한다. 이를 위해 스프링은 일관성 있는 서비스 추상화를 제공해 기술의 변화에 유연한 대응할 수 있게 해준다.

스프링의 서비스 추상화의 개념과 장점을 잘 이해한다면 직접 서비스 추상화 기법을 적용할 필요도 있다. 직접 추상화 레이어를 도입해 일관성 있는 API를 정의해 사용한다.

서비스 추상화에 사용되는 기술은 DI 뿐이다. DI를 적극적으로 활용한다면 서비스 추상화는 자연스럽게 만들어 쓸 수 있다.

서비스 추상화를 이용해 테스트가 어려운 API나 설정을 통해 주요 기능을 외부에서 제어하게 만들고 싶을 때도 이용할 수 있다.

출처

토비의 스프링(책)

태그:

카테고리:

업데이트:

댓글남기기