코드네임 JY

[스프링 입문] 스프링 빈과 의존관계 본문

백엔드 공부

[스프링 입문] 스프링 빈과 의존관계

영재임재영 2022. 12. 11. 14:10

미리보기

✅ 스프링 빈이란 무엇인가!

✅ 스프링 IoC 컨테이너가 하는 역할은 무엇인가!

✅ 스프링 빈 등록 방법에 대해 알아보자!


🍕 스프링 빈 (Spring Bean)

스프링 (IoC) 컨테이너에 의해 관리되는 자바 객체

 

라고 정의하는데, 객체는 코딩하는 사람이 직접 생성하고 관리할 수 있는거 아닌가? 라는 생각이 든다.

맞다. 보통 자바에서는 우리가 직접 클래스의 객체를 생성하고, 해당 인스턴스를 활용하도록 코드를 작성할 수 있다.

해당 객체를 사용해야하는 클래스가 여러 개라면, 각 클래스마다 매번 새롭게 객체를 생성해서 사용할 수도 있다.

 

하지만, 하나의 객체를 여러 개의 클래스에서 여러 번 정의하는 것은 조금 찝찝하기도 하고.. 관리하기도 쉽지 않을 것이다.

여기서 드는 생각!! 하나의 객체 인스턴스로 전체 코드에 적용할 수는 없을까?

 

🌮 IoC 컨테이너와 의존성 주입

실생활에서 '컨테이너(Container)'는 필요한 물건들을 담는 하나의 박스 같은 느낌으로 생각할 수 있다.

이 개념은 스프링에서도 사용된다. 스프링에서는 객체를 담아야하는데, 이 객체를 담고 있는 부분을 컨테이너라고 할 수 있다!

 

개발자는 객체의 생성 및 호출을 직접 코드로 작성할 수 있는데, 객체의 생성 및 호출 권한을 컨테이너에게 위임할 수도 있다.

이는 일반적인 과정과 달라 '제어의 역전' 이라고 부르며, 이를 담당하는 컨테이너를 바로 'IoC 컨테이너' 라고 한다!

(참고 : 'IoC' 는 'Inversion of Control' 을 의미한다)

 

'일반적으로 자바에서 객체를 사용하는 과정''IoC 컨테이너에게 이를 위임하는 것'은 위와 같이 비교할 수 있다.

객체를 생성했다면 서로 의존성을 주입해주어야 하는데, 이를 '의존성 주입(Dependency Injection)' 이라고 부른다!

(의존성 주입에는 크게 3가지 방식이 있는데, 아래 내용까지 설명하고 나열하겠다.)

 

🎯 참고 : 의존성 주입을 활용하면, 스프링 내에서 더 많은 기능들을 사용할 수 있다! (예시 : AOP ← 추후 포스팅 예정)

 

🥨 스프링 빈 등록 방법

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

컴포넌트 스캔과 자바 코드로 직접 등록하는 방법이 있다.

컴포넌트 스캔

스프링 백엔드에서 Controller ↔ Service ↔ Repository로 이어지는 구조를 사용한다고 했는데, 각각의 어노테이션이 존재한다.

 

컨트롤러 역할을 하는 클래스에는 @Controller

비즈니스 로직을 담당하는 클래스에는 @Service

저장소를 활용을 담당하는 클래스에는 @Repository 어노테이션을 각각 붙일 수 있다!!

 

하지만 이는 @Component 라는 어노테이션이 다 포함하고 있는 구조로 되어 있다.

따라서 각각 이름을 붙여도 되고, 아니면 한 번에 @Component 어노테이션을 사용해도 된다.

 

스프링이 서버에서 동작하기 시작할 때, @Component 어노테이션과 관련된 클래스들이 있으면 (Controller, Service, Repository)

이들의 객체를 하나씩 생성해서 스프링 IoC 컨테이너에 등록을 해놓는다. 그러면 이들의 연관관계를 설정해주어야 하는데,

그 관계들을 연결시켜주는 것이 바로 @Autowired 어노테이션이다!!

@Controller
public class MemberController {
    private final MemberService memberService;
    
    @Autowired
    public MemberController(MemberService memberService) {
    	this.memberService = memberService;
    }
    
}
@Service
public class MemberService {
    private final MemberRepository memberRepository;
    
    @Autowired
    public MemberService(MemberRepository memberRepository) {
    	this.memberRepository = memberRepository;
    }
    
}

각 클래스마다 @Controller, @Service, @Repository 이 달려있다면, @Autowired 를 통해서 이들의 관계를 묶어준다는 것이다!!

그리고 위에서 설명한 @Autowired 어노테이션을 활용해 연관관계를 이어주는 것이 바로 '의존성 주입(DI)' 라고 할 수 있는데,

의존성 주입 방식에도 크게 3가지 방법이 있다.

/* 1. 생성자 주입 */
@Autowired
public MemberController(MemberService memberService) {
    this.memberService = memberService;
}

/* 2. 필드 주입 */
@Autowired final MemberService memberService;
    
/* 3. Setter 주입 */    
@Autowired
public void setMemberSerivce(MemberService memberService) {
    this.memberService = memberService;
}

하지만 되도록이면 생성자 주입 방식을 사용하는 것을 추천한다!

자바 코드로 직접 등록

@Configuration
public class SpringConfig {

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

이는 자바 코드를 사용해서 수동으로 스프링 IoC 컨테이너에 빈을 등록할 수 있다. ('빈 == 객체' 라고 생각하면 된다!)

설정 클래스임을 알려주는 어노테이션은 @Configuration 이고, @Bean 을 사용해 각 메소드마다 빈을 등록할 수 있다.

일단 이 코드를 사용하려면 @Component 와 관련된 어노테이션을 지우고 진행해야한다.

 

개인적으로 컴포넌트 스캔방식이 더 직관적이라고 느끼는데, 코드로 직접 작성하는 방식의 장점이라면..

예를 들어 Repository를 변경하고 싶을 때, 컴포넌트 스캔은 해당 Repository에 가서 관련 어노테이션을 모두 변경해주어야하지만,

위 방식에서는 @Bean이 달린 Repository 관련 메소드의 return 부분을 새로운 클래스 이름으로 대체하면 된다!!

정리하면, 조금 더 변경에 용의하다는 것이다!! (리포지토리를 갈아끼우고 싶을 때와 같은 경우)

정리

'스프링 IoC 컨테이너' 는 스프링에서 사용되는 객체를 담을 수 있다.

이러한 스프링 IoC 컨테이너에 의해 관리되는 자바 객체를 '스프링 빈' 이라고 한다.

 

스프링이 서버에서 시작될 때, 스프링은 @Component 와 관련된 클래스들을 객체로 만들어서 스프링 IoC 컨테이너에 올려놓는다.

컨테이너에 올려진 객체들의 연관관계는 @Autowired 를 활용해 스프링이 직접 의존성 주입(Dependency Injection) 해준다.

하지만 코딩하는 사람이 이러한 연관관계를 직접 자바 코드로 작성할 수도 있다! 이는 조금 더 클래스 변경에 용의하다!

Comments