카테고리 없음

스프링 입문-4 스프링 빈과 의존관계

남승현 2023. 11. 1. 01:16

여태까지 구현한 것들을 바탕으로 화면을 만들고 싶다면 member controller를 만들어야 한다. 

이 member controller는 member service를 통해 회원가입하고 member service를 통해 데이터를 조회할 수 있어야한다

-> "controller가 member service를 의존한다" 라고 표현한다

 

스프링 창이 뜰때 스프링 컨테이너라는 통이 생기는데 아래와 같이 @Controller를 적어두면 MemberController 객체를 생성해서 스프링에 넣어두고 관리한다.

-> "스프링 컨테이너에서 스프링 빈이 관리된다" 라고 표현한다

package com.example.demo.controller;

import org.springframework.stereotype.Controller;

@Controller
public class MemberController {
    
}

스프링이 관리하게 되면 모두 스프링 컨테이너에 등록하고 스프링 컨테이너에서 받아서 쓰도록 바꾸어야 한다.

생성자에 @Autowired라고 되어있으면 memberService를 스프링이 스프링 컨테이너에 있는 memberService를 가져다가 연결시켜준다.

package com.example.demo.controller;

import com.example.demo.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;
    }

}

but 실행시키면 MemberService라는 빈이없다고 오류 뜬다! 이는 MemberService가 순수 자바코드라서 그렇다.

순수한 자바 코드(@controller와 같은 annotation이 없는 형태) 는 스프링이 아무것도 하지 못한다.

따라서 @Service 써서 annotation 써주기->스프링이 "service네" 하면서 memberService를 컨테이너에 등록해줌

MemoryMemberRepository도 마찬가지로 @Repository라고 annotation 달아주기

정형화 된 패턴

-> controller에서 외부 요청을 받고 service에서 비즈니스 로직을 만들고 repository에서 데이터 저장

controller를 service에 연결시켜주어야한다. 연결시켜줄 때 @Autowired 쓴다.

-> MemberController가 생성될 때 스프링 빈에 등록되어 있는 memberService 객체를 받아 넣어준다.

   -> Dependency Injection (의존 관계 주입)

 

@Service
public class MemberService {
    //member service를 만드려면 member repository가 필요함. 따라서 얘부터 만들기
    private final MemberRepository memberRepository;

    @Autowired
    public MemberService(MemberRepository memberRepository){
        this.memberRepository=memberRepository;
    }  //외부에서 repository 넣어주게 바꿈

이렇게 쓰면 스프링이 @Service를 보고 "어 서비스네?"하면서 스프링 컨테이너에 등록하며 생성자를 호출한다. 이때 @Autowired가 붙어 있으면 "너는 memberRepository가 필요하구나" 하면서 스프링 컨테이너에 있는 memberRepository를 넣어준다. 여기서는 MemoryMemberRepository memberRepository의 구현체로 있으므로 MemoryMemberRepository를 service에 주입해준다. 즉, @Autowired는 연결해주는 역

 

이상태로 이제 main인 DemoApplication을 실행시켜주면 Tomcat started on port(s): 8080 (http) with context path '' 가 뜨면서 잘 실행됨을 알 수 있다.

 

 

우리가 여태까지 뭘 한걸까? 스프링 빈 등록하는 방법을 배운 것이다.

이쯤에서  스프링 빈을 등록하는 2가지 방법을 짚고 넘어가보자.

 

1. 컴포넌트 스캔과 자동 의존관계 설정 (여태까지 위에서 한 방법!)

우리가 지금 DemoApplication을 실행시키고 있다. 이 파일에 있는 com.example.demo package포함해서 이 패키지의 하위들은 스프링이 모두 뒤져서 스프링 빈으로 등록한다.

하지만 하위 패키지가 이거랑 동일하거나 하위 패키지가 아닌 애들은 스프링 빈으로 컴포넌트 스캔하지 않아 등록하지 않는다.

 

cf) 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때 기본적으로 싱글톤으로 등록한다(유일하게 하나만 등록해 공유한다)

 -> memberService는 memberService하나만, helloController는 helloController하나만 이런식으로

따라서 orderService가 autowired라고 해서 memberRepository내놓으라고 하면 이미 등록된 memberRepository를 내놓는다.

 

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

직접 등록해달라고 코드 작성하는 방법

-> 앞서 작성했던 MemoryMemberRepository에서 @Repository, MemberService에 있던 @Service, @Autowired 지운다

+ SpringConfig class 새로 만들어주기

package com.example.demo;

import com.example.demo.repository.MemberRepository;
import com.example.demo.repository.MemoryMemberRepository;
import com.example.demo.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

    @Bean  //MemberService를 스프링 빈에등록 + 스프링 빈에 등록된 repository를 넣어준다(autowired 역할)
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean   //Repository를 스프링 빈에등록 (MemberRepository는 인터페이스, MemoryMemberRepository가 구현체
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
}

 

But @Controller는 스프링에서 작동되는 거기에 그대로 @Controller 납둔다

 

cf) DI 작성법

1. 생성자 주입 -> 가장 권장되는 방법! (의존관계가 실행중에 변는 경우가 아에 없으므로)

2. 필드 주입 ( 별로다! 바꿀 수가 없어서 )

3. setter 주입

-> setter 한 다음 autowired 넣어줌

-> 누군가 MemberController 호출할 때 public 으로 열려있어야 한다. 따라서 MemberService가 한번 세팅되면 바꿀 필요가 없는데도 public이라 노출되어 있다.

-> 개발은 생성시점에 만들어 놓고서 변경 못하도록 막아버리는 게 중요하다

 

cf) 주의할 점! @Autowired를 통한 DI는 'helloController', 'MemberService'와 같이 스프링이 관리하는 객체에서만 동작한다.

-> 스프링 빈으로 등록하지 않고 내가 직접 작성한 객체에 대해서는 동작하지 않는다

 

 

1방법( 컴포넌트 스캔과 자동 의존관계 설정 )과 2방법( 자바코드로 직접 스프링 빈 등록하기 )모두 장단점이 있어 둘다 알아놔야한다.

-> 실무에서 정형화된 컨트롤러, 서비스, 레포지토리와 같은 코드는 컴포넌트 스캔 방법 사용

-> 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야하면 설정을 통해 스프링 빈으로 등록

여기서 MemoryMemberRepository를 나중에 실제db와 연결되는 애로 바꿀 때 코드를 아예 바꾸지 않으면서 repository 바꿔치기 할 수 있는 방법이 2번 방법이다.

현재 이렇게 작성되어 있는 코드를

이렇게 DbMemberRepository라고 이름 바꿔주기만 하면 된다.

 

 

 

https://m42-orion.tistory.com/87

 

Spring 입문 Chapter 4-2. 스프링 빈과 의존관계 : 자바 코드로 직접 스프링 빈 등록하기

이 글은 인프런에 있는 김영한님의 "스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술" 강의를 듣고 정리한 필기입니다. ⛅️ 코드로 직접 등록하기 전 상태 되돌리기 이전 시간에는

m42-orion.tistory.com