Hello

스프링 프레임워크(Spring framework) [feat, DI · IoC · AOP]

by 볼빵빵오춘기

스프링 프레임워크(Spring framework) 

자바에서 가장 많이 사용되는 프레임워크이다.

DI(Dependecy Injection, 의존성 주입),IoC(Inversion of Control, 제어의 역전)와 AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)를 핵심 개념으로 하며, 모듈화된 아키텍처를 통해 개발자에게 많은 유연성과 확장성을 제공한다.

 

DI(Dependecy Injection, 의존성 주입)

객체를 직접 생성하는 것이 아니라 외부에서 생성 후 주입시켜주는 방식이다.

즉, 의존 관계를 외부에서 결정하고 주입하는 것을 의미한다.

 

 

interface Book { // 더 다양한 Book을 의존받을 수 있도록 인터페이스로 추상화
	...
}
 
class ScienceBook implements Book {
	...
}
 
class EnglishBook implements Book {
	...
}
 
public class Library {
    private Book book;
 
    public Library() {
        this.book = new ScienceBook(); // ScienceBook의 생성자를 작성하여 넣은 것을 확인
//        this.book = new EnglishBook();
    }
}
  • Library 클래스를 보면 Library() 생성자에 this.book = new ScienceBook() 로 작성하여 직접적으로 ScienceBook의 생성자를 작성하여 넣은 것이 보인다.
  • 이렇게 되면 ScienceBook 클래스 내부가 변화가 이루어지면 Library 클래스에도 영향이 미치게 된다.(= 결합도가 강하다.)
  • DI는 객체를 직접 생성하는 것이 아니라 외부에서 생성 후 주입시켜주는 방식으로 외부에서 제어하도록 하여 의존성을 분리시키는것이다.
  • 외부에서 의존관계를 결정하고 주입하여 결합도를 약하게 한다.

다음과 같이 의존관계를 분리할 수 있다. 

public class Library {

    private Book book;
    
    public Library(Book book) {
        this.book = book;
    }
}

 

추가로 단위테스트를 위해 Mock객체를 사용할 수 있게한다.

Mock객체란 더미부품을 만드는것인데 Mock객체를 이용하게 되면 인스턴스를 만들어서 사용하게되면 단위테스트할 때 대체하기 쉽다.

(why?

외부에 띄어져있는 스프링컨테이너에있는 객체를 주입받는 방식이기때문에 주입받는 과정에서 Mock이라는 객체를 대체해서 넣어줄 수 가 있기 때문이다.)

위에 코드에서 의존성을 분리하지않았다면 Mock객체로 대입이 할 수 없기때문에 위에 상황에서는 Library 클래스를 테스트하면서 ScienceBook 클래스도 같이 테스트를 해야한다.

 

의존성 주입의 장단점

장점

- 코드의 재사용, 유연성이 높아진다.
- 코드의 가독성이 높아진다.
- 결합도를 낮춘다.(클래스간의 영향력이 낮춘다.)
- 단위 테스트에 용이하다.

 

단점

- 책임이 분리되어 있기 때문에 클래스 수를 늘림으로써 복잡성이 증가한다.
- 빌드 시간이 늘어날 수 있으며, 프레임워크에 대한 의존도를 높인다.

 

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

전통적인 객체 생성 방식일 경우에는 개발자가 직접 객체를 생성하고, 객체 간의 의존성을 설정한다. 이로 인해 객체 간의 결합도가 높아지고, 유지보수가 어려웠다.

 

제어의 역전 방식을 경우에는 객체의 생성, 초기화, 의존성 주입, 라이프사이클 관리 등의 제어권을 프레임워크가 담당하기때문에 개발자는 필요한 객체를 명시하고, 객체 간의 의존성을 설정하는 역할만 한다.

 

IoC 의 역할을 담당하는것이 Spring Container이며, Spring ContaIner를 IoC Container 라고도 한다.

 

전통적인 객체 생성 방식 : 

public class Service {
    public void serve() {
        System.out.println("Service is serving...");
    }
}

public class Client {
    private Service service;

    public Client() {
        this.service = new Service(); // Client가 Service 객체를 직접 생성
    }

    public void doSomething() {
        service.serve();
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.doSomething();
    }
}

 

위의 코드에서 Client는 Service 객체를 직접 생성한다.

이 경우 Client는 Service 객체에 강하게 결합되어 있다.

 

IoC를 사용한 객체 생성 방식

먼저, 스프링 컨테이너를 설정하는 XML 파일을 작성하거나 어노테이션을 사용하여 설정을 할 수 있다.

 

XML 설정 예시:

<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="service" class="com.example.Service"/>
    <bean id="client" class="com.example.Client">
        <property name="service" ref="service"/>
    </bean>

</beans>

 

Java 클래스:

// Service.java
package com.example;

public class Service {
    public void serve() {
        System.out.println("Service is serving...");
    }
}

// Client.java
package com.example;

public class Client {
    private Service service;

    public void setService(Service service) {
        this.service = service; // Setter를 통해 주입
    }

    public void doSomething() {
        service.serve();
    }
}

// Main.java
package com.example;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Client client = (Client) context.getBean("client");
        client.doSomething();
    }
}

 

위의 예제에서는 Client 클래스가 Service 객체를 직접 생성하지 않는다.

대신 스프링 컨테이너가 Service 객체를 생성하고, Client 객체에 주입한다.

이를 통해 Client와 Service 간의 결합도가 낮아지고, 객체의 생성과 관리에 대한 제어권이 스프링 컨테이너에 넘어간다.

이렇게 하면 애플리케이션의 유연성과 확장성이 향상된다.

 

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

공통적인 기능을 분리하여 애플리케이션의 주요 비즈니스 로직과는 독립적으로 관리할 수 있게 해준다.

AOP를 사용하면 코드의 중복을 줄이고, 코드의 모듈화를 향상시킬 수 있다.

 

 

참고

https://ysmp.tistory.com/entry/프레임워크Framework-이해하기

https://ooeunz.tistory.com/56

https://www.youtube.com/watch?v=YSsl5-q2BR4

https://dmaolon00.tistory.com/entry/Spring-의존성-주입DI-Dependency-Injection-생성자-주입을-사용해야-하는-이유

https://velog.io/@sana/DI-의존성-주입Dependency-Injection-의-개념과-방법

https://lucas-owner.tistory.com/39

https://creamilk88.tistory.com/148

 

블로그의 정보

Hello 춘기's world

볼빵빵오춘기

활동하기