본문 바로가기
[inflearn] 스프링 입문

스프링 입문) 8. 스프링 빈과 의존관계 - 컴포넌트 스캔

by 슬픈 야옹이 2023. 4. 10.

지난 포스트에서 회원 관리 서비스인 MemberSerivce를 작성하고 테스트해보았다.

 

일반적으로 Cotroller를 이용해 외부 요청을 처리하고,

Service에서 비즈니스 로직을 구현하며,

Repository에서 데이터 저장 및 처리를 구현하는 것이 정형화된 패턴이다.

 

지금까지 MemberService와 MemberRepository를 작성하였으니,

이제 MemberService를 이용해 외부 요청을 처리할 Controller를 작성해보도록 한다.

 

 

의존관계

우선 MemberController 클래스를 생성하고, MemberController에서 사용할 MemberService 인스턴스를 생성한다.

 

package hdxian.hdxianspring.controller;

import hdxian.hdxianspring.repository.MemoryMemberRepository;
import hdxian.hdxianspring.service.MemberService;

public class MemberController {

    private final MemberService memberService = new MemberService(new MemoryMemberRepository());

}

그런데 이러한 방식의 코드에는 문제가 있다.

MemberController처럼 어떤 Service를 사용하는 Controller는 여러 개 있을 수 있는데,

이렇게 Controller마다 Service를 따로 선언해 사용하는 것은 매우 비효율적이고 위험한 방법이다.

 

위 코드처럼, MemberController가 제대로 동작하려면 MemberService가 필요하고,

MemberService가 동작하려면 MemberRepository가 필요하다.

 

이렇게 클래스 등의 어떤 요소가 제대로 동작하기 위해 다른 요소를 필요로 하는 관계를 의존관계라 하고,

의존하는 요소를 클래스 내에서 직접 생성하지 않고 외부에서 주입해주는 방식을 DI(Dependency Injection, 의존성 주입)이라 한다고 하였다.

 

 

 

스프링 빈

스프링은 이러한 의존관계를 위해 스프링 빈이라는 요소를 만들어 관리한다.

스프링 빈이란 스프링 애플리케이션 구동 시 스프링 컨테이너가 직접 생성하고 관리하는 인스턴스들을 말한다.

 

자주 쓰이는 클래스의 인스턴스들을 스프링 빈으로 등록하고 이를 DI를 통해 주입받도록 하면,

여러 클래스에서 같은 인스턴스를 공유하여 사용할 수 있다.

 

MemberController가 DI를 통해 의존성을 주입받도록 변경해보자.

MemberController의 생성자에서 MemberService 인스턴스를 인수로 받도록 하고,

@Autowired 어노테이션을 붙여 MemberService 인스턴스를 스프링 빈으로 주입받도록 한다.

package hdxian.hdxianspring.controller;

import hdxian.hdxianspring.repository.MemoryMemberRepository;
import hdxian.hdxianspring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class MemberController {

    private final MemberService memberService;

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

 

 

근데 이대로 서버를 실행해보면 다음과 같은 오류가 발생한다.

 

보아하니 MemberService라는 bean이 없다는 내용이다.

 

이게 무슨 상황이냐면,

MemberController에서 @Autowired를 통해 MemberService 인스턴스를 스프링 빈으로 주입받겠다고 설정했지만,

MemberService가 스프링 빈으로 등록되어있지 않아서 오류가 발생한 것이다.

 

즉 MemberController에서 DI로 스프링 빈을 주입받으려면 받으려는 요소가 스프링 빈으로 등록되어있어야 한다는 의미다. 어찌보면 당연한 소리다.

 

그럼 MemberService를 어떻게 스프링 빈으로 등록할까?

몇 가지 방법이 있지만 우선 컴포넌트 스캔을 통해 스프링 빈을 등록하는 방법을 사용해보자.

 

방법은 간단하다. 클래스 선언부에 @Service 어노테이션을 추가하면 된다.

다만, MemberService도 MemberRepository를 의존하므로 생성자에 @Autowired를 추가한다.

package hdxian.hdxianspring.service;

import hdxian.hdxianspring.domain.Member;
import hdxian.hdxianspring.repository.MemberRepository;
import hdxian.hdxianspring.repository.MemoryMemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class MemberService {

    // private final MemberRepository repository = new MemoryMemberRepository();
    private final MemberRepository repository;

    @Autowired
    public MemberService(MemberRepository repository) {
        this.repository = repository;
    }

    ...
	...
	...
}

 

 

같은 방식으로 MemoryMemberRepository도 @Repository 어노테이션을 추가한다.

 

package hdxian.hdxianspring.repository;

import hdxian.hdxianspring.domain.Member;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class MemoryMemberRepository implements MemberRepository {
	...
	...
}

 

 

컴포넌트 스캔

이렇게 클래스에 직접 @Controller, @Service, @Repository 어노테이션을 붙여 스프링 빈으로 등록하는 방식을

컴포넌트 스캔이라 한다.

 

사실 @Controller, @Service, @Repository 어노테이션은 @Component 어노테이션의 확장 개념이고,

스프링은 애플리케이션 구동 시 자바 코드들을 스캔해 @Component가 붙은 클래스를 찾아내어 인스턴스를 생성한 뒤,

스프링 빈으로 등록하여 관리한다.

 

특별한 설정이 없는 한, 스프링의 컴포넌트 스캔 범위는 main 메서드가 정의된 클래스의 하위 패키지들이다.

예제에서는 표시된 부분이 컴포넌트 스캔 범위다.

 

 

 

이렇게 모든 클래스(MemberController, MemberSevice, MemoryMemberRepository)를 스프링 빈으로 등록하고 나면,

다음과 같은 의존관계가 완성된다.

세 클래스가 모두 스프링 컨테이너에 스프링 빈으로 등록되어, 스프링 프로그램 내부 어디에서든 해당 클래스는 동일한 하나의 스프링 빈을 공유하게 되는 셈이다.

 

+) 설명에서는 스프링 빈이 싱글톤으로 운영되는 것처럼 설명하였고, 실제로 거의 대부분 싱글톤으로 등록된다.

하지만 절대적인 것은 아니고, 특별한 경우 설정을 바꿀 수도 있다.