728x90


스프링에서의 관심사의 분리는 소프트웨어 개발에서 중요한 개념 중 하나입니다. 관심사의 분리는 코드의 유지보수성, 재사용성, 테스트 가능성 등을 향상시키는 방법으로, 코드를 모듈화하여 각 모듈이 특정한 역할과 책임을 갖도록 하는 것을 의미합니다.

스프링에서의 관심사의 분리는 주로 다음과 같은 방식으로 이루어집니다:

  1. 의존성 주입 (Dependency Injection)
    • 의존성 주입은 객체 간의 의존성을 외부에서 주입하는 방식으로, 객체 간의 결합도를 낮춥니다. 이를 통해 각각의 객체는 자신이 수행해야 할 기능에만 집중할 수 있으며, 객체 간의 의존성을 명확하게 분리할 수 있습니다.
  2. 제어 역전 (Inversion of Control)
    • 제어 역전은 객체의 생명주기와 의존성 관리를 프레임워크가 담당하도록 하는 개념입니다. 스프링은 제어 역전을 통해 객체의 생성, 관리, 소멸 등의 생명주기를 관리하고, 필요한 의존성을 주입합니다. 이를 통해 개발자는 객체의 생성과 의존성 관리에 대한 부분을 신경쓰지 않고 핵심 비즈니스 로직에 집중할 수 있습니다.
  3. 관점 지향 프로그래밍 (Aspect-Oriented Programming, AOP)
    • AOP는 애플리케이션의 핵심 로직과 관련 없는 부가적인 기능들을 모듈화하여 분리하는 방식입니다. 예를 들어, 로깅, 트랜잭션 관리, 보안 등과 같은 부가적인 기능을 각각의 핵심 로직에서 분리하여 관리합니다. 이를 통해 핵심 로직의 코드는 더욱 간결해지고, 부가적인 기능은 별도의 모듈로 분리하여 관리할 수 있습니다.

위의 방법들을 활용하여 관심사를 분리하면 코드의 가독성과 유지보수성을 높일 수 있으며, 코드의 재사용성과 테스트 가능성도 향상시킬 수 있습니다. 또한, 각각의 모듈이 독립적으로 개발 및 테스트될 수 있어 개발 프로세스를 효율적으로 관리할 수 있습니다.

 

        //OrderService 인터페이스를 구현해 줄 코드!
        //OrderService는 DisCountPolicy가 변경되더라도 아무런 영향을 받지 않음
        public class OrderServiceImpl implements OrderService{

            private final MemberRepository memberRepository = new MemoryMemberRepository();
            //아래와 같이 odreServiceImpl에서 discountPolicy를 직접 객체를 생성해서 구체적으로 작성하게 되면서 DIP를 위반하게 됩니다.
            
            //private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
            //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

            private DiscountPolicy discountPolicy;
            //선언을 위와같이만 변경해준다면, 인터페이스에만 의존하게 됩니다. 주문을 만들어서 반환을 해주면 해당 파일의 실행이 종료됨

                @Override
            public OrderDTO createOrder(Long memberId, String itemName, int itemPrice) {
                Member member = memberRepository.findById(memberId);
                int discountPrice = discountPolicy.discount(member, itemPrice);

                return new OrderDTO(memberId, itemName, itemPrice, discountPrice);
            }
        }

 

👉 관심사의 분리가 필요 ! AppConfig의 등장

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

 

AppConfig 

     
     public class AppConfig {
            //AppConfig를 통해서 memberService를 호출해서 사용하게 되는데 , return 값인 MemberServiceImpl 객체의 구현체가
            //생성이 되는데, 그 때, MemoryMemberRepository가 매개변수로 전달되게됩니다. 임플로!
            
            public MemberService memberService() {
            
                return new MemberServiceImple(new MemoryMemberRepository());
            //MemberServiceImple는 new MemoryMemberRepository 객체를 생성 후 참조 값을 받음
            }

            //AppConfig를 통해서 orderService를 조회하면 OrderServiceImpl이 반환되는데,
            //그 안에 MemoryMemberRepository와 FixDiscountPolicy가 매개변수로 담겨져서 반환됩니다.
            
            public OrderService orderService() {
                return new OrderServiceImpl(new MemoryMemberRepository(),
                                            new FixDiscountPolicy());

            이러한 방식을 생성자를 통해 주입한다고 표현합니다 
            }

        }

 

OrderServiceImpl

      
      	//OrderService 인터페이스를 구현해 줄 코드!
        //OrderService는 DisCountPolicy가 변경되더라도 아무런 영향을 받지 않음
        public class OrderServiceImpl implements OrderService{

            //인터페이스에만 의존하도록 수정되어 DIP를 지킬 수 있음
            private final DiscountPolicy discountPolicy;
            private final MemberRepository memberRepository;

            public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy ) {
                this.memberRepository = memberRepository;
                this.discountPolicy = discountPolicy;

            }

            @Override
            public OrderDTO createOrder(Long memberId, String itemName, int itemPrice) {
                Member member = memberRepository.findById(memberId);
                int discountPrice = discountPolicy.discount(member, itemPrice);

                return new OrderDTO(memberId, itemName, itemPrice, discountPrice);
            }
        }

 

  • AppConfig는 애플리케이션의 실제 동작에 필요한 “구현 객체를 생성” 합니다.
  • AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)합니다. MemberServiceImpl → MemoryMemberRepository OrderServiceImpl → MemoryMemberRepository , FixDiscountPolicy
    • 설계 변경으로 MemberServiceImpl는 MemoryMemberRepository에 의존하지 않고 MemberServiceRepository 인터페이스만 의존합니다.
    • MemberServiceImpl의 입장에서 생성자를 통해 어떤 구현객체가 들어올지 (주입될 지)는 알 수 없으며, 어떤 구현 객체를 주입할 지는 오직 외부(AppConfig)에서 결정됩니다.
    • MemberServiceImpl은 “의존관계에 대한 고민은 외부” 에 맡기고 “실행에만 집중” 가능

 

 

 

 

       
       package Spring.core;


        public class AppConfig {
            //↓ 클라이언트 (주문생성 -회원 ID)
            public MemberService memberService() {
                return new MemberServiceImple(memberRepository());
            }

            //↓ 회원 저장소 역할 (메모리 회원 저장소 / DB 회원 저장소)
            private  MemberRepository memberRepository() {
                return new MemoryMemberRepository();
            }

            //↓ 주문 서비스 역할 (회원 조회 / 할인 적용)
            public OrderService orderService() {
                return new OrderServiceImpl(memberRepository(),
                                          discountPolicy());
            }

            //↓ 할인 정책 역할 (정액 할인 정책 / 정률 할인 정책)
            public DiscountPolicy discountPolicy() {
                return new FixDiscountPolicy();
            }

        }

 

📄 새로운 구조와 할인 정책 적용

  • 정액 할인 정책을 정률 할인 정책으로 변경해보자! 앞서 작성해둔 AppConfig의 등장으로 애플리케이션이 크게 사용 영역과 객체를 생성하고 구성(Configuration) 하는 영역으로 분리되었습니다.

 

 

AppConfig에서 할인 정책을 담당하는 구현부를 FixDiscountPolicy → RateDiscountPolicy로 변경을 진행하였습니다. 할인 정책이 변경되더라도 구성 영역인 AppConfig만 영역을 받고, 사용 영역은 전혀 영향을 받지 않을것입니다.

구성영역은 당연히 변경이 진행됩니다. 구성 역할을 담당하는 AppConfig를 애플리케이션이라는 공연의 기획자로 생각하면 간단합니다. 공연 기획자(=AppConfig)는 공연 참여자인 구현 객체들 (출연자)을 모두 알아야 합니다.

       
       //↓ AppConfig 클래스의 discopuntPolicy 메서드의 리턴 값만 정률 할인 정책은 
            //RateDiscountPolicy()로 변경해주면 가능하다. 다른 구현체에게는 (사용영역에 있는) 영향이 없습니다.
        public DiscountPolicy discountPolicy() {
            return new FixDiscountPolicy();
        }
728x90
반응형

'[ BACKEND] > Spring' 카테고리의 다른 글

[SPRING] 서블릿과 JSP  (0) 2023.07.18
[SPRING] IoC , DI , 스프링 컨테이너  (0) 2023.07.17
[SPRING] 💡JPA  (0) 2023.07.13
[SPRING] HTTP 요청과 응답  (1) 2023.07.10
[SPRING] 스프링 DB 접근 기술  (0) 2023.07.08

+ Recent posts