스프링 기본 원리 2. SOLID 와 스프링 그리고 Bean...

xellos·2022년 4월 28일
1

Spring

목록 보기
2/6

1) SOLID의 실현과 오해

아래와 같은 코드가 있다. 여기서 아래의 코드는 OCP와 DIP를 잘 지켰을까?
→ 그렇지 않다. 주문 서비스를 구현한 OrderServiceImpl는 겉보기에는 MemberRepository라는 인터페이스를 사용하는것 같지만, 실제로는 이를 구현한 MemoryMemberRepository를 생성하는 코드가 내부에 하드코딩 되어있다.

public class OrderServiceImpl implements OrderService {
	private final MemberRepository memberRepository = new MemoryMemberRepository();
    
    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
    	//...
    }
}

2) SOLID의 실현

실제로는 아래와 같이 인터페이스 부분에 이를 구현한 클래스 인스턴스를 생성자 주입으로 넘겨주어야 한다. 이때 DI의 개념이 적용된다.
이제야 비로소 아래의 코드는 구체적인 구현체가 아닌 인터페이스에 의존하며(DIP) 해당 구현체만 바꾸면 내부의 코드를 건드리지 않고 쉽게 기능을 변환할 수 있다(OCP).

public class OrderServiceImpl implements OrderService {
	private final MemberRepository memberRepository;
    
    public OrderServiceImpl(MemberRepository memberRepository) {
    	this.memberRepository = memberRepository;
    }
    
    //...
}

3) Config 클래스의 필요성 대두 (설정 클래스)

하지만, 의문점이 하나있다. 결국에는 OrderServiceImpl 클래스 외부에서 의존성을 주입해주는 역할을 해줄 무언가가 필요한데 이를 누가 수행할 것인가? 이다.
→ 이를 해결하기 위해 각 클래스 사이에 의존성을 만들고 넣어주는 Config 클래스 파일이 필요하다. 즉 아래와 같은 설정파일이 필요하다.

public class AppConfig {

	public OrderService orderService() {
    	return new OrderServiceImpl(memberRepository());
    }
    
    public MemberRepository memberRepository() {
    	return new MemoryMemberRepository();
    }
    
    //...
}

4) 스프링으로 변환

그렇다. 바로 스프링에서 우리가 xml 설정 파일 또는 아래와 같이 빈을 주입하는 설정 클래스를 만드는 과정이 바로 이 과정이다. 이를 통해 우리는 DIP, OCP, DI에 대한 개념을 몰라도 그동안 손쉽게 이를 만들고 사용하고 있었다.

@Configuration
public class AppConfig {
	
    @Bean
    public OrderService orderService() {
    	return new OrderServiceImpl(memberRepository());
    }
    
    @Bean
    public MemberRepository memberRepository() {
    	return new MemberRepository();
    }
}

5) 마무리: 스프링 컨테이너

마지막으로 스프링 컨테이너에서 만든 빈을 확인하는 코드를 보고 마무리하자. 아래의 코드는 테스트 필드에서 사용되었다.
→ AnnotationConfigAppliationContext는 ApplicationContext의 하위 클래스로 우리가 위에서 본 설정 파일을 인자로 받아서 컨테이너 내부에 빈을 생성한다.

AnnotatioConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("모든 빈 출력하기")
void findAllBean() {
	String[] beanDefinitionNames = ac.getBeanDefinitionNames();
    
    for(String beanDefinitionName : beanDefinitionNames) {
    	Object bean = ac.getBean(beanDefinitionName);
        System.out.println("name=" + beanDefinitionName + " object=" + bean);
    }
}

0개의 댓글