IoC/DI&AOP&PSA

leekun·2022년 10월 21일
0

스프링(Spring)

목록 보기
4/4

스프링 삼각형

POJO(Plain Old Java Object)

  • 주로 특정 자바 모델이나 기능, 프레임워크를 따르지 않는 Java Object를 지칭한다.
  • Java Bean 객체가 대표적이다.
  • 간단하게 getter/setter를 생각하면 된다.

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


1. IoC(Inversion of Control)

  • 제어의 역전이라 부른다.
  • 스프링 프레임워크에서 제공하는 빈(Java Bean) 관리 컨테이너를 IoC 컨테이너라고 한다.
  • 객체의 생성과 그 객체들의 관리까지 모두 관리한다는 개념, 기존에 자바 기반으로 애플리케이션을 개발할 때 자바 객체를 생성하고 서로간의 의존 관계를 연결시키는 작업에 대한 제어권은 보통 애플리케이션에 있었는데, IoC 컨테이너는 객체의 생성,초기화,서비스 소멸에 관한 모든 권한을 가지면서 객체의 생명주기를 관리한다.

IoC의 분류
*DL 사용시 컨테이너 종속이 증가하기 때문에 주로 DI를 사용한다.

  1. DL(Dependency Lookup) : 저장소에 저장되어 있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 Lookup하는 것
  2. DI(Dependency Injection) : 각 클래스간의 의존관계를 빈 설정(Bean Definition)정보를 바탕으로 컨테이너가 자동으로 연결해주는 것
  • Setter Injection(수정자 주입)
  • Constructor Injection(생성자 주입)
  • Method Injection(필드 주입)

IoC 장점

  • 객체 간 결합도를 낮춘다
  • 유연한 코드 작성 가능
  • 가독성 증진
  • 코드 중복 방지
  • 유지보수 용이

2. DI(Dependency Injection)

  • 의존성 주입이라 부른다.
  • 객체 자체가 아니라 Framework에 의해 객체의 의존성이 주입되는 설계 패턴 (IoC 와 연결되는 개념)
  • IoC 컨테이너는 DI를 통해 의존성을 주입시키는데 방법은

의존성 주입을 해야하는 이유

  • Test가 용이해진다
  • 코드의 재사용성을 높여준다
  • 객체 간의 의존성을 줄이거나 없앨 수 있다
  • 객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다

의존성 주입의 3가지 방법

  • 생성자 주입(Constructor Injection)
  • 필드 주입(Field Injection)
  • 수정자 주입(Setter Injection)

2-1. 생성자 주입(Constructor Injection)

@Controller
public class ExampleController {
    private final ExampleService exampleService;

    public ExampleController(ExampleService exampleService) {
        this.exampleService = exampleService;
    }
}
  • 클래스의 생성자가 하나이고, 그 생성자로 주입받을 객체가 빈으로 등록되어 있다면 @Autowired를 생략할 수 있습니다.

2-2. 필드 주입(Field Injection)

@Controller
public class ExampleController {
    
    @Autowired
    private ExampleService exampleService;
}
  • 필드에 @Autowired 어노테이션만 붙여주면 자동으로 의존성 주입됩니다.

단점

  • 코드가 간결하지만, 외부에서 변경하기 힘들다.
  • 프레임워크에 의존적이고 객체지향적으로 좋지 않다.

2-3. 수정자 주입(Setter Injection)

@Controller
public class ExampleController {
    private ExampleService exampleService;

    @Autowired
    public void setExampleService(ExampleService exampleService) {
        this.exampleService = exampleService;
    }
}
  • Setter 메소드에 @Autowired 어노테이션을 붙이는 방법이다.

단점

  • 수정자 주입을 사용하면 setXXX 메서드를 public으로 열어두어야 하기 때문에 언제 어디서든 변경이 가능하다.

2-4. 권장 주입 방식
생성자를 통한 주입 방식

1. 순환 참조를 방지할 수 있다.
개발을 하다 보면 여러 컴포넌트 간에 의존성이 생긴다.
ex) A가 B를 참조하고, B가 다시 A를 참조하는 순환 참조되는 코드

@Service
public class AService {
    //순환참조
    @Autowired
    private Bservice bService;

    public void HelloA() {
        bService.HelloB();
    }
}

@Service
public class BService {
    //순환참조
    private AService aService;

    public void HelloB() {
        aService.HelloA();
    }
}

필드 주입과 수정주 주입은 빈이 생성된 후에 참조를 하기 때문에 어플리케이션이 아무런 오류 그리고 경고 없이 구동된다. 즉, 실제 코드가 호출될 때까지 문제를 알 수 없다는 의미이다.
반면, 생성자를 통해 주입하고 실행하면 BeanCurrentlyInCreationException이 발생하게 됩니다.
순환 참조 뿐만아니라 더 나아가서 의존 관계에 내용을 외부로 노출 시킴으로써 어플리케이션을 실행하는 시점에서 오류를 체크할 수 있다.

2. 불변성(Immutability)

@Controller
public class ExampleController {

    private final ExampleService exampleService;

    public ExampleController(ExampleService exampleService) {
        this.exampleService = exampleService;
    }

}

생성자로 의존성을 주입할 때 final로 선언할 수 있고, 이로인해 런타임에서 의존성을 주입받는 객체가 변할 일이 없어지게 된다.
하지만 수정자 주입이나 일반 메소드 주입을 이용하게 되면 불필요하게 수정의 가능성을 열어두게 되고, 이는 OOP의 5가지 원칙 중 OCP(Open-Closed-Principal,개방-패쇄의 원칙)를 위반하게 됩니다.
그러므로 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 좋다.
또한,필드 주입 방식은 null이 만들어질 가능성이 있는데, final로 선언한 생성자 주입 방식은 null이 불가능하다.

3. 테스트에 용이하다.
생성자 주입을 사용하게 되면 테스트 코드를 좀 더 편리하게 작성할 수 있다.
DI의 핵심은 관리되는 클래스가 DI 컨테이너에 의존성이 없어야 한다는 것이다.
즉, 독립적으로 인스턴스화가 가능한 POJO(Plain Old Java Object)여야 한다는 것이다.
위와 같은 이유로 필드 주입이나 수정자 주입보다는 생성자 주입의 사용을 권장한다.


3. AOP(Aspect Oriented Programming)

  • 관점 지향 프로그래밍이라 부른다.
  • 개발을 하다보면 반복되는 작업들을 모아서 필요한 적절한 시기에 적용하는 개념
  • 따로 코드 밖에서 개발을 해두고 프록시 개념으로 메서드가 실행되기 전, 실행된 직후, 실행시점에 따라 기능을 적용 시키는 것이다.

4. PSA(Portable Service Abstraction)
서비스 추상화라는 의미이다.
서비스의 내용을 모르더라도 해당 서비스를 이용할 수 있다는 것을 의미한다.
예를 들어, JDBC Driver를 사용해 데이터베이스에 접근하지만 JDBC Driver가 어떻게 구현되어 있는지는 관심이 없다.
실제 구현부를 추상화 계층으로 숨기고 핵심적인 요소만 개발자에게 제공함으로써 실제 구현부를 모르더라도 해당 서비스를 이용할 수 있도록 하는 것이다.

Portable 은 휴대용이라는 뜻으로 JDBC Driver 종류를 mysql driver에서 oracle driver로 변경한다고 해서 프로젝트의 비지니스 로직에 변화가 없다는 것이다.
이런 기능이 가능한 것은 추상화 계층의 존재 때문이다.
모든 JDBC Driver는 공통적인 인터페이스를 가지고 있기 때문에 해당 인터페이스를 구현하는 어떤 것으로 대체되든 프로젝트에 영향이 없어지는 것이다.

Spring PSA는 확장에는 열려있고 수정에는 닫혀있어야 한다는 OCP의 대표적인 예시라고 할 수 있다.(객체지향 SOLID원칙)

Reference :
https://dev-coco.tistory.com/80?category=1009530
https://ch4njun.tistory.com/270

profile
백엔드 개발자 지망

0개의 댓글