🍀Spring/기본

컴포넌트 스캔과 자동 의존관계 설정

말동말동현 2024. 7. 6. 18:01

먼저 빈(Bean)에 대해 알아보자.

 

빈(Bean)

 

스프링(Spring) 컨테이너가 관리하는 자바 객체를 빈(Bean)이라 한다.

 

스프링의 특징에는 제어의 역전(IoC)가 있다.

 

지금까지 사용자가 new연산을 통해 객체를 생성하고 메소드를 호출했지만 제어의 역전이 적용 된 경우에는 객체의 생성과 사용자의 제어권을 스프링에게 넘긴다.

 

사용자는 직접 new를 이용해 생성한 객체를 사용하지 않고, 스프링에 의하여 관리당하는 자바 객체를 사용한다. 이 객체를 '빈(bean)' 이라 한다.

 

 

 

@Controller
public class MemberController {

 	private final MemberService memberService;
    
 	@Autowired
 	public MemberController(MemberService memberService) {
 	this.memberService = memberService;
 	}
}

회원 컨트롤러가 회원 서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 설정한 것이다.

 

  • 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라한다.

 

스프링 빈을 두가지 방법으로 등록할 수 있는데,

  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록하기

 

@Component 어노테이션이 있으면 스프링 빈으로 자동 등록된다.

@Controller, @Service, @Repository도 컴포넌트 스캔이 안에 달려있기 때문에 스프링 빈으로 등록된다.

 

스프링 빈 등록은 @Component, 연결은 @Autowired 라고 볼 수 있다.

 

 

 

참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록해서 공유한다) -> @Configuration을 통해 수동으로 등록 가능

 

따라서 같은 스프링 빈이면 모두 같은 인스턴스다.

 

 

 

@Controller

 

1. @Controller 어노테이션의 역할

 

   - @Controller 어노테이션은 말 그대로, 해당 클래스가 웹 애플리케이션의 컨트롤러임을 나타낸다.

   - 스프링 컨테이너는 @Controller 어노테이션이 지정된 클래스를 빈으로 등록하고, 요청을 해당 컨트롤러에 매핑하여 처       리한다.

   - @Controller 어노테이션은 클라이언트의 요청을 처리하는 비즈니스 로직과 뷰를 결합하여 전체적인 동작을 제어한다.

 

 

 

2. @Controller 어노테이션의 사용 방법

 

   - @Controller 어노테이션이 지정된 클래스는 일반적으로 HTTP 요청을 처리하기 위한 메소드를 포함한다.

   - 각 메소드는 @RequestMapping 어노테이션과 함께 사용하여 특정 URL 패턴에 대한 처리를 정의한다. 

   - @RequestMapping 어노테이션을 사용하여 요청 URL과 해당 메소드를 매핑시키고, 메소드는 요청을 처리하고 필요한        로직을 수행한 후에 응답을 생성한다.

   - 컨트롤러 메소드에서는 ModelAndView, Model, ResponseEntity 등을 반환하여 응답 데이터와 뷰 정보를 제공한다.

 

 

 

3. @Controller 어노테이션의 추가 기능

 

   - @PathVariable 어노테이션을 사용하여 경로 변수를 추출하고 활용할 수 있다.

   - @RequestParam 어노테이션을 사용하여 요청 파라미터를 추출하고 활용할 수 있다.

   - @ModelAttribute 어노테이션을 사용하여 요청 데이터를 객체에 바인딩하고, 자동으로 모델에 추가할 수 있다

 

 

4. @Controller 어노테이션의 예시 

@Controller
@RequestMapping("/products")
public class ProductController {
    
    @Autowired
    private productService productService;
    
    @RequestMapping("/{id}")
    public ModelAndView getProduct(@PathVariable("id")int id) {
        Product product = productService.getProductById(id);
        ModelAndView modelAndView = new ModelAndView("productView");
        modelAndView.addObject("products", products);
        return modelAndView;
    }
}

 

위의 예시에서는 "/products"로 시작하는 URL에 대한 요청을 처리하기 위한 컨트롤러이다.

@RequestMapping 을 사용하여 각 요청 URL과 해당 메소드를 매핑하고, ProductService를 이용해 비즈니스 로직을 수행한다.

 

 

 

 


 

 

 

* DI에는 필드 주입, setter 주입, 생성자 주입 3가지 방법이 있고

 

DI (Dependency Injection)

의존대상 B가 변하면, 그것이  A에 영향을 미친다. (이일민, 토비의 스프링 3.1)

public class A {
    private B b = new B();
}

A라는 클래스가 있고, 이 클래스는 B라는 클래스를 필드로 가질때 B에 final 필드가 추가되는 변경이 일어난다면

new B()부분에서 컴파일 에러가 난다. B 내부의 변경이 일어났는데, A에도 영향을 미치게 된것이다. 

이런 경우를 A가 B에 의존한다. 라고한다. 그렇다면 의존성 주입이란, 의존성을 외부에서 주입해준다는 의미가 될것이다. 예를 들어 다음과 같은 코드이다.

public class A {
    private B b;
    
    public A(B b) {
        this.b = b;
    }
}

B가 변경되면 A의 내용도 변경되지만, 의존 대상을 직접 생성하는 것이 아닌 외부로 부터 주입을 받는다.

 

의존성을 주입하는 방법에는 세가지가 있다.

 

생성자 주입(Constructor Injection)

public class A {
    private B b;
    
    public A(B b) {
        this.b = b;
    }
}

 

Setter 주입(Setter Injection)

public class A {
    private B b;
    
    public void setB(B b) {
        this.b = b;
    }
}

 

인터페이스 주입(Interface Injection)

 

public interface BInjection {
    void inject(B b);
}

public A implements BInjection {
    private B b;
    
    @Override
    public void inject(B b) {
        this.b = b;
    }
}

 

 

그렇다면 왜 DI를 써야하는가?

 

- 의존성이 줄어든다 (변경에 덜 취약해진다.)

- 모의 객체를 주입할 수 있기 때문에 단위 테스트가 쉬워진다.

- 가독성이 높아진다.

- 재사용성이 높아진다