본문 바로가기

Backend/Spring

[Spring] Spring Bean1

728x90

 

  • 지금까지 Project Review
  • Spring Bean이 무엇인가?
  • Bean으로 만들어야 하는 객체들
  • Bean등록하는 방법 2가지
    • 컴포넌트 스캔과 자동 의존관계 설정
    • 자바 코드로 직접 스프링 빈 등록하기

 

 

Project Review

지금까지 

백엔드 단에서 "회원가입" "회원조회"기능을 구현하기 위해

 

Domain : 비지니스 도메인 객체_ ex 회원, 쿠폰 등의 객체로 만들 수 있는 것들

Domain내에 Member클래스를 생성해 주었고, field로 식별가능한 ID와 회원이름을 가지도록 설정했다.

 

Repository : 데이터베이스에 접근, 도메인 객체(Member)를 DB에 저장하고 관리

제대로된 DB를 구체적으로 정한 것이 아니므로 향후에 결정되면 편하게 갈아끼울 수 있도록 인터페이스 형식(MemberRepository)으로 먼저 만들어 두었으며, 이를 구현체로 가벼운 메모리 기반으로 설계해두었다. 

Member를 저장할 수 있는 map자료형 store를 만들고,

store에 접근하는 작업 store에 회원추가(회원가입), store에 접근하여 데이터 조회(회원조회) 메소드를 만들었다. 

 

Service : 핵심 비즈니스 로직 구현

핵심 비즈니스 로직인 "회원가입(join)"과 "회원조회(findMember)"하는 메소드를 작성해준다.

**레포지토리에 있는 메소드와 다르다! 레포지토리에 있는 메소드는 DB접근에 대한 메소드이다.

 

을 구현하였다.

 

이제

화면이 필요하다. 회원가입창과 회원가입 결과를 html을 통해 보여주어야 한다. 

그러기 위해서는 "Controler"와 "View Template"이 필요하다. 

 


 

Spring Bean?

먼저 시작하기전에 Spring Bean에 알아보고 넘어가자.

Spring (IoC)컨테이너가 관리하는 자바 객체를 빈(Bean)이라고 한다. 

더보기

IoC란?

 

모든 작업을 사용자가 직접 제어하는 구조가 아닌, 객체 생성을 특별한 주체에게 위임함으로서, 사용자가 객체를 직접 생성하지 않고, 객체의 생명주기를 다른 주체가 컨트롤하게 된다.

사용자의 제어권을 다른 주체에게 넘기는 것 이므로 제어의 역전(IoC)라고 한다. 

 

지금까지는 객체를 생성할 때 다음과 같이 생성하였다.

//특정 클래스 
public class Student{

    private Long id;
    private String name;

}

//객체 생성
private final Student s1 = new Student();

new연산자를 이용하여 객체를 생성했다. 이렇게 생성된 객체는 빈이 아니다. 

특히 이렇게 생성했을 때는 new를 이용해 생성할 때 마다 서로 다른 객체가 되기 때문에, 내용을 공유할 수 없다. 

 

Spring에서는 스프링에 의하여 생성되고 관리되는 하나로 만들어 공유해서 사용할 수 있는 자바 객체를 만들어 Spring Container내부에 넣어두고 사용하는데, 이것이 바로 Bean이다.

스프링 프레임워크에서는 Bean을 얻기 위해 ApplicationContext.getBean()과 같은 메소드를 사용하여 스프링에서 직접 자바 객체를 얻을 수 있다. 

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

 

 

갑자기 이 이야기를 왜 했을까..

당연하다. 앞으로 어떤 객체들은 new가 아닌 빈으로 만들어야 하기 때문일 것이다.

 

우리의 서비스를 다시한번 살펴보자, 우리는 이제 View 와 Controller가 필요하다

 

1) 컨트롤러를 이용해 외부 요청을 받고, 2)서비스를 이용해 내부 로직을 만들고, 3)레포지토리에서 데이터를 저장 (매우 흔하게 정형화된 패턴임)

하나씩 살펴보면, 

Member Controler는 MemberService를 통해서 "회원가입" / "회원조회" 기능을 사용할 수 있다.

이는 위에서 구현한 "Service"를 통해 이루어진다. 

∴ Member Controller가 Member Service를 의존함(의존 관계에 있음)

 

MemberService는 MemberRepository를 이용해 데이터에 접근하여 기능을 수행할 수 있다.

∴ Member Service가 Member Repository를 의존함(의존 관계에 있음)

으로 정리해볼 수 있으며, 그림으로 표현하면 다음과 같다.

 

이런식으로 의존관계에 있으니, new로 객체를 생성하게 되면 전부 다른 객체가 되어버리므로 이를 공유하기 어렵다.

따라서 위의 3개의 객체는 new가 아닌 Spring bean으로 제작되어야 한다. 

 

 

 

 

 

 


Bean등록하는 방법 

 

 

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

 

다음 그림을 만족시키기 위해서

1단계. 우리는 memberController / memberService / memberRepository를 빈으로 만들어야한다.

빈으로 만드는 방법 첫번째는 @Component라는 annotation을 이용하는 것이다.

편리하게도, 컨트롤러, 서비스, 레포지토리라는 특수한 Component가 annotation으로 존재한다. 

 

특정 클래스에 컴포넌트 annotation을 설정하게되면, 

이를 붙이게 되면 스프링 컨테이너가 스프링참에 뜰 때 스프링컨테이너라는 스프링통이 생기게 되고, 거기에 컨트롤 annotation이 잇으면

해당 클래스의 객체를 생성해서 스프링이 관리한다.

 

Controller를 먼저 생성하여, 예를 보여주면 다음과 같다. 

Controller생성 

다음 controller package안에 MemberController클래스를 생성한다. 

 

package hello.hellospring.controller;

import org.springframework.stereotype.Controller;

@Controller
public class MemberController {

}

Controller의 기능을 하기위해 annotation @Controller를 붙여주는데,

이를 붙이게 되면 스프링 컨테이너가 스프링참에 뜰 때 스프링컨테이너라는 스프링통이 생기게 되고, 거기에 컨트롤 annotation이 잇으면

MemberController객체를 생성해서 스프링이 관리한다.

≒ 스프링 컨테이너에서 스프링 빈이 관리된다. ( MemberController가 빈이됨)

 

초록색 원이 Bean을 의미함.

 

마찬가지로 Service와 Member에 대해서도 annotation을 추가한다. 

@Servie @Repository들을 열어 안에 들어가면 @Component라는 annotation이 등록되어있음

(즉, 이들이 특수화된 컴포넌트(@Component)임을 알 수 있다.) 

스프링이 올라올때 컴포넌트와 관련있는 어노테이션이 있으면, 모두 스프링이 객체를 생성해서 스프링 컨테이너에 등록해둠

@Service

 

@Repository

 

1단계를 완료했다. 

각각을 Bean으로 만들었다. 이제 객체를 new로 생성하지 않고, 이전 게시글(using SAME repository)에서 했던 것 처럼 

생성자에서 매개변수를 이용해 받아올 때 항상 같은 객체를 이용할 수 있게 되었다. 

 

2단계. 서로서로의 연관 관계 의존관계를 반영해주는 것이다. 

(의존관계가 어떻게 되는지에 대한 설명은 위쪽을 다시 보고 오도록하자)

Controller는 Servcie에 의존한다. 

이말은 Controller를 생성자를 이용해 생서할 때, 무조건 memberService가 필요하다는 것이고, 매개변수로 넘겨주어 등록하면된다.

이는 다음과 같이 코드를 작성할 수 있다.

이때 연관관계를 반영해주는 것이 @Autowired이다. 

MemberController

package hello.hellospring.controller;

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

@Controller
public class MemberController {

// new로 새로운 객체를 생성하지 않고 하나를 만들어 공유함 -> 등록하여 사용함 (Bean)
//    private final MemberService memberService = new MemberService(); 

    private final MemberService memberService; 

    //생성자 생성
    // 맴버 컨트롤러가 스프링 컨테이너가 뜰 때 생성된다. 
    // 그 때, 생성자를 호출하게되는데 생성자에 autowired가 잇으면 맴버서비스를
    // 스프링이 스프링컨테이너에 있는 맴버서비스에서 가져다가 연결시킴 
    @Autowired
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

 

그렇다면, memberService와 Repository의 연관관계도 다음과 같이 코드를 작성할 수 있다.

memberService

// 순수한 자바코드이기 때문에 스프링 컨테이너가 알 방법이 없음 -> @Service : 스프링이 올라올떄 서비스임을 인식하고, 스프링컨테이너의 맴버서비스에 등록해줌.
@Service
public class MemberService {


    private final MemberRepository memberRepository;

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



//생략

}

 

그럼 연결관계 또한 완성되었다. 

 

정리해 보면 다음과 같다. 

1단계가 annotation으로 component를 스캔하여 등록함 의 방법을 이용한 것이고

2단계가 DI (의존성주입)으로 객체의 의존관계를 외부에서 넣어준 방법을 이용한 것이다. 

 

<component scan의 범위>

또한, 이 컴포넌트 스캔 방식은 기본적으로

HelloSpringApplication에서부터 실행되어 hello.hellospring 패키지의 하위를 스캔하는 것 이기 때문에,

이 하위에 존재하지 않으면, 스캔되지 않는다.

 

 

2번째 방법인 자바코드로 직접 스프링 빈 등록하기는 글이 너무 길어져서 다음게시글에... 

 

최종적으로

  • Bean등록하는 방법 2가지
    • 컴포넌트 스캔과 자동 의존관계 설정
      • annotation으로 component를 스캔하여 등록(@Component (하위 @Service / @Controller / @Repository )
      • DI (의존성주입)으로 객체의 의존관계를 외부에서 넣음 (@Autowired)
    • 자바 코드로 직접 스프링 빈 등록하기

으로 정리해볼 수 있겠다.

728x90