본문 바로가기
Spring

[Spring] AppConfig를 이용한 의존관계 주입

by 당코 2022. 12. 23.

클래스를 설계할 때 앞서 말한 SOILD를 지키면서 하는 것이 중요하다

먼저 OCP, DIP를 지키지 않는 클래스를 간단한 자바 코드를 통해 살펴보겠다

public class MemberServiceImpl implements MemberService {
	
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    
    ...
}

 

public class OrderServiceImpl implements OrderService {
	
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    
    ...
}

 

 

각각 MemberService, OrderService에 대한 구현 클래스이다. 이 두 클래스는 OCP와 DIP를 지키고 있는 것일까?

Service의 역할과 구현을 충실하게 분리하였고, 인터페이스와 구현객체를 분리해 구현을 하였다.

하지만 OCP와 DIP를 지키지 않는 구현 방식이다.

만약 저장소를 MemoryMemberRepository가 아닌 다른 저장소를 이용하거나, RateDiscountPolicy가 아닌 다른 할인 정책을 사용할 경우 클라이언트의 코드를 수정해야 하므로 OCP를 위반한다.

클래스 의존관계를 살펴보면 두 코드 모두 추상(인터페이스)만 의존하는 것이 아닌, 구체(구현) 클래스에도 의존하기 때문에 DIP를 위반한다.

즉, 이를 해결하기 위해서는 구체(구현)클래스에 의존하는 것이 아닌 추상(인터페이스)만 의존하도록 변경해야 한다.

 

AppConfig 도입

public class MemberServiceImpl implements MemberService {
	//private final MemberRepository memberRepository = new MemoryMemberRepository();
    	private final MemberRepository memberRepository;
    
    	...
}
public class OrderServiceImpl implements OrderService {
 	//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
	 private DiscountPolicy discountPolicy;
     
     	...
}

인터페이스만 의존하도록 코드를 변경하였다. 하지만 구현체가 없이 코드를 실행할 수는 없다.

즉, 클라이언트가 아닌 다른 누군가가 직접 구현 객체를 생성하고 주입해주어야 한다는 의미이다.

애플리케이션의 전체 동작 방식을 구성(config) 하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들어 보겠다.

 

public class AppConfig {
 	public MemberService memberService() {
 		return new MemberServiceImpl(new MemoryMemberRepository());
 	}
    
 	public OrderService orderService() {
 		return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
    }
}

AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다. 그리고 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)해준다

AppConfig를 이용하여 어떤 객체를 주입할지를 한 번에 관리하여 OCP, DIP를 지킬 수 있다.

사용 방법은 다음과 같다.

public class MemberServiceImpl implements MemberService {
	
    private final MemberRepository memberRepository;
	
    public MemberServiceImpl(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
 	}
}
public class OrderServiceImpl implements OrderService {
 	
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
 
 	public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
 		this.memberRepository = memberRepository;
 		this.discountPolicy = discountPolicy;
 	}
}

각각의 클래스들은 생성자를 통해 객체를 주입받는다.

이러한 형태를 생성자 주입이라고 한다.

 

public class OrderApp {
 	public static void main(String[] args) {
    		AppConfig appConfig = new AppConfig();
 		MemberService memberService = appConfig.memberService();
 		OrderService orderService = appConfig.orderService();
	}
}

AppConfig를 통해 객체를 생성하고 있다. 이런 방식은 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입이라 한다.

 

AppConfig 리팩터링

public class AppConfig {
 	public MemberService memberService() {
 		return new MemberServiceImpl(new MemoryMemberRepository());
 	}
    
 	public OrderService orderService() {
 		return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
    }
}

현재 AppConfig의 코드를 살펴보면 new MemoryMemberRepository()가 중복으로 사용되고 있다.

또한 객체 안에서 new 키워드를 사용하여 특정 객체를 직접 사용하고 있다.

이 또한 약간의 리팩터링을 하여 해결할 수 있다.

public class AppConfig {
 	public MemberService memberService() {
 		return new MemberServiceImpl(memberRepository());
 	}
 	public OrderService orderService() {
 		return new OrderServiceImpl(memberRepository(), discountPolicy());
 	}
 	public MemberRepository memberRepository() {
 		return new MemoryMemberRepository();
 	}
 	public DiscountPolicy discountPolicy() {
 		return new FixDiscountPolicy();
 	}
}

중복된 부분을 메서드로 분리하여 중복을 제거하고 객체 안의 new 키워드 사용을 막았다.

이에 대한 장점은 역할과 구현 클래스가 한눈에 들어오는 것이다.

또한 저장소, 할인정책의 변경이 필요할 경우에는 한 부분만 변경하면 된다는 장점이 있다. 

 

지금까지는 순수한 자바 코드로 OCP, DIP를 지키도록 AppConfig를 사용하여 설계하였다. 하지만 스프링을 이용한다면 이보다 더 쉽게 의존관계 주입을 해줄 수 있다. 이에 관한 이야기는 앞으로의 포스팅에서 계속하겠다.

 

출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard