스프링 빈 등록 방법

guswls·2023년 1월 17일
2

스프링 입문

목록 보기
6/13
post-thumbnail

이 포스트는 김영한 이사님의 스프링 입문 강의를 듣고 작성하였습니다.

이번에는 우리가 만든 도메인, 서비스, 리포지토리 를 토대로 화면을 통해 회원가입을 하고 회원을 조회할 수 있도록 만들고자 한다. 그러려면 컨트롤러서비스를 통해 해당 기능을 제공할 수 있어야 한다. 이에 대한 표현을 다음과 같이 쓸 수 있다.

memberControllermemberService를 의존한다.

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

package memberpractice.memberpractice.controller;

import org.springframework.stereotype.Controller;

@Controller
public class memberController {

}

먼저 memberController클래스를 만든 후에 @Controller 어노테이션을 붙이면 스프링이 실행될 때 생성된 스프링 컨테이너안에 memberController객체를 생성해서 안에 넣어두게 된다.

이렇게 해서 스프링이 그 컨트롤러를 관리해주는 것이다.
이러한 과정을 다음과 같이 말할 수 있다.

스프링 컨테이너에서 스프링 빈이 관리된다.

package memberpractice.memberpractice.controller;

import memberpractice.memberpractice.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;
    }
}

우선 우리가 memberController에서 memberService를 가져오려면 다음과 같이 하면 된다.

new를 통해 객체를 생성하여 사용할 수도 있지만, 스프링이 관리를 하게 되면 스프링 컨테이너에 등록을 하고 스프링 컨테이너에서 받아서 쓰도록 관리를 해야 한다.

따라서 @Autowired어노테이션이 붙어있는 생성자를 활용하면 memberController가 실행될 때 스프링 컨테이너에 등록된 memberService를 가져올 수 있게 된다. 이를 Dependency Injection이라고 한다.

우리가 앞서 memberService를 테스트할 때 생성자를 통해 DI를 해준 것 처럼 여기서는 스프링이 memberControllermemberService에 대한 의존 관계를 주입하여 준 것이다.

참고

참고로 스프링 빈스프링 컨테이너에 등록을 하게 되면 딱 하나만 등록이 된다.

하지만 이렇게만 한 후에 실행을 해보면 다음과 같은 화면이 뜰 것이다. 왜냐하면 memberController의 경우 @Controller어노테이션을 통해 스프링 컨테이너에 등록이 된 상태지만 그 외에 memberServicememoryMemberRepository의 경우는 순수한 자바코드이기 때문에 스프링에 등록되어있지 않다.

따라서 이들 클래스들도 각각 어노테이션을 통해 스프링 컨테이너에 등록을 해주어야 한다.

//MemberService.java
@Service
public class MemberService {
    @Autowired
    public MemberService(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
        		.
            	.
            	.
    }
}

memberService의 경우 memoryMemberRepository를 들고 와야되기 때문에 역시 생성자에 @Autowired 어노테이션을 붙여야 한다.

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

그리고 memberRepository의 구현체인 memoryMemberRepository에도 @Repository를 붙여주어야 한다.

즉, controller를 통해서 외부요청을 받고 service에서 비지니스 로직을 만들고 repository에서 데이터를 저장하는 이 과정은 앞서 다뤘듯 정형화된 패턴이기 때문에 이렇게 우리가 스프링을 통해서 구현을 할 수 있다.
이렇게 의존 관계를 주입한 후에 서버를 실행하면 정상적으로 스프링 컨테이너가 실행된 것을 확인할 수 있다.
스프링 컨테이너에 등록된 스프링 빈의 의존관계는 다음과 같다.

이렇게 어노테이션을 통해 스프링 컨테이너스프링 빈을 등록하는 것을 컴포넌트 스캔이라고 한다.
위의 사진을 보면 @Controller@Component 어노테이션이 붙어있는 것을 확인 할 수 있다. 사실 @Service@Repository도 모두 컴포넌트의 특수화된 형태이고 이것들은 모두 컴포넌트이다.

참고

스프링이 컴포넌트 스캔을 수행하는 범위는 아래와 같이 main메소드가 존재하는 패키지 안에 대해서만 수행을 하게 된다.

컴포넌트 스캔에 관해서 간단히 정리하자면 다음과 같다.

정리

@Compnent 혹은 @Service@Repository 어노테이션을 통해 자동으로 스프링 컨테이너에 스프링 빈을 등록할 수 있다.


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

이번에는 우리가 하나하나 직접 스프링 빈을 등록하는 방법에 대해 알아보고자 한다. 전에 사용했던 memberController이외에 컴포넌트 스캔 관련 어노테이션과 @Autowired어노테이션은 떼고 진행한다.

package memberpractice.memberpractice;

import memberpractice.memberpractice.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();
    }
}

위와 같이 클래스를 하나 생성한 후에 @Configuration어노테이션을 붙이면 설정파일처럼 클래스를 사용할 수 있다.

이때 우리가 등록하고자 하는 클래스에 대해 @Bean어노테이션을 붙여서 메소드로 MemberService를 생성해주면 스프링이 실행될 때 @Configuration이 붙어 있는 파일을 읽어와서 MemberService스프링 빈으로 등록해준다.

하지만 이때 우리는 MemberService에서 DI를 통해 memoryMemberRepository를 가져오도록 구현을 했기 때문에 다음과 같이 코드를 추가해주어야 한다.

 package memberpractice.memberpractice;

import memberpractice.memberpractice.repository.MemberRepository;
import memberpractice.memberpractice.repository.MemoryMemberRepository;
import memberpractice.memberpractice.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();
    }
}

이렇게 MembserService생성자의 인자로 memberRepository()를 넣으면 스프링 빈으로 등록된 memoryMemberRepositoryDI해주게 되어 앞선 예제와 똑같은 의존관계가 완성된다.

참고

컨트롤러의 경우는 어차피 스프링이 관리를 받게 되므로 @Controller 어노테이션과 @Autowired 어노테이션을 사용하여 스프링 빈으로 등록하고 사용하여야 한다.


3. 주의 사항

1. DI의 방법

우리는 지금까지 생성자를 통한 DI를 하였지만 실제로는 3가지의 DI방법이 존재한다.

  1. 생성자 주입
  2. 필드 주입
  3. Setter 주입

이 3개중에 권장되는 방법은 생성자 주입이다. 왜냐하면 클래스 간 의존 관계는 프로그램 동작 중에 변경될 일은 존재하지 않아 처음 객체가 생성될 때 바로 세팅하는 것이 좋기 때문이다.

각각의 특징과 장단점에 대해서는 이 글을 참고하자.

2. Configuration을 통한 스프링 빈 등록 이유?

실제 실무에서는 정형화된 ServiceRepository와 같은 것들은 컴포넌트 스캔방식을 사용한다. 하지만 만약 상황에 따라 스프링 빈에 등록할 구현 클래스를 변경해야 되는 소요가 발생하는 경우 Configuration에서 관리하면 편하다.

3. Autowired는 스프링 빈에서만 동작한다.

스프링 빈으로 등록되지 않은 클래스에서는 @Autowired를 사용할 수 없다.

4. 총 정리

이번 포스팅에서는 제일 중요한 부분 중 하나라고 할 수 있는 스프링 빈의 등록 방법에 대해서 간단하게 살펴보았다. 결론적으로는 스프링으로 프로젝트를 진행할 땐 웬만해선 스프링 빈으로 등록하여 스프링이 관리하도록 하는 것이 좋다고 한다.

스프링 빈 등록 방법 이외에도 DI를 비롯한 개념들에 대해서도 간략히 다뤘는데 나중에 스프링 기본원리 강의를 들으면서 자세히 공부해 볼 것이다.

profile
안녕하세요

0개의 댓글