DI, IoC, Bean...

최봉진·2022년 3월 27일
0

3주차 주특기 기초

목록 보기
2/4

오늘은 스프링을 사용하는데 있어 핵심키워드라고 할 수 있는 DI(의존성 주입), IoC(제어의 역전), 그리고 Bean에 대해서 공부해 보기로 했다.

DI (Dependency Injection)

위키백과 - 소프트웨어 엔지니어링에서 의존성 주입(dependency injection)은 하나의 객체가 다른 객체의 의존성을 제공하는 테크닉이다. "의존성"은 예를 들어 서비스로 사용할 수 있는 객체이다.

위의 내용을 읽어봐도 무슨말인지 잘 모르겠다. 일단 의존관계에 대해서 알아보자. 의존관계란 만약 A가 B를 의존한다고 했을 때 의존대상 B가 변하면 그 영향이 A에 까지 미친다라고 할 수 있다.
즉, B의 기능이 추가되거나 변하면 A에 까지 그 영향이 미친다는 말이다.

우리는 또한 강한 결합이라는 개념에 대해 알아야 한다.

public class Controller1 {
	private final Service1 service1;

	public Controller1() {
		this.service1 = new Service1();
	}
}

위의 코드 처럼 Controller1에 Service1 객체를 생성해서 사용한다고 한다.

public class Service1 {
	private final Repository1 repository1;

	public Service1() {
		this.repository1 = new Repository1();
	}
}

Service1 에서는 repository1의 객체를 생성해서 사용한다

public class Repository1 { ... }

Repository1 객체를 선언한다.

public class Repository1 {

	public Repository1(String id, String pw) {
    // DB 연결
    Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", id, pw);
  }
}

만약 Repository1 객체를 생성할때 DB 접속 id와 pw를 받아서 사용한다면 어떻게 될까?


위 그림처럼 모든 컨트롤러와 모든 서비스에 id와 pw를 넣어줘야 할 것이다. 꼬리의 꼬리를 무는 상황이 발생한다. 즉 Repository1 하나의 변경이 서비스와 컨트롤러 까지 영향을 주었다. 이것이 바로 강한 결합이다. 강한결합은 객체지향 프로그래밍에서 지양해야 되는 부분이다. 그렇다면 강한 결합을 해결하기 위해선 어떻게 해야할까?

방법은 간단하다.
1. 각 객체에 대한 생성은 딱 1번만 한다.
2. 생성된 객체를 모든곳에서 재사용한다.

public class Repository1 { ... }

// 객체 생성
Repository1 repository1 = new Repository1();

Repository1 객체를 새로 만든다.

Class Service1 {
	private final Repository1 repitory1;

	// repository1 객체 사용
	public Service1(Repository1 repository1) {
		this.repository1 = repository1;
	}
}

// 객체 생성
Service1 service1 = new Service1(repository1);

Service1을 생성할 때 new Repository를 하지 않고 위에서 생성된 repository1을 가져다 쓴다.

Class Controller1 {
	private final Service1 service1;

	// service1 객체 사용
	public Controller1(Service1 service1) {
		this.service1 = service1;
	}
}

Controller에서도 마찬가지로 Service1을 가져다 쓴다.
만약 아까와 같이 Repository1에서 id와 pw가 필요하다면 어떻게 하면될까?

public class Repository1 {

	public Repository1(**String id, String pw**) {
    // DB 연결
    Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", **id, pw**);
  }
}

// 객체 생성
**String id = "sa";
String pw = "";**
Repository1 repository1 = new Repository1(**id, pw**);

그냥 Repository1에 넣어주면된다. 아주 간단해졌다. Repository1이 변경되었다고 해서 다른 서비스나 컨트롤러에 영향을 전혀 주지 않는다. 결론적으로 강한 결합이 느슨한 결합이 되었다.

또한 프로그램의 제어 흐름이 반대가 되었다.

IoC (Inversion of Control)

그렇다면 제어의 역전이란 무엇일까? 제어의 역전을 정의하면 다음과 같다.
객체의 생성부터 소멸까지 객체의 모든 생명주기를 개발자가 아닌 컨테이너가 담당하는 것

간단하게 말해 원래 개발자가 해왔던 객체 관리를 프로그램이 알아서 해준다는 뜻이다. 스프링에는 IoC 컨테이너라는게 있는데 우리가 Bean(객체)을 등록하기만 하면 컨테이너에서 Bean의 생성부터 의존성 주입, 초기화, 소멸까지 알아서 관리해준다.

Bean - 스프링이 관리하는 객체
스프링 IoC컨테이너 - '빈'을 모아둔 통

제어의 역전에는 여러가지 장점들이 있다.
1. 객체 관리에 신경을 덜 쓸 수 있다.
2. 약한 결합을 이용하여 객체 간 의존 관계를 쉽게 변경할 수 있다.
3. 코드의 재사용성과 유지보수성을 높인다.

그렇다면 Bean을 설정하는 방법에 대해 알아보자.

우선 클래스 위에 @Component를 설정해 주는 방법이 있다.

@Component
public class ProductService { ... }

스프링 서버가 뜰 때 스프링 IoC에 빈을 저장한다.

// 1. ProductService 객체 생성
ProductService productService = new ProductService();

// 2. 스프링 IoC 컨테이너에 빈 (productService) 저장
// productService -> 스프링 IoC 컨테이너

코드 옆에 커피콩 모양의 아이콘이 생긴다. 이는 IoC에서 관리할 Bean이라는 표시다.

@Component의 적용조건에는 @ComponentScan에 패키지 위치를 설정해 주어야 한다. 하지만 우리는 @SpringBootApplication에 의해 별다른 설정을 해주지 않아도 적용 시킬 수 있다. (default 설정에 의해 ComponentScan이 되어있다.)

@Component를 갖는 어노테이션에는 다음 어노테이션 등이 있다.

@Controller, @Service, @Repository, @Configuration

단 @Configuration 어노테이션은 수동으로 빈을 등록하는 방법이기 때문에 직접 제어가 불가능한 라이브러리를 빈으로 등록할 경우 등에 사용된다.

Bean 사용방법에는 다음과 같은 방법도 있다.

@Component
public class ProductService {
		
    @Autowired
    private ProductRepository productRepository;
		
		// ...
}

멤버 변수 위에 @Autowired 선언을 해주면 스프링에 의해 DI된다.

@Component
public class ProductService {

    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
		
		// ...
}

Bean을 사용할 메소드 위에 선언하면 스프링에 의해 DI된다.
단! @Autowired 선언은 IoC 컨테이너에 의해 관리되는 클래스에서만 적용된다. (커피콩 모양 확인!)
또한, Lombok의 @RequiredArgsConstructor 를 사용하면 @Autowired는 생략할 수 있다.

오늘은 스프링에서 가장 중요한 개념인 DI 와 IoC에 대해서 공부해 봤다. 아직도 명확히 개념이 서지 않기에 더 많은 공부가 필요할 것 같다.

profile
개발자가 되고픈 비전공자

0개의 댓글