스프링 핵심 원리 이해 - 의존관계 자동 주입

꾸준하게 달리기~·2023년 8월 20일
0

스프링 + 자바

목록 보기
17/20
post-thumbnail

들어가기 앞서

SOLID원칙은 참 어려운 원칙이다.
정답이 존재하지 않고,
또 더 나아지는 코드를 만들 방법은 무한하기 때문이다.

우리는 SOLID원칙을 잘 준수하기 위해
공통 모듈은 분리하고,
확장은 용이하게,
가독성 또한 좋게,
등등의 방법을 사용한다.

그 여러 방법중에서
적절하게 분리를 잘 시키기 의 방법도 있다.

잘 분리된 정말 쉬운 예시는
다음과 같다.

@Component
public class MemberServiceImpl implements MemberService {


    private final MemberRepository memberRepository;
    
    @Autowired
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    
    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }
}

MemberServiceImpl 클래스에서는,
join 매서드를 통해 멤버를 저장한다.
근데 다시한번 자세히 봐보자.

memberRepository.save(member); 로직을 통해 저정한다.
즉, 저장의 수행은 memberRepository가 행하는것이다.

즉 각자는 각자가 수행해야 할 일만 잘 해내고,
그렇게 수행해야 할 일은 철저하게 분리시켜야 한다.

그런데! 여기서,
각자가 할 일을 분리해주면, 일이 세분화되고,
클래스에서 또다른 클래스의 일이 필요할 때가 생긴다.
우리는, 이때 의존성을 주입(DI)받아 사용한다.

//여러가지 의존성 주입 방법 중 하나인 생성자 주입
//(Service 단위에서 할 일이 repository 클래스의 역할이 필요하기 때문에)
	private final MemberRepository memberRepository;
    
    @Autowired
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

그리고, 의존성을 주입받는 방법은 여러가지가 있다.



다양한 의존관계 주입 방법


생성자 주입

@Component
public class MemberServiceImpl implements MemberService {


    private final MemberRepository memberRepository;
    
    
    @Autowired
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}

흔히 볼 수 있고, 가장 많이 사용하는 방식이다!

해당 코드가 생성자 주입 방식이다.
@Component어노테이션때문에
MemberServiceImpl이 스프링 컨테이너에 등록될때,
Spring에서 @Autowired어노테이션을 보고
MemberRepository를 주입해준다.

위와 같이 MemberServiceImplmemberRepository를 주입해주면,
그 누구도 MemberServiceImpl클래스의 필드변수 memberRepository를 수정할 수 없다.

다시 말하면,

  • 생성자 호출 시점에 한번만 호출되는것이 보장된다.
  • 불변, 필수 의존관계에 사용된다.
    불변 : 내가 할당해준 값(memberRepository)을 변경할 필요가 없을때.
    필수 : final 생성자로 해당 값은 할당시켜주어야 할때.

참고 : 생성자가 위의 코드처럼 하나라면, @Autowired 없이도 스프링이 알아서 해당 생성자를 선택해서 사용한다.
(위의 코드에선 @Autowired없어도 된다는 소리)


수정자 주입

@Component
public class MemberServiceImpl implements MemberService {


    private MemberRepository memberRepository;
    
    
    @Autowired
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}

우리가 흔히 사용하는 setter 매서드를 이용해서
의존관계를 주입한다.

public void setMemberRepository
final 키워드가 없는것을 확인할 수 있다.

즉,

  • 내가 원하는 값(원하는 memberReopsotory)을 선택해서 할당 가능하다.

  • memberRepository 값은 불변이 아니라, setMemberRepository 를 통해 변경 가능하다.


필드 주입

@Component
public class MemberServiceImpl implements MemberService {

	@Autowired
    private MemberRepository memberRepository;
    
    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

}

필드변수에 의존관계를 바로 때려박아버리는 방식이다.
코드가 간결해져서 어.. 이거 괜찮을지도...? 라는 생각이 들 수 있다.
하지만, memberRepository 필드를 Spring에서 내부적으로 정해서 할당해주기 때문에,
이 방식은 테스트하기 힘들다는 치명적 단점이 있다.

위의 필드 주입 방식으로 작성하고 나서,
테스트가 실패하는 내용을 코드로 예시를 보자.

    @Test
    void fieldInjectionTest() {
        //given
        MemberServiceImpl memberService = new MemberServiceImpl();
        Member testMember = new Member(1L, "테스트멤버", Grade.VIP);

        //when
        memberService.join(testMember);

        //then
        
    }

해당 코드는, Spring 없이 순수 자바 코드로 작성되었다.

필드 주입 방식은,
필드변수에 바로 @Autowired 어노테이션을 사용하면
우리가 Spring 프레임워크를 사용하여 어플리케이션을 구동하면
자동으로 memberRepository를 찾아서 주입해주지만,

순수 자바에서는 그렇지 않다.

즉, join 매서드에서,
memberRepository.save() 를 실행시킬때,
주입된 memberRepository 가 없으므로,
테스트코드의 memberService.join(testMember); 에서 아래와 같이
NullPointerException예외가 발생하게 된다.

의존성을 주입해주는 프레임워크가 없다면 위와 같이 복잡한 상황이 나올 수 있으므로, 거의 사용하지 않는다.


일반 메서드 주입

@Component
public class MemberServiceImpl implements MemberService {


    private MemberRepository memberRepository;
    
    
    @Autowired
    public void init(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

}

init()이라는 일반 매서드를 통해 주입하는 방식이다.
근데, 생성자 주입과 별반 다를게 없다.
서로 다른점은 매서드, 생성자 정도라는것이다.
그렇기 때문에, 많이 사용되는 방식이 아니다.


레퍼런스 : 김영한님 Spring 강의

profile
반갑습니다~! 좋은하루 보내세요 :)

0개의 댓글