상세 컨텐츠

본문 제목

[스프링2] 섹션 4. 스프링 빈과 의존관계

23-24/Spring 2

by YUZ 유즈 2023. 9. 29. 13:56

본문

728x90

스프링은 처음 실행 시 스프링 컨테이너(Spring Container)가 생성됩니다. 스프링 컨테이너는 스프링의 핵심 컴포넌트로, 자바 객체를 관리하는데, 이 자바 객체를 빈(Bean)이라 칭합니다. 빈은 스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트입니다. 빈은 인스턴스화된 객체를 의미하며, 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 합니다. 스프링 컨테이너는 객체를 생성하고 소멸하며, 클래스 간의 연결을 통하여 각 객체의 의존관계를 주입할 수 있습니다.

섹션 4에서는 스프링 컨테이너가 클래스를 인식하는 방법과 자바 객체를 어떻게 관리하는지 알아봅니다.


스프링 빈을 등록하는 것에는 2가지 방법이 있습니다.

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

스프링 컨테이너가 생성될 때, 어노테이션(@)을 탐색합니다. 어노테이션은 주석이라는 뜻으로, 자바에서 어노테이션은 코드 사이에 특별한 기능을 수행하도록 합니다. 

클레스에 컴포넌트 어노테이션(@Component)을 사용하면, 스프링 컨테이너가 패키지 하위로 탐색하여 해당 클래스의 객체를 생성하고 컨테이너에 자동 등록합니다. 이후로 빈을 관리하는데, 객체를 사용할 때 단독으로 객체를 생성하지 않고 컨테이너에서 받아 써야 하기 때문에 하나의 객체만 사용하게 됩니다. 만약 단독으로 객체를 생성한다면, 다른 컨트롤러가 객체를 쓸수도 있으며, 여러 객체를 생성할 이유가 없기 때문에 컨테이너를 통해 관리하는 것입니다.

package hello.hellospring.controller;
import org.springframework.stereotype.Controller;

@Controller //컴포넌트 어노테이션
public class MemberController {}

해당 MemberController 클래스는 Controller 역할을 하는 클래스기 때문에 @Controller를 붙여 표시하였습니다.

 

컨테이너에 객체를 등록했다면, 연관된 객체 간의 연결을 통하여 하나의 객체를 사용할 수 있습니다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 의존관계 설정, 의존성 주입(Dependency Injection)이라 부릅니다.

의존성 주입은 필드 주입, setter 주입, 생성자 주입으로 3가지 방법이 있습니다.

// 필드 주입
@Controller
public class MemberController {
    @Autowired private MemberService memberService;
}
// setter 주입
@Controller
public class MemberController {
    private MemberService memberService;
    
    @Autowired
    public void setMemberService(MemberService memberService){
        this.memberService = memberService;
    }
}
// 생성자 주입
@Controller
public class MemberController {
    private final MemberService memberService;

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

먼저, MemberController 클래스는 MemberService라는 서비스 기능을 사용하는 의존관계를 형성하고 있습니다. 앞선 방법으로 MemberController가 스프링 컨테이너에 등록이 되었다면, MemberService와의 의존성을 주입해야 하는데요, @Autowired을 통해 두 객체를 연결할 수 있습니다.

필드 주입 방법은 이후의 수정이 불가능한 단점이, setter 주입 방법은 set 함수를 public으로 선언해야 한다는 단점이 있습니다. 런타임 중에 의존관계가 변하는 경우는 겨의 없으므로 주로 생성자 주입을 선호합니다.

생성자에 @Autowired을 붙여 인자로 MemberService를 받는다면, 컨테이너에 등록된 MemberService 빈이 MemberController의 생성자 인자로 들어갑니다. 그 이후로 하나의 객체만 생성(Singleton)하여 사용하기 때문에 두 객체가 연결되어 동작하게 됩니다. 설정으로 싱글톤 등록이 아니게 설정할 수 있으나 대부분 싱글톤을 사용합니다.

 

MemberService MemberRepository을 의존하는데, 이 역시 컴포넌트 스캔과 @Autowired으로 구현합니다.

@Service
public class MemberService {
    private final MemberRepository memberRepository;

    // 생성자, 인자로 저장소 받음 (같은 저장소 객체 공유) (의존성 주입(Dependency Injection)(DI))
    @Autowired
    public MemberService(MemoryMemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
    // 중략
    }
@Repository
public interface MemberRepository {
    // 중략
}

@Controller, @Service, @Repository @Component를 포함하는 어노테이션으로, 스프링 빈으로 자동 등록됩니다.

 

 

 

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

@Service, @Repository, @Autowired 어노테이션을 사용하지 않고 Config 클래스를 통하여 스프링 빈을 직접 등록할 수 있습니다. @Controller는 사용합니다. 의존성 주입을 하는 이유가 객체간의 결합도를 낮추어 요구사항에 따라 각 객체의 수정과 관리를 용이하게 하기 위함인데요, 컨트롤러는 요구사항이 변한다고 하여 수정하거나 다른것으로 교체해야 하지 않기 때문에 Config로 관리하지 않습니다.

아래는 MemberService MemberRepository의 컴포넌트 어노테이션을 제거하고 @Configuration으로 관리하는 코드입니다.

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

    @Bean
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}

@Bean 어노테이션을 통하여 각 클래스의 빈을 등록하고, MemberService MemberRepository에 대해 의존성을 지니고 있기 때문에 MemberService 생성자의 인자로 빈에 등록한 MemberRepository 객체를 전달하였습니다.

 

주로 일반적인 컨트롤러, 서비스, 레포지토리와 같은 클래스는 컴포넌트 스캔 방식을 사용합니다. 정형화되지 않거나, 상황에 따라 구현 클래스를 변경해야 한다면 Config 파일을 통해 등록하는 방식을 사용합니다. 직접 등록 시에는 변경할 클래스 외의 다른 클래스에서는 수정이 필요 없기 때문입니다.

과거에는 XML 문서로 빈을 설정하였으나 최근에는 사용하지 않습니다.

@Autowired을 사용하는 의존성 주입은 빈에서만 동작하며, 개발자가 new로 직접 생성한 객체에서는 동작하지 않습니다.


Quiz

1. 스프링 컨테이너는 (   자바 객체   )를 관리하는데, 이 자바 객체를 (   빈   )이라 칭한다.

2. 생성자에 (   @Autowired   )가 있으면 스프링 컨테이너에서 찾아서 등록된다.

3. (   @Component   )가 있으면 스프링 빈으로 자동 등록된다.

4. 스프링 빈을 등록할 때 기본적으로 (   싱글톤   )으로 등록된다.

5. 의존성 주입에는 (   필드 주입, setter 주입, 생성자 주입   ) 세 가지 방법이 있다.

6. 정형화된 코드에 (   컴포넌트 스캔   )을 사용한다.

7. 상황에 따라 구현 클래스를 변경할 때 (   설정 파일(Config)   )을 사용한다. 

8. 아래의 코드는 필드 주입으로 작성된 코드입니다. 이를 생성자 주입 방식 코드로 작성하세요.

@RestController
public class CornerController {
    @Autowired
    private CornerRepository cornerRepository;
    
    @Autowired
    private CornerService cornerSerivce;
}

9. 아래의 코드는 @Configuration을 사용한 코드입니다. 이를 컴포넌트 스캔 방식 코드로 작성하세요.

@Configuration
public class ResourceConfig {
    @Bean
    public CornerResource cornerResource() {
        return new CornerResource();
    }
}

해당 포스트는 김영한, [스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술], (2022, 11월 28일), 섹션 4. 스프링 빈과 의존관계 강의를 참고하여 작성하였습니다.

 

 

 

Corner Spring 2

Editor : 동동

728x90

관련글 더보기