티스토리 뷰

회원 서비스를 구현하는 과정에서 생긴 문제이다.

public class MemberSerivce() {
	
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    
}
class MemberServiceTest {
	MemberService memberService = new MemberService();
    MemoryMemberRepository memberRepository = new MemoryMemberRepository;
}


(개발자가 new를 사용하여 필요한 객체를 직접 주입함)
위의 코드를 보면 MemberService에서 생성한 MemoryMemberRepository객체와 Test에서의 MemoryMemberRepository객체가
서로 다른 객체라는 것을 알 수 있다. 이렇게 코드를 작성하는 방법은 좋지 않다.
서로 다른 인스턴스를 쓰기 때문에, DB와 내용물이 달라질 수 때문이다. 또한 메모리도 낭비가 된다.

같은 인스턴스를 쓰게 만들려면,

@Service
public class MemberService {

    private final MemberRepository memberRepository; 

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

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach
    public void beforeEach() {
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }

이렇게 작성하면 된다.


위의 방식처럼 직접 주입하는 방식이 있지만 다르게 의존관계를 가지는 방법이 있다.(더 많이 쓰임)

Controller를 만들어준다.

@Controller
public class MemberController {

}


스프링 부트안에 스프링 컨테이너라는 공간이 있는데, @Controller 어노테이션을 붙이면 스프링이 MemberController객체를 생성하여 스프링컨테이너에 저장 후 관리하게 된다.

MemberController에서 아무런 기능은 없지만 MemberService를 가져다 쓴다고 가정해보자.

@Controller
public class MemberController {

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

    private final MemberRepository memberRepository; 

}


대부분 위의 코드처럼 new로 직접 생성하는 경우가 많다.
하지만 스프링에서 관리하게 되면, 컨테이너로 등록을 하고 컨테이너로부터 받아서 쓰도록 바꿔야 한다.
MemberController 말고도 다른 Controller들이 MemberService를 가져다 쓸 수 있기 때문이다.

예를 들면 주문Controller에서도 가져다 쓸 수 있고, 회원Controller에서도 가져다 쓸 수 있다.
굳이 계속해서 생성할 필요없이, 하나만 생성해서 같이 공용으로 쓰면 된다.

일단 Controller가 Service를 가져다 쓸 수 있도록 연결을 시켜주자.

@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired //필요한 객체를 컨테이너에 꺼내와서 연결
    public MemberController(MemberService memberService) { //외부(스프링컨테이너)에서 주입함
        this.memberService = memberService;
    }
}
public class MemberService {

    private final MemberRepository memberRepository; 

}


위 코드를 보자. MemberController는 스프링컨테이너에서 자동 생성한다. 그 때 생성자를 호출하게 되는데,
생성자에 @Autowired가 있으면 스프링이 스프링컨테이너 안에 있는 MemberService를 가져와서 연결을 시켜준다.

하지만 오류가 뜰 것이다.
스프링컨테이너 안에서 MemberService를 가져다 연결해줘야 하는데, MemberService는 순수 자바 객체이기 때문에 스프링이 알 수 있는 방법이 없다.(다른 말로 스프링컨테이너에 MemberService라는 객체가 없는 상황이다.)

스프링컨테이너 안에 등록하는 방법은 @Service를 붙여주면 된다.

@Service
public class MemberService {

    private final MemberRepository memberRepository; 

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


@Service를 붙이게 되면 스프링이 실행할 때 MemberService를 스프링컨테이너에 등록해준다.
Repository도 마찬가지로 @Repository를 붙여준다.

Controller를 통해서 외부에서 요청을 받고, Service에서 비즈니스 로직을 만들고, Repository에서 데이터를 저장하는 방식을 보면 정형화된 패턴임을 알 수 있다.


총정리

스프링이 실행되면 스프링컨테이너라는 틀을 만들게 되는데,
@Controller, @Service, @Repository를 통해 스프링컨테이너에 객체를 등록한다.
그리고 생성자에 @Autowired가 있으면 스프링이 스프링컨테이너에서 필요한 객체를 가져다 연결시켜준다.

@Controller
public class MemberController {

    private final MemberService memberService;

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


@Autowired를 통해 Service를 Controller에 연결해줬고,

@Service
public class MemberService {

    private final MemberRepository memberRepository; 

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


Repository를 Service에 연결시켜줬다.

@Repository
public class MemoryMemberRepository implements MemberRepository{

}

 

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

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

(참고 : 스프링 빈 = 스프링컨테이너에서 등록한 객체)

위에서 구현한 방법은 컴퍼넌트 스캔 방식이다.

1. 왜 이름이 컴퍼넌트 스캔일까 ?

사실 @Controller라고 쓰지 않고 @Component라고 써도 등록이 된다.
@Controller, @Service, @Repository 안에 들어가보면 @Component가 붙어져있는데,
스프링은 Component을 포함하는 어노테이션이 있으면 전부 객체를 생성해 스프링컨테이너에 등록을 한다.
한마디로 자동 등록되는 이유는 컴포넌트 스캔 때문이라고 보면 된다.
그리고 Autowired로 의존 관계를 연결해주는것이다.

2. 그렇다고 아무데나 Component 또는 Component가 포함된 어노테이션을 쓰면 자동 등록이 될까?

결론부터 말하자면 안된다.
왜냐하면 SpringApplication의 패키지를 포함해서 하위패키지 안에 있으면 스프링이 자동으로 등록을 해주지만,
하위패키지 안에 없으면 컴퍼넌트 스캔을 하지 않는다.

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

 

 

의존성주입(DI)에 대해 알아보았다. 강의에서 DI 개념이 나와서 내 방식대로 정리해보았다.
검색하지 않고 직접 작성한만큼 잘못된 정보가 있을 수 있다는 점을 고려해줬으면 좋겠다.
다음에는 자바 코드로 직접 스프링 빈을 작성하는 방법을 알아보겠다.

나중에 알아보니까 훨씬 간편한 방법이 있었다. 바로 @RequiredArgsConstructor 어노테이션을 이용한 방법이다.
의존주입의 원리는 알았으니 적용하는 방법만 알아보자. MemberController가 MemberService를 주입해서 사용한다고 가정해보자.

@RequiredArgsConstructor
public class MemberApiController {

	private final MemberService memberService;
}


이렇게 어노테이션을 넣어주고 선언만 해주면 자동으로 주입을 해준다. 전에 소개해준 3가지 방법에서 가장 간편했었던 @Autowired 와 비슷한 사용방식이지만, 요즘에는 @RequiredArgsConstructor를 훨씬 더 많이 쓰는 추세이니 알고 있으면 좋다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday