Spring/Spring Core Basic

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

JWonK 2022. 1. 6. 14:24
728x90
반응형

지금까지 단순한 객체를 통한 서비스 개발 및 테스트를 통한 검증을 하였는데 이제 서비스 화면을 백엔드 시야에서 어떻게 처리하는지 알아본다.

 

Controller와 ViewTemplate을 이용한다.

 

1. MemberController를 생성할 건데, Controller가 memberService를 통해 서비스가 이루어지고 데이터를 조회할 수 있어야한다. → MemberController와 MemberService의 의존관계 설정

 

-> 이것을 Spring을 통해 구현

 

가장 먼저 MemberController를 구현할건데

package Hello.HelloBus.controller;

import Hello.HelloBus.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;
    }
}

위와 같이 구현할 수 있다.

 

가장 먼저 @Controller 어노테이션을 통해 스프링 컨테이너에 MemberController를 등록해준다. 그리고 MemberService를 이용하기 위해 받아주어야하는데 전 게시물에서 사용했던 것처럼 new를 통해 새로운 MemberService를 생성하는 것이 아닌 생성자를 통해 주입 받았다. 

 

이는 스프링을 이용하게 되면 모두 스프링에 객체를 저장하고 사용하고자 하는 것은 스프링 컨테이너로부터 받아서 사용해야하기 때문이다.

 

@Autowired 어노테이션은 뭘까?

일단 위에서 말한 것처럼 MemberController는 @Controller 어노테이션을 통해 스프링 컨테이너에 등록을 해준다. 그럼 그 때 생성자를 호출하는데 생성자에 @Autowired가 있으면 생성자 매개변수 MemberService를 스프링이 스프링 컨테이너에 있는 MemberService와 연결해준다. 

 

그럼 위 말은 스프링 컨테이너에 MemberService가 등록이 되어있다는 뜻인데 전 게시물에서 MemberService와 MemberRepository를 스프링 컨테이너에 등록해준 적이 없다. 따라서 위와 같이 MemberController만 스프링 컨테이너에 등록시켜주고 위와 같이 사용하려하면 당연히 제대로 이루어질 수 없다.

 

MemberService와 MemberRepository를 스프링 컨테이너에 등록 시켜준 후 사용해야한다.

package Hello.HelloBus.service;

import Hello.HelloBus.domain.Member;
import Hello.HelloBus.repository.MemberRepository;
import Hello.HelloBus.repository.MemoryMemberRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class MemberService {
    private final MemberRepository memberRepository;

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

    /* 회원가입 서비스 */
    public Long join(Member member){
        // 같은 이름이 존재하는 중복 회원 생성 x
        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }

    private void validateDuplicateMember(Member member) {
        memberRepository.findByName(member.getName())
                .ifPresent(m->{
                    throw new IllegalStateException("이미 존재하는 회원입니다.");
                });
    }

    /* 회원 조회 서비스 */
    public List<Member> findMembers(){
        return memberRepository.findAll();
    }

    public Optional<Member> findOne(Long memberId){
        return memberRepository.findById(memberId);
    }
}

 


 

 

package Hello.HelloBus.repository;

import Hello.HelloBus.domain.Member;
import org.springframework.stereotype.Repository;

import java.util.*;

@Repository
public class MemoryMemberRepository implements  MemberRepository{

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    @Override
    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional<Member> findById(Long id) {
        // id로 얻는 것인데 만약 존재하지 않는 경우 Null을 반환
        // 따라서 Optional로 반환
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<Member> findByName(String name) {
        return store.values().stream()
                .filter(member->member.getName().equals(name))
                .findAny();
    }

    @Override
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }

    public void clearStore(){
        store.clear();
    }
}

위 처럼 MemberService Class에는 @Service 어노테이션을,

MemberRepository에는 @Repository,

MemberController에는 @Controller를 통해 스프링 컨테이너에 등록을 해준 후 사용을 한다. 이는 현재 실무의 표준이라고 말할 수 있을 정도로 정형화되어있다고 한다.

 

위 처럼 3개의 객체가 모두 스프링 컨테이너에 등록이 되어있다. 이를 서로 연결시켜주는 것이

아까 처음에 말한 @Autowired 어노테이션이다. 그리고 이것이 Dependancy Injection 방식이다.

(각 클래스에서 다른 생성자를 호출 할 경우 무조건 @Autowired를 통해 스프링으로부터 받아야한다)

 

이 방법을 "컴포넌트 스캔과 자동 의존관계 설정"   이라고 표현한다. 우리가 사용한건 @Service, @Controller, @Repository인데 왜 컴포넌트라 하냐? 3가지 어노테이션 모두 컴포넌트 어노테이션을 포함하고 있기 때문이다. 

 

그리고 컴포넌트 스캔과 자동 의존관계 설정을 허용하는 범위는 우리가 사용하고자 하는 main 클래스의 package만 포함한다. 

 

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

728x90
반응형