[Spring Boot] [2] 4. 스프링 빈과 의존 관계

윤경·2021년 8월 7일
2

Spring Boot

목록 보기
13/79
post-thumbnail

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

멤버 서비스, 레퍼지토리, 서비스를 통해 멤버 가입, 레퍼지토리에 저장, 레퍼지토리에서 꺼내오고..
화면을 붙이려면 컨트롤러, 뷰 템플릿이 필요

멤버 컨트롤러가 필요한데 멤버 컨트롤러가 멤버 서비스를 통해 가입, 데이터 조회할 수 있어야 함.
➡️ 이것을 서로 의존관계가 있다. 라고 함. ("멤버 컨트롤러가 멤버 서비스를 의존한다.")

즉, 목표: 회원 컨트롤러가 회원 서비스와 회원 레퍼지토리를 사용할 수 있게 의존 관계를 준비

실습

우선, 멤버 컨트롤러를 생성하자
✔️ src/main/java/com.example.hellospring/controller 에 MemberController.java 생성

@Controller
public class MemberController {
}

⬆️ 이렇게 @Controller 작성

여기까지 해놓으면 스프링 컨테이너가 스프링 처음에 뜰 때 스프링 컨네이너란 통이 생성.
Controller 어노테이션에 있으면 MemberController 객체를 생성해 통에 넣음.(스프링이 관리)

그래서 예전에 HelloController에서 작성했던 것처럼 기능들이 작동할 수 있었던 것.

📌 즉, 이제 위에 작성한 코드는 스프링 컨테이너가 관리를 한다는 것.

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

⬆️ 이제 얘가 멤버 서비스를 가져다 쓸 수 있어야 하는데
위처럼 new로 생성해 쓸 수도 있지만
스프링이 관리를 하게되면 다 스프링 컨테이너에 등록을 하게되고 스프링 컨테이너로부터 받아서 쓰도록 바꿔줘야 함.
그리고 new로 생성하게 되면 문제점이 멤버 컨트롤러 이외에도 많은 컨트롤러들이 가져다 쓸 수 있게 되어버림.

⬇️ 이렇게 쓰지말고 스프링 컨테이너한테 등록을 하고 쓰자.

    private final MemberService memberService;

    // 생성자로 연결해주기 (generate -> constructor)
    // @Autowired를 쓰면 스프링 컨테이너와 이 생성자를 딱 연결시켜줌.(스프링 컨테이너에서 멤버 서비스를 가져옴)
    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
  • 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어줌. 이렇게 객체 의존 관계를 외부에서 넣어주는 것을 DI(Dependency Injection), 의존성 주입이라 함.
  • 이전 테스트에서는 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입

여기까지 해놓고 실행시키면 오류 발생
➡️ Consider defining a bean of type 'hello.hellospring.service.MemberService' in your configuration.

⬆️ 이는 hellocontroller는 스프링이 딱 뜰 때 스프링 컨테이너에 딱 등록이 됨. 하지만 위의 사진처럼 memberService가 스프링 빈으로 등록되어 있지 않아 생기는 문제.

📌 (참고) helloController는 스프링이 제공하는 컨트롤러여서 스프링 빈이 자동 등록.(@Controller가 있으면 자동 등록)

MemberService를 보면 순수한 자바 클래스임. 즉, 스프링이 얘를 알 수 있는 방법이 없음.
MemberController는 @Controller라는 어노테이션이라도 걸어놓으니까 알 수 있지만 (😦 스프링: "아 이것은 내가 관리하는 애구나 내가 생성을 해야겠다")

‼️ 그래서 해주어야 할 것은 ⬇️ 아래와 같이 MemberService에 @Service를 붙여주는 것 ➡️ 이는 스프링이 올라올 때 얘를 😦 어 서비스네 하고 스프링 컨테이너에 멤버 서비스를 등록하게 됨.

📌 (참고): 생성자에 @Autowired를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입. 생성자가 1개만 있으면 @Autowired는 생략 가능.

그리고 레퍼지토리로 이동, ⬇️ 아래와 같이 어노테이션 추가

📌 결국 이는 컨트롤러를 통해 외부 요청을 받고, 서비스에서 비즈니스 로직을 만들고, 레퍼지토리에서 데이터를 저장하고. (➡️ 정형화된 패턴)

⬆️ 스프링이 뜰 때 이렇게 연결되어야 함. (memberService와 memberRepository가 스프링 컨테이너에 스프링 빈으로 등록되었음)
멤버 컨트롤러가 등록될 때 스프링 빈에 있는 멤버 서비스 객체를 넣어줌.(의존 관계 주입. DI)

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

⬇️ 지금까지의 결과 잘 실행되는 것을 확인. (컨테이너에 문제 없음.) 하지만 아직 아무 기능도 없는 상태.

스프링 빈을 등록하는 두 가지 방법

  • 컴포넌트 스캔과 자동 의존관계 설정 (이번 시간에 활용한 방법)
  • 자바 코드로 직접 스프링 빈 등록

‼️ 두 가지 방법을 모두 알아야 함.

컴포넌트 스캔 원리

  • @Conponent 어노테이션이 있으면 스프링 빈으로 자동 등록됨
  • @Controller 컨트롤러가 스프링 빈으로 자동 등록된 이유도 컴포넌트 스캔 때문

@component 를 포함하는 다음 어노테이션도 스프링 빈으로 자동 등록됨. (아래의 어노테이션들이 component 어노테이션을 포함했다는 것)

  • @Controller
  • @Service
  • @Repository

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

먼저 1️⃣에서 했던 어노테이션 삭제하기.

✔️ MemoryMemberRepository.java와 MemberService.java에서 @Repository와 @Service, @Autowired 삭제(주석처리)
이때, MemberController는 그대로 두기

✔️ 그리고 src/java/com.example.hellospring에 SpringConfig.java 생성

package com.example.hellospring;

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

}

➡️ 여기까지 하면 아래와 같은 사진 관계가 완성됨.


📌 참고

📌 (참고): XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략.

📌 (참고): DI에는 필드 주입, setter 주입, 생성자 주입 (⬅️ 이 방법을 추천) 이렇게 3가지 방법이 있음. 의존 관계가 실행 중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장. (실제로는 거의가 아니라 아예 없음.)

📌 (참고): 실무에서는 주로 정형화된 컨트롤러, 서비스, 레퍼지토리 같은 코드는 컴포넌트 스캔을 사용. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록.
(여기서 우리는 데이터 저장소가 선정되지 않았다고 가정했으므로 우선 MemoryMemberRepository를 인터페이스로 해놨었다. 추후 다른 코드를 수정하지 않고 저장소를 변경하려고 한.)

‼️ (주의): @Autowired를 통한 DI는 helloController, memberService 등과 같이 스프링이 관리하는 객체에서만 동작. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작 ❌


어렵다,,😶🔨

profile
개발 바보 이사 중

0개의 댓글