스프링

토비의 스프링 | 8장 스프링이란 무엇인가

그zi운아이 2024. 1. 10. 17:48

스프링의 정의

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

 

애플리케이션 프레임워크

  • 범용성: 스프링은 특정 계층(예: MVC, ORM)에 국한되지 않고, 애플리케이션의 전 영역을 아우르는 범용적인 프레임워크입니다. 웹 개발, 데이터 접근, 보안, 트랜잭션 관리 등 다양한 영역에서 활용될 수 있으며, 개발자는 필요에 따라 선택적으로 기능을 사용할 수 있습니다.
  • IoC(Inversion of Control, 제어의 역전): IoC는 스프링의 핵심 원리입니다. 기존의 프로그래밍에서 객체의 생성과 생명 주기를 개발자가 관리하는 것과 달리, 스프링에서는 프레임워크가 이러한 제어를 담당합니다. 이는 객체의 생성과 의존성 주입을 스프링 컨테이너가 관리하게 함으로써 개발자가 비즈니스 로직에 집중할 수 있도록 도와주고, 코드의 재사용성 및 테스트 용이성을 향상시킵니다.

경량급

  • 경량급의 진정한 의미:  방대한 기능과 오랜 역사를 가진 프레임워크임에도 불구하고, 필요한 기능만을 선택하여 사용할 수 있는 유연성을 제공합니다. 불필요하게 무거운 리소스를 필요로 하지 않으며, 가벼운 서버 환경에서도 효과적으로 작동합니다.
  • EJB와의 비교: EJB는 무겁고 느린 WAS(웹 애플리케이션 서버)가 필요했던 반면, 스프링은 톰캣과 같은 가벼운 서버에서도 효과적으로 작동합니다. 이는 스프링이 더 유연하고 가볍게 설계되었으며, 빠른 시작과 성능을 제공한다는 것을 의미합니다.

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

  • 엔터프라이즈 수준의 기능 제공: 스프링은 데이터베이스 연결, 트랜잭션 관리, 보안 등 엔터프라이즈 개발에 필수적인 기능들을 쉽고 편리하게 구현할 수 있도록 지원합니다.
  • 개발 복잡성 감소: EJB와 같은 이전 기술에 비해 스프링은 개발자가 비즈니스 로직에 더 집중할 수 있도록 개발의 복잡성을 줄여줍니다. 스프링은 기술적인 세부사항을 추상화하고, 개발자가 엔터프라이즈 기술을 보다 쉽게 다룰 수 있게 만듭니다.

오픈소스

  • 자유롭게 사용 가능: 스프링은 Apache 2.0 라이선스 하에 배포됩니다. 이 라이선스는 스프링 소프트웨어의 사용, 복사, 배포, 수정을 허용합니다. 스프링 소프트웨어에 대한 비상업적 재배포나 호환되는 도구 개발 등에는 스프링 상표를 사용할 수 있지만, 스프링 소프트웨어의 상업적 재배포에는 사용할 수 없습니다
  • 커뮤니티 지원 및 발전: 강력한 오픈소스 커뮤니티는 스프링의 지속적인 개선과 확장을 지원합니다. 개발자들은 소스 코드를 직접 검토하고, 필요에 따라 수정할 수 있으며, 이는 프레임워크의 범용성과 유연성을 증가시킵니다.

스프링 프레임워크의 목적

스프링 프레임워크의 주요 목적은 엔터프라이즈 애플리케이션 개발의 복잡성을 줄이는 것입니다. 이는 개발자가 더 효율적으로 작업할 수 있게 하며, 애플리케이션의 유지보수 및 확장성을 향상시킵니다.

 

 기술적 제약 조건과 요구사항의 증가

  • 통합 및 보안: 엔터프라이즈 시스템은 다양한 서비스와 애플리케이션을 통합해야 하며, 높은 수준의 보안 요구사항을 충족시켜야 합니다.
  • 트랜잭션 관리: 복잡한 트랜잭션 관리와 데이터 일관성 유지는 중요한 도전 과제입니다.
  • 성능과 확장성: 시스템은 높은 성능을 유지하면서도, 필요에 따라 확장 가능해야 합니다.

 비즈니스 로직의 복잡성 증가

  • 다양한 비즈니스 요구사항: 엔터프라이즈 애플리케이션은 점차 복잡해지는 비즈니스 요구사항을 효과적으로 처리할 수 있어야 합니다.
  • 유연성과 재사용성: 애플리케이션은 변경 가능한 비즈니스 환경에 빠르게 적응하고, 코드의 재사용성을 높여야 합니다.

복잡함을 해결하려는 도전

엔터프라이즈 개발의 복잡함은 근본적으로 제거 대상이 아니며, 비즈니스 로직의 복잡함을 효과적으로 다루기 위해 기술적인 복잡함을 효율적으로 처리하는 것이 중요합니다. 엔터프라이즈 개발에서 비즈니스 로직과 기술적인 로직은 서로 분리되어야 합니다. 이러한 분리는 시스템의 유연성과 확장성을 증대시키며, 개발 및 유지보수의 효율성을 높입니다.

실패한 해결책: EJB

EJB는 기술적인 복잡함을 줄이려는 시도였지만, 실제로는 더 큰 복잡함을 초래했습니다. 특정 클래스에 종속되고 상속을 요구하는 등의 제약이 많았으며, 이러한 제약은 개발 프로세스를 더 복잡하고 무겁게 만들었습니다. EJB는 특정 환경에 종속되는 코드를 작성하도록 강제하여, 자바의 강점을 활용하지 못하게 했습니다.

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

스프링은 비침투적인 접근 방식을 채택하여 이러한 문제를 해결했습니다. 스프링은 개발자가 순수한 자바 객체(POJO)를 사용하여 비즈니스 로직을 구현할 수 있게 하며, 프레임워크 자체가 애플리케이션 코드에 직접적으로 나타나지 않도록 합니다. 이 접근 방식은 개발자가 플랫폼이나 환경의 제약에서 벗어나, 비즈니스 로직에 집중할 수 있게 도와주며, 애플리케이션의 확장성과 유지보수성을 개선합니다.

핵심도구: 객체지향과 DI

스프링은 "기본으로 돌아가자"라는 모토를 갖고 있으며, 이는 자바의 객체지향적 특성을 최대한 활용하는 것을 의미합니다. 객체지향 프로그래밍은 코드 재사용, 유지보수의 용이성, 테스트 용이성 등 여러 장점을 제공합니다. 스프링은 이러한 객체지향적 특성을 살리면서도, 복잡한 의존성 관리를 용이하게 하는 DI를 통해 개발자가 비즈니스 로직에 더 집중할 수 있도록 합니다.

DI는 객체 간의 의존성을 외부에서 주입해주는 방식으로, 스프링 컨테이너가 객체의 생성과 의존성 관리를 담당합니다. 이를 통해 개발자는 객체의 생성과 의존성 해결에 신경 쓰지 않고, 더 깨끗하고 유지보수가 쉬운 코드를 작성할 수 있습니다.


스프링의 핵심: POJO

POJO란 무엇인가?

  • POJO(Plain Old Java Object)는 자바 언어의 간단한 오브젝트를 뜻합니다. 스프링에서는 POJO를 사용하여 애플리케이션의 비즈니스 로직을 구현합니다. 이는 개발자가 복잡한 프레임워크나 서버 환경에 종속되지 않고 순수한 자바 언어의 기능을 활용하여 애플리케이션을 개발할 수 있게 하는 중요한 개념입니다.

POJO의 조건

  • 특정 규약에 종속되지 않음: POJO는 특정 규약, 클래스 상속 또는 인터페이스 구현에 의존하지 않습니다. EJB와 같이 규약을 따라야 하는 컴포넌트가 아닌, 자바의 단일 상속 제한에 영향을 받지 않으므로, 객체지향적 설계 기법을 쉽게 적용할 수 있습니다.
  • 특정 환경에 종속되지 않음: POJO는 특정 서버나 OS에 특화된 API를 사용하지 않으며, 환경에 독립적입니다. 예를 들어, 웹 환경에 특화된 클래스나 인터페이스를 직접적으로 사용하지 않습니다.
  • 객체지향적 원리에 충실함: POJO는 단순히 규약이나 환경에 종속되지 않는 것을 넘어, 객체지향적 원리에 충실해야 합니다. 이는 강한 결합을 피하고, 상속, 다형성, 위임 등의 객체지향 디자인 패턴을 적절히 활용하는 것을 의미합니다.

POJO의 장점

  • 로우레벨 기술과 환경의 독립성: POJO는 특정 기술이나 환경에 종속되지 않아, 코드가 더 단순하고 이해하기 쉽습니다. 이로 인해 검증, 테스트 작성, 유지보수, 오류 디버깅이 용이해집니다.
  • 자동화된 테스트의 용이성: POJO 기반 코드는 자동화된 테스트에 유리합니다. 코드가 기술적 복잡성에서 자유로워져서, 원하는 레벨에서 코드를 빠르고 명확하게 테스트할 수 있습니다.
  • 객체지향적 설계의 자유로운 적용: POJO는 자바와 객체지향 프로그래밍의 핵심 원리에 충실하며, 다양한 객체지향적 설계 기법과 디자인 패턴을 적용하기 쉽습니다. 이는 코드의 재사용성과 확장성을 높여줍니다.

POJO 프레임워크 

  • 스프링 프레임워크는 POJO를 기반으로 한 엔터프라이즈 애플리케이션 개발을 용이하게 하는 프레임워크입니다. 하이버네이트와 같은 다른 POJO 프레임워크와는 달리, 스프링은 엔터프라이즈 애플리케이션의 모든 영역과 계층에서 POJO 방식을 구현할 수 있도록 설계되었습니다.
  • 스프링은 비즈니스 로직과 엔터프라이즈 기술의 복잡함을 효과적으로 분리하고 관리할 수 있게 해줍니다. 이러한 접근 방식은 개발자가 객체지향적인 설계 원칙을 활용하여 더 깔끔하고 효율적인 코드를 작성할 수 있도록 지원합니다
  • 스프링을 사용하는 것만으로는 개발의 모든 부담이 줄어들지는 않습니다. 오히려 스프링을 효과적으로 사용하기 위해서는 객체지향 분석과 설계에 대한 깊은 지식이 필요합니다. 이와 함께 자바 언어, JVM 플랫폼, JDK API에 대한 이해도 중요합니다. 또한, 디자인 패턴, 구현 패턴, 리팩토링 기술 등을 습득하는 것이 중요합니다. 스프링은 이런 기술적인 부분보다는 객체지향적인 설계와 개발 원리에 더 집중할 수 있는 기회를 제공합니다.

스프링의 기술

  • 과거에는 스프링을 단순히 편리한 기술을 제공하는 프레임워크로만 생각했었지만, 공부를 하면서 생각이 바뀌었습니다. 이제는 문제를 해결하거나 더 좋은 객체지향 언어를 개발하기 위해서는 기술 영역이 아닌 코드 레벨에서 먼저 고민해야 한다는 것을 깨달았습니다.
  • 스프링의 핵심 기술인 IOC/DI, AOP, PSA는 POJO 기반 개발을 가능하게 합니다. 이들은 복잡한 엔터프라이즈 기술을 단순화하고, 객체지향적인 코드 작성을 용이하게 합니다. 하지만 중요한 것은, 스프링 기술의 사용이 단순히 편리함을 제공하는 것을 넘어, 개발자가 기술적인 부분보다는 코드 수준에서 더 깊이 있는 고민을 할 수 있도록 하는 것입니다.

제어의 역선(IOC) / 의존관계 주임(DI)

IoC는 객체의 생성 및 생명 주기 관리를 프레임워크에 맡깁니다. DI는 이러한 IoC의 한 방법으로, 객체 간의 의존성을 외부에서 주입하여 결합도를 낮추고 유지보수와 확장성을 향상시킵니다.

핵심 기능의 변경

  • DI를 통해 애플리케이션의 핵심 기능을 구현하는 클래스를 유연하게 변경할 수 있습니다.
  • 예시: JDBC를 사용하는 DAO가 JPA로 변경되는 경우, DI를 통해 DAO의 구현체를 변경할 수 있습니다.
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void executeStrategy() {
        strategy.execute();
    }
}

핵심 기능의 동적인 변경

  • 실행 중에 의존 객체의 기능을 동적으로 변경할 수 있습니다.
  • 예시: 사용자의 요청에 따라 데이터 액세스 기술을 JDBC에서 JPA로 변경하는 경우.

부가 기능 추가

  • DI를 통해 기존 클래스의 기능을 변경하지 않고 부가 기능을 추가할 수 있습니다.
  • 예시: 기존 로직에 로깅, 트랜잭션 처리 등의 부가 기능을 추가하는 경우.
public class LoggingUserDao implements UserDao {
    private UserDao userDao;

    public LoggingUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(User user) {
        System.out.println("Adding a user");
        userDao.addUser(user);
    }

}

 

인터페이스의 변경 

  • 인터페이스가 클라이언트와 호환되지 않는 경우, 어댑터를 통해 인터페이스를 적절하게 변경할 수 있습니다.
  • 예시: 기존의 UserDao 인터페이스와 다른 새로운 형태의 DataAccessor 인터페이스를 사용해야 하는 경우.
  •  
public class UserDaoAdapter implements DataAccessor {
    private UserDao userDao;

    public UserDaoAdapter(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save(Object data) {
        User user = (User) data;
        userDao.addUser(user);
    }

}

프록시

  • 프록시 패턴을 사용하여 지연 로딩(lazy loading)을 구현할 수 있습니다. 이 방식은 오브젝트가 실제로 필요한 시점까지 초기화를 지연시키는 것을 의미합니다. 예를 들어, 데이터베이스 연결이나 복잡한 리소스의 로딩을 필요한 순간까지 미룰 수 있습니다.

템플릿과 콜백

  • 반복되는 작업을 템플릿으로 정의하고, 특정 상황에 따라 변경되는 부분을 콜백으로 구현합니다. 이 방식은 코드의 재사용성을 높이고, 변화에 유연하게 대응할 수 있게 해주며 OCP 원칙에 부합하는 구조를 만듭니다.

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

  • 스프링은 객체의 생명주기를 효율적으로 관리합니다. 싱글톤 스코프를 활용하면, 애플리케이션 전체에서 단 하나의 인스턴스만 유지되도록 할 수 있습니다. 이는 메모리 사용 최적화와 함께 일관된 상태 관리를 가능하게 합니다.

테스트

  • DI를 통해 테스트 환경에서 실제 오브젝트 대신 스텁이나 목 오브젝트를 주입할 수 있습니다. 이를 통해 실제 복잡한 환경을 모방하는 테스트를 용이하게 구현할 수 있으며, 테스트의 독립성과 정확성을 향상시킬 수 있습니다.

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

AOP (Aspect-Oriented Programming)는 프로그래밍의 핵심 개념으로, 애플리케이션의 여러 부분에 걸쳐있는 공통적인 관심사(예: 로깅, 보안, 트랜잭션 처리)를 모듈화하는 방법입니다. 이를 통해 비즈니스 로직과 부가적인 기능을 분리하여, 코드의 재사용성과 유지보수성을 향상시킵니다.

 

AOP의 적용 기법

  • 다이나픽 프록시를 사용하는 방법 (스프링 핵심 AOP)
  • 자바의 한계를 넘어서는 언어의 확장을 이용하는 방법 

다이내믹 프록시 사용

  • 스프링 AOP는 다이내믹 프록시를 사용하여 런타임에 객체의 메소드 호출을 가로채고, 추가적인 기능을 실행합니다. 이 방법은 주로 런타임에 객체의 행동을 조작할 때 사용됩니다.

언어 확장 사용

  • 자바의 한계를 넘어서는 언어 확장, 예를 들어 AspectJ와 같은 도구를 사용하여 AOP를 구현할 수 있습니다. 이 방법은 컴파일 타임에 코드를 조작하여 훨씬 더 강력한 AOP 지원을 제공합니다.

AOP의 적용 단계

1단계 - 미리 준비된 AOP 사용:

  • 스프링이 제공하는 AOP 기능(예: 트랜잭션 관리)을 활용합니다. 이 단계는 기존에 정의된 AOP 기능을 적용하는 것으로, 복잡한 설정 없이 편리하게 사용할 수 있습니다.

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

  • 애플리케이션 전체에 걸친 정책을 AOP로 구현합니다. 예를 들어, 보안이나 로깅 같은 공통 기능을 전담 팀이 관리하며, 이러한 정책적인 AOP는 필요에 따라 추가하거나 제거할 수 있습니다.

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

  • 개발자가 AOP를 자유롭게 활용하여 애플리케이션의 특정 부분에 맞춤형 AOP를 적용합니다. 이 단계에서는 더 섬세한 제어와 복잡한 AOP 구현이 가능해지며, 애플리케이션의 특정 요구 사항에 맞게 AOP를 구성할 수 있습니다.

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

  • 포터블 서비스 추상화(Portable Service Abstraction)는 스프링 프레임워크의 중요한 개념 중 하나입니다. 이 개념은 애플리케이션을 개발할 때 환경이나 구체적인 기술 구현에 대한 의존성을 줄이는 것을 목표로 합니다. 이를 통해 개발된 POJO 코드는 특정 환경이나 기술에 직접적으로 노출되지 않으며, 일관된 방식으로 기술에 접근할 수 있습니다.
  • 스프링의 서비스 추상화는 AOP나 템플릿/콜백 패턴과 결합되어 사용될 수 있으며, 개발자는 설정을 통해 사용할 기술의 종류를 지정할 수 있습니다. 
 

스프링 핵심 AOP!

AOP의 목적과 중요성 AOP: 프로그래밍에서 공통적인 관심사를 핵심 비즈니스 로직으로부터 분리하여 모듈화하는 프로그래밍 패러다임입니다. 이는 코드 중복을 줄이고 유지보수를 용이하게 합니

dev-th.tistory.com