BackEnd/Spring

[Spring] 스프링의 3대 요소 (IoC/DI, PSA, AOP)

샤아이인 2022. 7. 22.

Spring의 핵심적인 요소 3가지가 있다.

이에 대하여 잘 알고 있다 생각했는데, 막상 대답해주려니 생각보다 나의 생각을 전달하기가 어려웠다.

 

이번 기회에 3대 요소에 대하여 간단하게나마 정리를 해볼까 한다.

  • IoC/DI
  • PSA
  • AOP

 

1. 공통 프로그래밍 모델

Application을 구성하는 객체(Bean)가 생성되고 동작하는 틀을 제공해줄 뿐만 아니라, 애플리케이션 코드를 어떻게 작성해야 하는지에 대한 기준도 제공한다. 

 

이를 일반적으로 프로그래밍 모델이라고 부르는데, 스프링에서는 크게 3가지 핵심 프로그래밍 모델을 지원한다.

 

1-1) IoC/DI (제어의 역전/ 의존성 주입)

IoC/DI는 객체의 생명주기와 의존 관계를 관리하기 위한 모델이다.

 

▶ 제어의 역전 IoC(Inversion of Control)

기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행했다.

한마디로 구현 객체가 스스로가 프로그램의 제어 흐름을 조종했던 것 이다. 개발자 입장에서는 자연스러운 흐름이라 할 수 있다.

왜냐하면 자기가 생각 한 logic대로 순차적으로 진행이 되기 때문이다.

 

반면에 IoC에서는 구현 객체(Impl)는 자신의 로직을 실행하는 역할만 담당한다.

프로그램의 제어 흐름은 더이상 클라이언트가 아닌, IoC 컨테이너가 통솔하게 된 것 이다.

 

예를 들어서 OrderServiceImpl 은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모른다.

프로그램에 대한 제어 흐름에 대한 권한은 모두 IoC 컨테이너가 가지고 있다.

심지어 OrderServiceImpl 도 IoC 컨테이너가 생성한다.

그리고 IoC 컨테이너는 OrderServiceImpl 이 아닌 OrderService 인터페이스의 다른 구현 객체를 생성하고 실행할 수 도 있다.

 

그런 사실도 모른체 OrderServiceImpl 은 묵묵히 자신의 로직을 실행할 뿐이다.

이렇듯 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리로 인해 관리당하는 것을 제어의 역전(IoC)이라 한다.

 

▶ 제어의 역전 개념을 이용하면 프레임워크 와 라이브러리의 구분 또한 가능하다.

프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크가 맞다. (JUnit)

반면에 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것은 프레임워크가 아니라 라이브러리다.

 

 DI (Dependency Injection)

의존관계 주입에는 2가지가 있다.

 

1) 정적인 클래스 의존 관계

클래스가 사용하는 import 코드만 보고 의존관계를 쉽게 판단할 수 있다.

정적인 의존관계는 애플리케이션을 실행하지 않아도 분석할 수 있다. 다음 코드를 살펴보자.

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}
 

OrderServiceImpl 은 MemberRepository 와 DiscountPolicy 에 의존하게 됨을 알 수 있다.

다만 어떤 구현체가 주입되어 사용될지는 알수가 없다.

 

2) 동적인 객체 의존 관계

애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계다.

출처 - 인프런 스프링 (김영한) 강의

 

애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입(DI)이라 한다.

 

의존관계 주입을 사용하면 정적인 클래스 의존관계를 변경하지 않고, 동적인 객체 인스턴스 의존관계를 쉽게 변경할 수 있다.

 

 IoC 컨테이너, DI 컨테이너

AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해 주는 것을 IoC 컨테이너 또는 DI 컨테이너라 한다.

 

1-2) PSA (서비스 추상화)

스프링을 사용하면 서비스 추상화를 통해 특정 환경이나 서버, 기술에 종속되지 않으며 유연한 애플리케이션을 개발할 수 있다.

스프링에서는 추상화 계층을 통해 구체적인 기술과 환경에 종속되지 않도록 한다.

즉, 추상화 계층을 사용해 어떤 기술을 내부에 숨기고 개발자에게 편의성을 제공하는 것 이다!

 

대표적인 예로 Spring Web MVC 라는 추상화 계층과 Transaction이 있다.

 

Spring에서 Servlet기반의 Application을 만들고 있음에도 불구하고, Servlet 코드는 전혀 보이지 않는다.

단순하게 Controller만 봐도 @GetMapping, @PostMapping등의 에노테이션을 통해 편하게 코드를 작성한다.

 

그 기반에는 Servlet기반으로 코드가 구현되는 것 이다.

이런 추상화 계층을 사용하면 개발자가 좀더 편하게 개발을 할수 있게 된다.

 

Transaction도 마찬가지 이다.

원래는 JDBC를 사용해서 Transaction을 걸때는 다음과 같이 auto commit을 꺼줘야 한다.

dbConnection.setAutoCommit(false);
 ~~
dbConnection.commit()

이후 명시적으로 commit을 호출하면서 Transaction처리를 할 수 있다. 

 

하지만 Spring에서는 @Transactional 에노테이션을 메서드에 추가하면, 알아서 Transaction처리가 된다.

내부적으로 복잡함을 감춘것 이다.

 

1-2) AOP (관점 지향 프로그래밍)

AOP(Aspect Oriented Programming, 관점 지향 프로그래밍)는 애플리케이션에 공통적으로 나타나는 부가적인 기능들을 독립적으로 모듈화하는 프로그래밍 모델이다.

 

애플리케이션의 요구 사항이 점점 복잡해져가고, 기술적인 난해함이 더해지면 모든 책임과 관심사를 분리하는 것이 상당히 어렵다.

그래서 스프링은 AOP를 지원하여 다양한 엔터프라이즈 서비스를 적용하고도 책임을 분리하여 깔끔한 코드를 유지할 수 있고, 객체지향스럽게 개발할 수 있도록 도와준다.

 

예를 들어 비즈니스 로직과 트랜잭션 관리를 위한 로직이 결합된다면 로직이 중복되고, 여러 책임을 갖는 등의 단점이 존재하게 된다. Spring에서는 AOP를 적용하여 @Transactional이라는 선언적 트랜잭션 기능을 구현하여 큰 장점을 누릴 수 있도록 도와준다.

댓글