[ Spring ] 스프링 빈과 의존관계(DI)




Dependencies Injection


지금까지 MemberService와 MemberRepository를 만들었다.

이제 회원가입을 할때 Controller와 View Template이 필요하다.

그런데, Controller는 Service를 통해서 회원가입하고, 데이터를 조회하는 등 로직을 처리한다.

이런것을 보고 ‘서로 의존관계에 있다’고 표현한다.

더 구체적으로는 ‘controller가 service에 의존한다.’ 라고 얘기한다.



그럼 스프링답게 작성해보자

package spring.spring.controller;

import org.springframework.stereotype.Controller;

@Controller
public class MemberController {
    
}

스프링이 뜰때, 스프링 컨테이너라는 통이 생기는데, 거기에 @Controller라는 어노테이션이 있으면 클래스 이름( =MemberController )에 해당하는 객체를 생성해서 가지고 있는다. 그리고 스프링이 관리를 한다.


이런과정을 스프링 컨테이너에 스프링 빈이 관리된다 라고 표현한다.

그러니까 스프링 빈은 MemberController 라고 생각하면 되고, 이제부터 MemberController안에 있는 모든 내용은 스프링 컨테이너가 관리한다.

만약 DI가 없다면 컨트롤러에서는 service를 이런식으로 호출 할 수 있다. (추천하지 ❌ 코드)

package spring.spring.controller;

import org.springframework.stereotype.Controller;
import spring.spring.service.MemberService;

@Controller
public class MemberController {
    private final MemberService memberService = new MemberService();
}

근데 스프링이 관리를 하게 되면(= @Controller 어노테이션이 있으면 ) 다 컨테이너에 등록을 하게 되고, 스프링 컨테이너에 한번 등록이 되면 스프링 컨테이너에서 받아쓰도록 바꿔야한다.

MemberServiceMemberController 이외에도 여러 다른 controller에서 호출을 해서 사용할텐데,

이렇게 new로 새 인스턴스를 만들어내게 되면 서로 다른 service들을 만들어서 사용한다는 뜻인데,

사실 그럴 필요가 없다. 그냥 공용으로 만들어놓고 그걸 가져다가 쓰면 된다.

그렇기 위해서는 스프링 컨테이너에 등록을 하고 사용 하면 된다.



그래서 위의 코드보다 아래의 코드처럼 작성하는게 좋다. (추천 ⭕️ 코드)

스프링 컨테이너에는 딱 하나만 등록되기 때문에 여러가지 부가효과를 누릴 수 있음

package spring.spring.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import spring.spring.service.MemberService;

@Controller
public class MemberController {
    private final MemberService memberService;

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

근데 저렇게 service를 연결해줘서 memberService 을 찾을수없다고 에러가 나는데, 그건 아무 어노테이션이 붙지않은 순수 자바클래스라서 이게 뭔지 스프링 컨테이너에서 접근을 할 수 없어서 에러가 난다.

그러므로 @Service 어노테이션을 붙여줘야함. @Repository도 똑같음.

클래스명이 MemberController, 어노테이션이 @Controller

클래스명이 MemberService, 어노테이션이 @Service

클래스명이 MemoryMemberRepository, 어노테이션이 @Repository 이런식으로 같은 단어를 쓰는게 정형화된 패턴이다.


controller에서 외부요청을 받고

service에서 비즈니스로직을 처리하고,

repository에서 저장을 하는게 사실은 가장 정형화된 패턴이다.



DI 등록의 3가지 방법


  • 필드 주입
    public class MemberController {
        @Autowired private MemberService memberService;
    }
    
    • 좋지 않음 (추천 안함)
    • 스프링이 뜰때만 넣어주기 때문에 중간에 수정을 할 수가 없음



  • setter 주입

    public class MemberController {
    	private MemberService memberService;
      	
    	@Autowired
    	public void setMemberService(MemberService memberService){
    		this.memberService = memberService;
    	}
    }
    
    • Cmd + n 로 setter를 하나 만들어주고 거기에 @Autowired 넣어주면 setter 주입 방식이다.
    • 누군가가 MemberController를 호출했을때 public으로 열려있어야 함. (= 호출되면 안될때 호출이 됨)



  • 생성자 주입
    public class MemberController {
        private final MemberService memberService;
      
        @Autowired
        public MemberController(MemberService memberService) {
            this.memberService = memberService;
        }
    }
    
    • 가장 추천하는 방법









생성자 사이의 연결, @Autowired


생성자(cmd + n 으로 contructor 생성) 위에 @Autowired 어노테이션을 작성해준다.

MemberController 컨트롤러가 뜰때 생성자에 @Autowired 가 붙어있으면 스프링 컨테이너에 있는 memberService 와 생성자 memberService 를 연결해준다.

//MemberController.java

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

MemberController.java 에 contructor로 연결된 MemberService 를 따라가보면

public MemberService(MemberRepository memberRepository){
    this.memberRepository = memberRepository;
}

이렇게 repository와 연결되어있는데 controller에서 service와 연결한것처럼

service에서도 repository와 연결하려면 @Autowired 를 아래처럼 넣어준다.

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



그럼 최종적으로 controller에서는 service를, service에서는 repository를 연결한 모양이 나온다.



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


  • 컴포넌트 스캔과 자동 의존관계 설정
  • 자바 코드로 직접 스프링 빈 등록

실무에서는 주로 정형화된 컨트롤러, 서비스, 레포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.

이유는 구현 클래스를 변경해야 할때 설정(=config)을 통해 빈으로 등록하면 코드를 수정할 일 없이 설정파일만 변경하면 되는데, 만약 컴포넌트 스캔 방식으로 하면 여러파일의 코드를 수정해야하기 때문에 정형화 되지 않거나 아직 디비 설정이 끝나지 않았거나 하는 등의 상황에는 설정을 통해 스프링 빈으로 등록한다.



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

@Controller, @Service , @Repository 어노테이션을 붙여주는 방법

@Component 어노테이션이 있으면 스프링 빈으로 자동 등록된다. @Controller, @Service , @Repository@Component를 포함하기 때문에 스프링 빈으로 자동 등록된다.

스프링은 컴포넌트 관련 어노테이션이 존재하면 객체로 하나씩 생성을 해서 스프링 컨테이너에 등록(=싱글톤)을 해놓는다.

싱글톤이란? 유일하게 하나만 등록해서 공유하는 것. 따라서 같은 스프링 빈이면 모두 같은 인스턴스이다.

@Autowired 는 연관관계를 설정하는 것이다.



자바 코드로 직접 스프링 빈 등록

main함수가 실행되는 파일과 같은 레벨에서 SpringConfig 라는 class를 하나 만든다.

package spring.spring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import spring.spring.repository.MemberRepository;
import spring.spring.repository.MemoryMemberRepository;
import spring.spring.service.MemberService;

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

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

@Bean 어노테이션으로 직접 스프링 빈으로 등록해준다.













김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 수업들으면서 정리!




© 2018. by sora

Powered by sora