Spring

스프링 기초3

oyatplum 2024. 1. 28. 15:21

 

스프링 빈을 등록하고 어떻게 의존 관계를  설정하는 지 알아봅시다.

 

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

 

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

기존에 만든 서비스에 화면을 붙이려고 합니다.

즉, 실제로 회원 가입을 하고 회원 가입된 결과를 html로 보여주려고 합니다.

 

그러려면 컨트롤러와 뷰 템플릿이 필요하겠죠?

 


 

우선 멤버 컨트롤러를 만들 것입니다.

멤버 컨트롤러는 멤버 서비스를 통해서 회원 가입하고 데이터를 조회할 수 있어야 합니다.

 

이런 것을 서로 의존 관계가 있다고 표현합니다. (멤버 컨트롤러가 멤버 서비스를 의존한다.)

 

 

controller package에 MemberController를 생성했습니다.

그리고 @Controller 어노테이션을 작성해줍니다. (그러면 자동으로 컨트롤러가 import 됩니다.)

 

 

여기까지 생성하면 크게 달라진 게 없어보이지만 스프링 빈이 생성된 것입니다.

아래 그림을 보며 이해해봅시다.

 

 

mvc와 템플릿 엔진에 대해 알아볼 때 참고했던 자료입니다.

 

스프링이 처음 시작되면 스프링 컨테이너가 생성됩니다.

거기에 @Controller라는 어노테이션이 있으면 해당 객체를 생성해서 스프링에 넣어두고 관리합니다.

 

위의 그림에서 보면 스프링이 처음 시작될 때 helloController에 있는 @Controller 어노테이션을 보고 helloController 객체를 생성하고 관리합니다.

이를 스프링 컨테이너에서 스프링 빈이 관리된다고 표현합니다. (쉽게 저 녹색 helloController를 스프링 빈이라고 생각하면 됩니다.)

 


 

 

MemberController는 MemberService를 통해 작동해야 한다고 했습니다.

 

 

MemberService를 사용할 때 private final MemberService memberService = new MemberService();라고 해도 됩니다.

하지만 new를 사용하지 않았네요. 왜일까요??

 

 

new로 생성하면 컨트롤러마다 각기 다른 MemberSerivce 인스턴스가 생성되므로 의존성 관리의 일관성이 깨질 수 있습니다. (이전 포스팅의 마지막 부분에서 Dependency Injection(di)를 배웠습니다.)

 

 

이렇게 MemberService가 memberRepository를 di하고 있죠?

그렇기 때문에 현재 MemberController에서 MemberService를 new로 직접 생성하면 MemberService가 의존하고 있는 객체나 설정등이 누락될 수 있어 dependency injection 원칙에 어긋나게 됩니다.

 

따라서 MemberService는 여러 인스턴트를 만들 필요 없이 하나만 생성하고 같이 공통으로 쓰면 됩니다.

 


 

 

스프링이 관리를 하게 되면(스프링 빈) 모두 스프링 컨테이너에 등록을 하고 스프링 컨테이너로부터 받아서 사용하도록 해야합니다.

따라서 위와 같이 new를 사용하지 않고 스프링 컨테이너에 등록을 하고 사용해야 합니다.

스프링 컨테이너에는 딱 하나만 등록이 되거든요.

 

따라서 private final MemberService memberService; 로만 작성하고 Constructor를 작성해줍니다. (command + n )

이때 중요한 것은 생성자 위에 @Autowired 어노테이션을 작성해줍니다.

 

스프링 컨테이너가 뜰 때 멤버 컨트롤러를 생성한다고 했습니다.(@Controller) 이때 생성자를 호출하게 되는데 @Autowired라고 하면 스프링이 스프링 컨테이너에 있는 MemberService를 가져다가 연결시켜줍니다.

 

 

 

하지만 오류가 뜹니다.

왜일까요?

 

MemberService는 그저 순수 자바 클래스이기 때문입니다.

따라서 

 

 

위와 같이 MemberService에는 @Service 어노테이션을, MemoryMemberRepository에는 @Repository 어노테이션을 작성합니다.

 


 

 

정리를 해보자면

@Controller @Service @Repository를 통해 각각 스프링 컨테이너에 스프링 빈으로 등록이 되었습니다.

 

먼저 MemberController와 MemberService를 연결시켜야 하죠?

@Autowired를 constructor에 쓰면 MemberController가 생성될 때 스프링 컨테이너에 등록되어 있는 MemberService 객체를 가져다가 연결해줍니다.(di 의존 관계 주입)

 

MemberService 역시 마찬가지로 @Autowired를 통해 MemberService가 생성될 때 스프링 컨테이너에 등록되어 있는 MemberRepository 객체를 연결해줍니다.

 

이렇게 하면 모두 연결이 됩니다.

 

 


 

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

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

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

 

 

1. @Controller @Service @Repository 로 설정하는 것이 컴포넌트 스캔 방식입니다.

저 셋 어노테이션에 들어가면 @Component 어노테이션이 포함되어 있습니다.

따라서 스프링이 컴포넌트와 관련된 어노테이션이 있으면 스프링 빈으로 자동 등록되며 모두 스프링 객체를 하나씩 생성해서 스프링 컨테이너에 등록을 합니다.

그리고 @Autowired를 통해 각각을 연결하기 때문에 컴포넌트 스캔과 자동 의존 관계 설정 방법입니다.

 

(예를 들어 MemberService가 아니라 OrderService를 새로 생성해서 memberRepository를 @Autowired로 연결하면 MemberService에 연결된 동일한 인스턴스가 연결됩니다.)

 

 


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

 

컨트롤러를 제외한 @Service, @Autowired, @Repository를 지우고 실행하면

 

당연히 컴포넌트 스캔이 되지 않기 때문에 MemberSerivce를 찾을 수 없다는 오류가 발생합니다.

 

 

 

이제 컴포넌트 스캔을 통해 자동으로 스프링 빈을 생성하지 않고 직접 스프링 빈을 등록해봅시다.

 

SpringConfig 클래스를 생성하고 @Configuration 어노테이션을 작성해줍니다.

스프링은 @Configuration 어노테이션을 읽으면 해당 클래스를 스프링 빈에 등록하라는 뜻으로 인식합니다.

 

그러면서 @Bean 어노테이션을 통해 아래 memberService 로직을 호출해서 스프링 빈에 등록합니다.

 

 

이때 생성자에 파라미터를 넣어주지 않아 빨간 줄이 뜹니다.

 

memberRepository 파라미터를 넣으라고 뜨네요. (command + p)

 

역시 @Bean 으로 memberRepository를 등록합니다.

return에는 MemoryMemberRepository를 작성합니다. MemberRepository는 인터페이스니까요.

 

그리고 MemberService의 파라미터에 memberRepository()를 넣어줍니다.

 

 

이렇게 작성하면

MemberService와 MemberRepository를 둘 다 스프링 빈에 등록합니다.

그리고 스프링 빈에 등록되어 있는 memberRepository를 MemberService에 넣어줍니다. (Autowired처럼)

컨트롤러는 어쩔 수 없이 컴포넌트 스캔 방식으로 작성해야 합니다.

 

이렇게 하면 MemberController, MemberService, MemberRepository 모두 연결이 됩니다.

 

 


 

 

두 가지 방식 모두 장단점이 있습니다.

 

실무에서 주로 정형화된 컨트롤러, 서비스, 레포지토리 같은 코드는 컴포넌트 스캔을 사용합니다.

반면 정형화되지 않거나 상황에 따라 구현 클래스를 변경해야 하는 경우에는 직접 스프링 빈에 등록을 합니다.

 

이게 무슨 소리일까요.

 

 

처음에 db가 정해지지 않았기 때문에 memory로 우선 만들고 교체하기로 했었습니다.

그래서 인터페이스를 설계하고 구현체로 memory를 쓰게 된 상황입니다.

그런데 나중에 이 memory레포지토리를 다른 레포로 변경하게 되는 경우가 중요합니다.

 

 

 

직접 스프링 빈에 등록한 경우에는 모든 코드를 수정할 필요 없이 한 번에 교체할 수 있습니다.

 

이렇게 요 부분만 싹 바꿔주면 끝입니다.

 

 

또한 주의할 점은 @Autowired를 사용할 때 해당 객체가 스프링 빈에 등록되어 있지 않으면 안 먹힙니다.

 

 


 

command + p : 파라미터 정보

 

'Spring' 카테고리의 다른 글

스프링 기초4  (0) 2024.02.01
스프링 기초2  (2) 2024.01.27
스프링 기초1  (1) 2024.01.26