본문 바로가기

Back-End/Spring

Spring DI(의존성 주입)에 대해서

우리는 Java에서 기본적으로 객체를 사용할 때는 Main 클래스의 main() 메서드 안에서 new로 객체를 생성하고, 필요한 객체를 직접 전달하는 방식으로 의존성을 주입을 처리했다.
하지만! Spring에서는 개발자가 객체를 new로 생성하지 않아도, 스프링 컨테이너(Application Context)가 알아서 객체(Bean)을 생성하고, 필요한 곳에 자동으로 주입해준다.


먼저 Java 방식을 살펴보고 Spring 방식으로 넘어가보자!


Java 방식 : 직접 객체 생성
Java 방식에서는 필요한 모든 객체를 직접 생성하고 직접 전달해야 했다.

public class Main {
    public static void main(String[] args) {
        // 객체 생성 및 의존성 주입
        Service service = new Service();
        Controller controller = new Controller(service);
        
        // 메서드 호출
        controller.doSomething();
    }
}

class Service {
    public void perform() {
        System.out.println("Service is performing...");
    }
}

class Controller {
    private final Service service;

    public Controller(Service service) {
        this.service = service;
    }

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

이럴 수록 문제점이 뭐냐??

  • 의존 관계가 많아질수록 객체 생성 코드가 복잡해진다.
  • 재사용성이 떨어지고, 테스트가 어렵다.

Spring 방식 : 자동 객체 관리
Spring 에서는 객체를 @Component로 등록하고, 필요한 곳에 자동으로 주입하도록 설정한다.

// 1. Service 클래스 정의
@Component
public class Service {
    public void perform() {
        System.out.println("Service is performing...");
    }
}

// 2. Controller 클래스 정의
@Component
public class Controller {
    private final Service service;

    // Spring이 Service 객체를 자동으로 주입
    @Autowired
    public Controller(Service service) {
        this.service = service;
    }

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

// 3. Main 메서드
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

Spring에서 작동 방식

  1. 스프링 부트 실행: SpringApplication.run()으로 스프링 컨테이너가 시작됩니다.
  2. Bean 등록: @Component, @Service, @Repository, @Controller로 표시된 클래스들을 스프링이 찾아서 객체(Bean)를 생성하고 관리합니다.
  3. 의존성 주입: @Autowired를 보고, 필요한 Bean을 찾아서 자동으로 주입합니다.
  4. 코드 실행: 스프링 컨테이너가 관리하는 객체를 사용해 로직을 실행합니다.

Spring 방식의 장점

  1. 객체 생성 코드 제거 : new 키워드 사용이 거의 없어짐
  2. 유지보수성 향상 : 의존성 변경 시 스프링 컨테이너 설정만 수정하면 됨
  3. 테스트 용이성: Mock 객체를 주입하거나 설정을 쉽게 변경 가능
  4. 결합도 낮춤 : 클래스 간의 강한 결합이 줄어들고 유연성이 높아짐.

나는 배우면서 2번 방식에 대해서 궁금했다. 의존성 변경 시 스프링 컨테이너 설정만 수정을 어케 하는데??

일단 자바 방식을 보자!
MyService 객체에서 AdvancedService 객체로 변경한다고 치면 

public class MyController {
    private final MyService myService;

    public MyController() {
        // 기존 서비스
        this.myService = new MyService();
    }
}
  • 만약 MyService 대신 AdvancedService를 사용하려면 : 
    • 코드를 직접 열고 new MyService()를 new AdvancedService()로 변경해야 한다.
    • 객체 생성 코드가 여러 곳에 흩어져 있다면, 모든 곳을 수정 해야한다.


하지만 Spring 방식에서는 객체를 코드에서 직접 생성하지 않고, 스프링 컨테이너가 Bean으로 관리해주기 때문에
객체를 바꾸고 싶으면 간단한 설정 변경만으로 끝난다.

@Service
public class MyService implements ServiceInterface {
    @Override
    public void perform() {
        System.out.println("MyService is performing...");
    }
}

@Service
public class AdvancedService implements ServiceInterface {
    @Override
    public void perform() {
        System.out.println("AdvancedService is performing...");
    }
}
@Controller
public class MyController {

    private final ServiceInterface service;

    @Autowired
    public MyController(ServiceInterface service) {
        this.service = service;
    }

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

보면 MyService와 AdvancedService를 보면 위에 @Service가 있다. 그럼 어? 둘 다 @Service를 달고 있는데 스프링 컨테이너가 어떻게 알고 주입해주는거지??

@Service
@Primary  // 우선 주입할 Bean 설정
public class AdvancedService implements ServiceInterface {
    @Override
    public void perform() {
        System.out.println("AdvancedService is performing...");
    }
}

이런 식으로 앞서 배운 애노테이션을 활용하여 우선 순위를 결정해줄 수 있다.

'Back-End > Spring' 카테고리의 다른 글

IOC / DI  (0) 2024.12.06
Spring Container와 Spring Bean  (0) 2024.12.06
객체 지향 설계(SOLID 원칙)  (1) 2024.12.06
Spring Annotation  (0) 2024.12.01
스프링으로 넘어 가기 전에..  (0) 2024.11.20