[스프링부트 #3] DI와 IoC, Bean

김지현·2023년 11월 3일
0
post-thumbnail

Ioc와 DI는 설계 원칙 및 디자인 패턴이다. 자세히 구분하자면 Ioc는 설계 원칙에 해당하고 DI는 디자인 패턴에 해당한다. spring의 핵심 기술 중 하나로 'DI패턴을 사용하여 Ioc 설계 원칙을 구현하고 있다'고 할 수 있다.

DI (Dependency Injection)

어떤 객체에서 다른 객체를 생성, 사용하게 된다면 두 객체는 의존 관계가 된다. 사용되는 객체가 사용하는 객체에 의존하게 된다.

public class Consumer {
	Chicken chicken = new Chicken();	// 강한 의존성
    chicken.eat();
}

 public static void main(String[] args) {
    Consumer consumer = new Consumer();		
    consumer.eat(new Chicken());		// 약한 의존성
}

위의 코드에서 Consumer 객체 안에서 Chicken 객체를 생성하는 코드는 강한 의존성, 강한 결합을 가지게 된다. 변경 사항이 생기면 많은 수의 코드를 변경해야 하므로 비효율적이다.

반면 아래 코드는 외부에서 Chicken 객체를 생성하여 Consumer 객체에게 매개변수로 전달해주고 있다. 이것을 약한 의존성, 약한 결합을 가진다고 하며 변경 사항에 효율적이고 유연하게 대처할 수 있게 된다. 이때 외부에서 객체를 생성하여 필요한 객체에 전달하는 이 행위를 의존성 주입(DI)이라고 한다.

의존성을 주입하는 방법에는 여러 가지가 있다. 필드에 직접 주입시켜줄 수도 있고, 메서드를 통해 주입시켜줄 수도 있고, 생성자를 통해 주입시켜줄 수도 있다. 중요한 것은 외부에서 객체를 생성하여 필요한 객체에 주입시켜준다는 것이다.

IoC (Inversion of Control)

Ioc는 말 그대로 제어의 역전이라는 뜻이며 메소드나 객체의 호출 작업이 외부에서 결정되는 것을 의미한다. 의존성 주입 방식을 사용하게 되면 제어의 역전이 일어나게 된다.

위의 코드 예시를 다시 활용해보자면, 강한 결합에서는 Consumer 객체 안에서 Chicken 객체를 생성하고 Chicken 객체 내부의 eat 메서드를 불러오므로 제어의 흐름이 Consumer → Chicken 이 된다.
반면 아래의 약한 결합에서는 생성된 Chicken 객체를 Consumer 객체에 전달해주므로 제어의 흐름이 Chicken → Consumer 이 된다. 즉 제어의 역전이 일어난 것이다.

Bean

DI는 외부에서 객체를 생성하는 것이라고 하였는데 그렇다면 여기서 이 외부는 어디일까. 바로 스프링이다. 정확히는 스프링의 Ioc 컨테이너로 이 공간에서 필요한 객체를 생성하고 관리하는 역할을 대신해준다. 스프링에서는 이 객체를 Bean이라고 부른다.

Bean 등록과 사용

스프링에 Bean을 등록하려면 클래스 위에 @Component 애너테이션을 사용하면 된다. 스프링은
@Component 가 설정된 클래스를 클래스의 앞글자만 소문자로 변경한 이름으로 자동으로 저장해주며 옆에 콩 모양 아이콘이 뜨는 것을 확인할 수 있다.

Bean을 사용하기 위해서는 필드나 메서드 위에 @Autowired 애너테이션을 사용하면 된다. 이때 생성자 선언이 1개뿐이라면 애너테이션 생략이 가능하다.

@Component
public class MemoService {
    private final MemoRepository memoRepository;

    @Autowired
    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }
}

Lombok 의 @RequiredArgsConstructor 애너테이션을 사용하면 final로 선언된 멤버 변수를 매개변수로 사용하여 생성자를 자동으로 생성해주는데 자동 생성된 생성자에 자동으로 Bean 객체가 사용된다.

@Component
@RequiredArgsConstructor 
public class MemoService {
    private final MemoRepository memoRepository;
    
// 자동 생성
//    public MemoService(MemoRepository memoRepository) {
//        this.memoRepository = memoRepository;
//    }

3 Layer Annotation

Controller, Service, Repository의 역할로 구분된 클래스들을 Bean으로 등록할 때는@Component 애너테이션을 사용하지 않고 각각의 애너테이션을 사용할 수 있다. 이 경우 Bean 등록뿐만 아니라 클래스의 역할을 명시하게 된다.

  1. @Controller, @RestController
  2. @Service
  3. @Repository

0개의 댓글