[Spring] Spring의 3대 요소(IoC, PSA, AOP)

yee·2024년 9월 28일

Spring

목록 보기
2/6
post-thumbnail

🎈 Spring 의 3대 요소

1) IoC (Inversion of Control)
2) PSA (Portable Service Abstraction)
3) AOP (Aspect Oriented Programming)

🎈 IoC (Inversion of Control, 제어의 역전)

✨ IoC

개념

: 객체 셍성 및 의존 관계 관리를 개발자가 아닌 Spring 컨테이너에 위임하는 소프트웨어 디자인 원칙

컴포넌트 의존관계 설정(Component dependency resolution), 설정(Configuration), 생명주기(LifeCycle)을 해결하기 위한 디자인 패턴

장점

1) 낮은 결합도 : 객체 간의 의존성을 낮춰 코드의 유연성과 재사용성을 높인다
2) 높은 응집도 : 객체는 자신의 핵심 기능에만 집중하고, 의존성 관리는 컨테이너에 위임하여 코드의 가독성과 유지 보수성을 향상시킴
3) 테스트 용이성 : 컨테이너를 통해 의존성을 쉽게 교체하거나 목 객체(Mock Object)를 주입하여 단위 테스트를 효과적으로 수행 가능

스프링에서의 활용

1) XML or Annotation 기반 설정 : 개발자느 XML 설정 파일이나 어노테이션을 통해 객체의 의존 관계를 설정
2) IoC 컨테이너 : Spring 컨테이너는 설정 정보를 기반으로 객체를 생성하고, 의존성을 자동으로 주입
3) 런타임 의존성 주입 : 애플리케이션 실행 시점에 컨테이너가 동적으로 의존성을 주입, 코드 변경 없이 객체 간의 관계를 변경 가능

✨ IoC 컨테이너 ( = 스프링 컨테이너 )

: 객체를 생성하고 의존성을 관리하고 책임지는 컨테이너

  • 인스턴스 생성부터 소멸까지 인스턴스의 생명 주기를 개발자가 아닌 이 컨테이너가 대신 해준다.
    => 그로 인해 객체 관리 주체가 개발자가 아닌 스프링 프레임워크(제어의 반전)가 되기 때문에 개발자는 로직에만 더 집중할 수 있게 된다.

✨ IoC와 싱글톤(Singleton) 패턴의 관계

싱글톤 패턴 이란?

  • 개념 : 애플리케이션 전체에서 특정 클래스의 인스턴스가 단 하나만 생성되도록 보장하는 디자인 패턴
  • 목적 : 메모리 사용량을 줄이고, 객체 생성 비용을 절감하며, 전역적인 접근을 가능하게 한다.


    <핵심 구성 요소>
  • private 생성자 : 외부에서 객체를 직접 생성하지 못하도록 제한
  • 정적 인스턴스 변수 : 클래스 내부에 유일한 인스턴스 저장
  • 정적 메서드 : 외부에서 유일한 인스턴스를 얻을 수 있도록 제공

IoC와 싱글톤 패턴의 관계

Spring Framework는 IoC 컨테이너를 통해 싱글톤 패턴을 효과적으로 지원하며, 개발자가 더욱 효율적이고 유지 보수하기 쉬운 애플리케이션을 개발할 수 있도록 돕는다.

  • IoC : 객체 간의 결합도를 낮추고 유연성을 높이는 디자인 원칙
  • 싱글톤 패턴 : 특정 클래스의 인스턴스를 하나만 생성하여 메모리 사용량을 줄이고 전역적인 접근을 가능하게 하는 디자인 패턴
    • Spring Framework는 IoC 컨테이너를 통해 싱글톤 패턴을 기본적으로 지원합니다. 즉, 특별한 설정 없이 빈(Bean)을 등록하면 싱글톤으로 관리.
    • 싱글톤 패턴의 단점 보완: IoC 컨테이너는 싱글톤 패턴의 단점(테스트 어려움, 전역 상태 관리 어려움 등)을 보완하여 객체의 생명주기와 의존성을 효과적으로 관리.
    • 유연한 객체 생성: IoC 컨테이너는 필요에 따라 싱글톤 외에도 다양한 스코프(prototype, request, session 등)의 빈을 생성하고 관리.

✨ IoC의 분류 : DL(Dependency LookUp) & DI(Dependency Injection)

DL(Dependency LookUp)과 DI(Dependency Injection)이 있지만 DL은 컨테이너의 종속성이 증가하기 때문에 잘 쓰이지 않는다.

✨ DI (Dependency Injection) 란?

객체를 직접 생성하는 게 아닌 외부(IoC 컨테이너)에서 생성한 후 주입 시켜주는 방식

  • 의존성 주입 방법 : IoC 컨테이너는 스프링 컨테이너에 객체들을 Bean으로 등록하여 관리
    ex) 클래스 선언부에 @Component 사용

@Component

@Controller, @Service, @Repository는 모두 @Component를 포함하고 있다.

의존성 주입 방식 3가지

1) 생성자 주입 (Constructor Injection)
2) 수정자 주입 (Setter Injection)
3) 필드 주입 (Field Injection)

1) 생성자 주입 (Constructor Injection)

: 생성자 주입이란 생성자를 통해 의존관계를 주입하는 방법으로 생성자를 호출 시에 딱 한 번만 호출되는 것을 보장한다.

  • Spring에서 권장하는 방법이다
  • 스프링 4.3 버전 이후부터는 @Autowired 를 생략해도 주입이 되며, 주입받는 필드에 final 키워드를 사용함으로써 주입되어야 하는 것을 보장한다
@Controller
public class TestController {
	private final TestService testService;
    public TestController(TestService testService){
    	this.testService = testService;
    }
}

2) 수정자 주입 (Setter Injection)

: Setter 메소드를 만들고 이를 통해 의존성 주입

  • 수정자 주입의 경우 final 키워드 선언이 불가능하며, setter 메서드에 @Autowired를 붙여 사용
  @Controller
public class TestController {
	private TestService testService;
    @Autowired
    public void setTestService(TestService testService){
    	this.testService = testService;
    }
}

3) 필드 주입 (Field Injection)

필드 주입이란 필드에 직접 의존관계를 주입하는 방법

  • 이는 코드가 짧아지는 장점이 있지만 외부에서 변경이 불가능하고 테스트 코드를 작성하기 힘들다
  • 편리하긴 하나 SOLID중 SRP위반, 테스트가 불편함, final선언 불가로 인한 불변성 보장이 되지 않음, 순환 의존성 등의 문제로 인해 권장되지 않는 방법이다.
  @Controller
public class TestController {
	@Autowired
	private TestService testService;
}

출처: https://mozzi-devlog.tistory.com/18 [우당탕탕:티스토리]

✨ 스프링 빈(Bean)

: Bean은 애플리케이션의 핵심 구성 요소로, 스프링 컨테이너가 생성, 관리, 제거하는 객체


개발자는 bean을 사용함으로써 객체의 생명주기 관리를 스프링 컨테이너에 위임할 수 있고, 이는 애플리케이션의 설정과 의존성 관리를 보다 쉽게 만들어준다.

스코프(Scope)

스프링 빈은 다양한 스코프(Scope)를 가질 수 있다. 스코프는 bean이 존재할 수 있는 범위를 정의하며, 대표적으로 싱글톤(Singleton), 프로토타입(Prototype), 요청(Request), 세션(Session) 등이 있다.

1) 싱글톤(Singleton)

  • 생성 방식 : 스프링 컨테이너가 시작될 때 빈의 인스턴스를 1개만 생성하고, 애플리케이션 전체에서 해당 인스턴스를 공유
  • 특징
    - 가장 기본적인 스코프이며, 명시적인 설정이 없을 경우 빈은 싱글톤으로 생성된다.
    • 메모리 사용량을 줄이고 성능을 향상시킬 수 있다.
      - 상태 정보를 저장하는 빈에는 적합하지 x ( 동시성 문제 발생 가능)

2) 프로토타입(Prototype)

  • 생성 방식 : 빈을 요청할 때마다 새로운 인스턴스 생성
  • 특징
    - 각 요청마다 독립적인 인스턴스를 사용할 때 유용
    • 상태 정보를 저장하는 빈에 적합
    • 싱글톤에 비해 메모리 사용량이 증가할 수 o

3) 요청(Request)

  • 생성 방식 : HTTP 요청 당 하나의 인스턴스를 생성하고, 해당 요청 처리가 끝나면 소멸
  • 특징
    - 웹 애플리케이션에서 각 요청마다 독립적인 데이터를 처리해야 할 때 유용
    • 주로 컨트롤러나 폼 객체에서 사용

4) 세션(Session)

  • 생성 방식 : HTTP 세션 당 하나의 인스턴스를 생성하고, 해당 요청 처리가 끝나면 소멸
  • 특징
    - 사용자 세션과 관련된 데이터를 저장하고 관리해야할 때 유용
    • 주로 사용자 정보, 장바구니 등에 사용

5) 애플리케이션(Application)

  • 생성 방식 : ServletContext 당 1개의 인스턴스를 생성하고, 애플리케이션 전체에서 공유
  • 특징
    - 사용자 세션과 관련된 데이터를 저장하고 관리해야할 때 유용
    • 주로 사용자 정보, 장바구니 등에 사용

6) 웹소켓(WebSocket)

  • 생성 방식 : WebSocket 당 1개의 인스턴스를 생성하고, 연결이 종료되면 소멸
  • 특징
    - 사용자 세션과 관련된 데이터를 저장하고 관리해야할 때 유용
    • 주로 사용자 정보, 장바구니 등에 사용

설정 방법

  • 어노테이션: @Scope 어노테이션을 사용하여 빈의 스코프를 지정할 수 있습니다. (예: @Scope("prototype"))
  • XML 설정: 태그의 scope 속성을 사용하여 빈의 스코프를 지정할 수 있습니다. (예: )

✨ IoC 컨테이너 분류

BeanFactory

  • Bean 등록, 생성, 조회, 반환에 대한 관리
  • 팩토리 디자인 패턴을 구현한 것으로 BeanFactory는 빈을 생성하고 분배하는 책임을 가진다.
    cf ) 팩토리 패턴 : 객체 생성을 캡슐화하여 객체 생성 과정을 유연하게 관리하고, 클라이언트 코드와 구체적인 객체 생성 로직을 분리하는 디자인 패턴

Application Context

  • BeanFactory의 모든 기능을 포함하며, 추가적으로 위에서 설명한 다양한 기능들을 제공
  • 일반적으로 Spring 기반 애플리케이션 개발에서는 Application Context를 사용

    Application Context의 주요 기능

    1) 빈(Bean) 관리
    : @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스들을 빈으로 등록하고 관리

    2) 메세지 국제화
    : 애플리케이션에서 사용되는 메시지를 다국어로 지원할 수 있도록 도와줌
    3) 이벤트 처리
    : 애플리케이션 내에서 발생하는 이벤트를 처리하고 관리할 수 있는 기능을 제공
    4) 리소스 관리
    : 파일, 이미지, 설정 파일 등 애플리케이션에서 사용되는 다양한 리소스를 로드하고 관리
    5) 환경 추상화
    : 애플리케이션이 실행되는 환경(개발, 테스트, 운영 등)에 따라 다른 설정을 적용할 수 있도록 지원

🎈 PSA (Portable Service Abstraction, 서비스 추상화)

환경의 변화와 관계없이 일관된 방식의 기술로의 접근 환경을 제공하는 추상화 구조

  • 다양한 환경에서도 동일한 방식으로 사용 가능
    :PSA를 통해 개발자는 특정 기술의 세부적인 구현 방식에 대해 깊이 알 필요 없이, 일관된 API나 인터페이스를 통해 기능을 활용
  • 기술 변경에 대한 영향 최소화
    : 시스템 내부에서 사용되는 기술이 변경되더라도, PSA 덕분에 상위 계층의 코드는 변경할 필요가 거의 없어 유지보수가 용이
  • 개발 생산성 향상
    : PSA는 개발자에게 익숙한 추상화 계층을 제공하여, 새로운 기술을 학습하는 데 드는 시간을 줄이고 개발 생산성을 높임

✨ PSA 예시

출처 : https://velog.io/@kimsunfang/Spring%EC%9D%98-3%EB%8C%80-%EC%9A%94%EC%86%8C

in Spring Web MVC

Spring Web MVC는 Spring 프레임워크에서 제공하는 웹 애플리케이션 개발을 위한 모듈

  • 기존 Servlet 방식의 복잡성을 해소하고, 잘 만들어진 인터페이스들, 의존성같은 기술들을 기반으로 하여 Spring은 개발자에게 편리한 환경을 제공한다.

    Ex 1) @Controller, @GetMapping같은 기능들의 뒷단의 복잡한 코드에 대해서 깊게 고려하지 않아도 되고 이런 기술들을 기반으로 하여 기존 코드를 거의 변경하지 않아도 된다.
    Ex 2) 실행 서버를 Tomcat이 아닌 netty로 변경하고 싶다면 프로젝트 설정에 spring-boot-starter-web 의존성 대신 spring-boot-starter-webflux 의존성을 받도록 하면 된다.

in Spring Transaction

JDBC는 DB에 접근하고 작업, 트랜잭션을 구현할 때는 연결, 작업할 쿼리문, commit, rollback하는 코드를 전부 작성해야 했다.
=> 그러나 Spring에선 메소드에 @Transactional을 통해 트랜잭션 처리를 간단하게 할 수 있다.

🎈 AOP (Aspect Oriented Programming, 관점 지향 프로그래밍)

AOP는 횡단 관심사를 효과적으로 관리하여 OOP의 한계를 극복하고, 더욱 객체 지향적인 개발을 가능하게 하는 강력한 도구

  • OOP의 한계 : OOP는 코드 재사용성과 유지 보수성을 높이는 데 효과적이지만, 트랜잭션 처리, 보안 등 여러 모듈에 걸쳐 반복적으로 나타나는 공통 기능(횡단 관심사, Cross-Cutting Concern) 을 효과적으로 관리하기 어렵다. 이러한 공통 기능은 각 모듈에 직접 구현하면 코드 중복, 복잡성 증가, 핵심 로직과의 혼재 등의 문제가 발생
  • AOP의 해결 : AOP는 이러한 문제를 해결하기 위해 등장. AOP는 부가 기능을 Aspect(관점)로 정의하여, 핵심 기능에서 부가 기능을 분리함으로써 핵심 기능을 설계하고 구현할 때 객체지향적인 가치를 지킬 수 있게 도와주는 개념

    AOP는 기능을 핵심 관심 사항(Core Concern)과 공통 관심 사항(흩어져 있는 관심사,Cross Cutting Concern)으로 분리시키고 각각을 모듈화 하는 것을 의미

    Core Concern

    : 핵심 관심사, 비즈니스 로직과 같이 애플리케이션의 핵심 기능을 담당하는 부분

    Cross-Cutting Concern

    : Core Concern을 실행하기 위한 DB연결, 로깅, 파일 입출력 등

    Target

    : 부가 기능을 부여할 대상(핵심 기능을 담고 있는 모듈)

    Aspect (관점)

    : 횡단 관심사를 모듈화한 단위

    • Advice와 PointCut을 함꼐 가지고 있다.
      1) Advice(공통 기능 코드)
      2) JointPoint
      : Advice가 적용될 수 있는 위치
      3) Pointcut
      : Advice를 적용할 JoinPoint를 선별하는 기능을 정의한 모듈
      4) Proxy
      : Traget을 감싸서 Target에 대한 요청을 대신 받아주는 Wrapping Object
    • 클라이언트에서 Target을 호출하면 실제 타켓이 아닌 Wrapper Object(=Proxy)가 호출되어 타겟 메소드 실행 전에 선처리, 후처리 실행
      5) Weaving
      : 핵심 기능 코드에 Aspect를 삽입하는 과정. 컴파일 시점, 클래스 로딩 시점, 런타임 시점에 Weaving이 가능

✨ AOP와 프록시(Proxy) 패턴의 관계

프록시 패턴 이란?

  • 개념 : 다른 객체에 대한 접근을 제어하는 Wrapper객체를 만드는데 사용되는 디자인 패턴
  • 목적 : 어플리케이션 코드에서 Cross Cutting Concern을 분리할 수 있어 모듈화 및 유지보수가 보다 용이, 접근 제어 및 부가기능을 추가하기 위함

<특징>

1) Spring은 타겟 객체에 대한 프록시를 만들어 제공
2) Target의 Wrapper 프록시는 RunTime에 생성된다.
3) 프록시는 Advice를 Target 객체에 적용하면서 생성되는 객체

호출 전 : 프록시는 호출을 Intercept 한다

  • 프록시는 Target 객체에 대한 호출을 가로챈 다음 Advice의 부가 기능 로직을 수행하고 난 후에 Target의 핵심 기능 로직을 호출

호출 후 : 메소드는 JoinPoint만 지원한다.

  • Target(핵심 기능)의 메소드가 호출되는 런타임 시점에만 Advice(부가 기능)을 적용할 수 있다.

✨ AOP 사용 예시

출처 : https://velog.io/@kimsunfang/Spring%EC%9D%98-3%EB%8C%80-%EC%9A%94%EC%86%8C

@ExceptionHandler

  @ControllerAdvice 
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)
    public String handleException(Exception ex) {
        // log the exception
        // return a view or redirect to an error page
    }
}

각각의 메소드에서 처리해야 했던 예외들(Cross-Cutting Concern)을 GlobalExceptionHandler 클래스 한 곳에 모아 모듈화하여 어플리케이션의 비즈니스 로직(Core Concern)과 분리시켜 결합도를 낮춘다.
( @ExceptionHandler를 사용하여 동일한 클래스, @ControllerAdvice가 있는 클래스들의 메소드가 던진 예외를 처리 )

@Transactional

@Service
public class MyService {
    @Transactional
    public void saveData(Data data) {
        // insert data into the database
    }
}

트랜잭션 로직(Cross Cutting Concern)을 비지니스 로직(Core Concern)으로부터 분리하여 모듈화하고 유지보수관리가 용이하게 만들었다.

<과정>
1. saveData메소드는 @Transactional로 인해 반드시 트랜잭션에 포함되어야 한다는 것을 알 수 있다.
2. 해당 메소드가 호출되면 Spring은 @Service를 통해 빈을 생성하고 해당 빈에 대한 프록시 객체를 생성한다.
3. 해당 프록시 객체에는 트랜잭션 관리 Advice가 포함된다.
4. 해당 Advice는 메소드 호출 전, 후 호출되고 트랜잭션이 성공하면 commit, 예외가 발생하면 rollback하게 된다.




📃출처 및 참고자료📃

0개의 댓글