개발 새발/Spring

[Spring] 스프링 빈과 의존관계

recordari 2024. 8. 8. 22:01

*인프런 김영한의 스프링 입문 강의를 기반으로 작성되었습니다.

 

스프링 빈(Bean)이란?

  • 스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트이다. 스프링 컨테이너가 관리하는 자바 객체를 말하며 하나 이상의 빈(bean)을 관리한다.
  • 빈은 인스턴스화된 객체를 의미하며 스프링 컨테이너에 등록괸 객체를 스프링 빈이라고 한다.

여기서 스프링 컨테이너란?

  • 자바의 객체의 생명주기를 관리하며 생성된 자바 객체들에게 추가적인 기능을 제공하는 스프링 프레임워크의 핵심 컴포넌트
  • 즉, 내부에 존재하는 빈의 생명주기(생성, 관리, 제거 등)를 관리하고 생성된 빈에게 추가적인 기능을 제공

 

지금까지 서비스를 통해서 회원가입, 리포지토리에 저장 및 꺼내오기를 테스트를 통해 제대로 작동하는지 확인했다.

이제는 화면을 만들어볼려고 한다. 그러기 위해서 컨트롤러와 뷰 탬플릿이 필요하다.

컨트롤을 통해서 외부 요청을 받고, 서비스에서 비즈니스 로직을 만들고, 레포지토리에서 데이터를 저장하는 것이 전형적인 방식이다.

 


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

회원 컨트롤러가 회원 서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 추가한다.(회원 컨트롤러는 회원 서비스를 통해 회원가입 및 데이터 조회를 하기 때문에 회원 서비스를 의존하고 있다)

src/main/java/hello.hello_spring/controller 위치에 MemberController.java 파일을 만들어 다음과 같이 코드를 작성해준다.

@Controller
public class MemberController {

    private final MemberService memberService;

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

@Controller 어노테이션이 적용된 클래스는 컨트롤러임을 나타내며 빈으로 등록된다. 생성자에 @Autowired가 있으면 memberService를 스프링이 스프링 컨테이너에 있는 MemberService를 가져와서 연결시켜 준다. 이렇게 객체 의존관게를 외부에서 넣어주는 것을 의존성 주입(Dependency Injection = DI)이라고 한다.

참고
helloController는 스프링이 제공하는 컨트롤러여서 스프링 빈으로 자동 등록된다.

 

회원 서비스 빈 등록

MemberService 클래스는 순수 자바 클래스이기 때문에 MemberService가 뭔지 알지 못한다. 그래서 memberService.java 파일에 들어가서 @Service 어노테이션을 통해 빈으로 등록해준다. 코드는 다음과 같다.

@Service
public class MemberService {

    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}
참고
생성자에 @Autowired를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아 서 주입한다. 생성자가 1개만 있으면 @Autowired 는 생략할 수 있다.

 

회원 리포지토리 빈 등록

회원 서비스도 회원 리포지토리를 의존하고 있기 때문에 MemberRepository 클래스에 @Repository 어노테이션을 붙여서 스프링 컨테이너에 등록한다. 코드는 아래과 같다.

@Repository
 public class MemoryMemberRepository implements MemberRepository {}
참고
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록해서 공유한다) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지 만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.

 

컴포넌트 스캔원리
@Component 어노테이션이 있으면 스프링 빈으로 자동 등록된다. @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문이다.
@Component를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다. @Controller, @Service, @Repository

 


자바 코드로 직접 스프링 빈 등록하기

회원 서비스와 회원 리포지토리의 @Service, @Repository, @Autowired 애노테이션을 제거하고 진행한다.

src/main/java/hello.hello_spring 위치에 SpringConfig.java 파일을 만들어서 아래와 같이 작성해준다.

package hello.hello_spring;

import hello.hello_spring.repository.MemberRepository;
import hello.hello_spring.repository.MemoryMemberRepository;
import hello.hello_spring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

스프링이 뜰 때 @Configuration 어노테이션을 읽고 @Bean 어노테이션을 통해 memberRepository를 스프링 빈에 등록 대상으로 인식한다. 마찬가지로 스프링 빈에 등록된 memberService이 컴포넌트 스캔방식으로 빈에 등록된 memberController에 주입된다.

그러면 스프링이 뜰 때 memberService, memberRepository를 둘 다 스프링 빈으로 등록하고 스프링 빈으로 등록되어 있는 memberRepository를 memberService에 넣어준다.

 

참고
DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으 로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그 리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
@Autowired를 통한 DI는 helloController, memberService 등과 같이 스프링이 관리하 는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.

 

정리
- 스프링 빈을 등록하는 두가지 방법
1. 컴포넌트 스캔과 자동 의존관계 설정
스프링이 띄워질 때 @Component 와 관련된 어노테이션이 있으면, 그것들은 전부 객체를 생성해서 스프링 컨테이너에 등록해주고 @Autowired는 연관관계를 만든다. 그래서 MemberController가 MemberService를 사용할 수 있고, MemberService가 MemberRepository를 사용할 수 있게 된다.

2. 자바 코드로 직접 스프링 빈 등록하기