개발/Spring

[Spring] 스프링을 사용하는 이유?

TutleKing 2022. 8. 25. 13:27

Spring이란 JAVA를 좀 더 편하게 사용할 수 있게 해주는 프레임워크로 대규모의 회사에서 많이 사용한다. 

 

여기서 "JAVA를 좀 더 편하게" 라는 단어의 의미를 김영한님의 "스프링 핵심 원리 - 기본편" 을 들으며 이해할 수 있게 되었다. 

 

내가 프로그래밍을 하다 보면 하나의 class 혹은 객체에 정~말 다양한 책임과 기능을 모두 넣어 사용했었다. (혼자 기획하고 개발하다보니..)

 

그 이후에 객체 지향 설계 원칙에 대해 알게 되었고 그 설계 원칙을 단 하나도 지키지 않고 프로그래밍을 했다는 것 또한 알게 되었다.

 

내가 느낀바로는 Spring은 객체 지향적인 설계를 할 수 있도록 도와주는 아주 좋은 툴이라는 생각이 들었다. 

 

Spring에서 주요하게 사용 되는 단어로 IoC , DI 라는 단어가 나온다 

 

 

IoC : 제어의 역전 (Inversion of Control)

기존의 프로그램은 구현 객체가 프로그램 제어 흐름에 직접적으로 개입(인스턴스 생성 등)하여 실행되었다.

// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
 private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
 
//인스턴스 생성시 discountPolicy에 RateDiscountPolicy를 직접적으로 선언

위의 코드와 같이 코드를 작성하게 되면 RateDiscountPolicy를 사용하지 않고 FixDiscountPolicy를 사용하게 되면 해당 class로 가서 계속 코드를 바꿔줘야한다. 이것은 OCP(Open - Closed Principle : 확장에는 오픈, 변경에는 클로즈)에 위배된다. 또한 DIP(Dependency Inversion Principle : 인터페이스에만 의존 하기) 도 위배되고 있다. 

 

그래서 내부의 코드를 바꾸지 않고 외부에서 생성자를 통해 기능을 주입하고자 한다. class는 외부에서 어떤 역할을 쥐어 주던 실행에만 신경을 쓰면 되기 때문에 OCP에 위배되지 않을 수 있다. 아래 AppConfig 클래스 코드를 보면 이해하기 쉬울 것이다.

...

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();
 }
}

그래서 AppConfig를 사용하여 기능 테스트를 해보면 더 명확해진다. 

public class MemberServiceTest {
    MemberService memberService;

    @BeforeEach
    public void beforeEach(){
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
    }

    @Test
    void join(){
        //given
        Member member = new Member(1L, "memberA", Grade.VIP);
        //when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);

        //then
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

이렇게 제어의 흐름을 직접 제어하는 것이 아니라 외부에서 제어 할 수 있도록 하는 것이 IoC(제어의 역전)이라고 한다.

 

DI : 의존관계 주입 (Dependency Injection)

애플리케이션 실행 시점(런타임)에 외부에서 인스턴스를 생성하고 클라이언트에 전달하여 클라이언트와 서버의 실제 의존 관계가 연결되는 것을 의존 관계 주입이라고 일컫는다.  

 

 

 

Spring에서는 AppConifg 처럼 객체를 생성 & 관리 하며 의존관계를 연결해주는 것을 IoC컨테이너 또는 "DI 컨테이너"라고 한다. (Spring 컨테이너라고도 부름 )

 

 

스프링에서 자동으로 DI 컨테이너 등록

...

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }


    @Bean
    public static DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }


}

스프링은 "@Configuration"이 붙은 AppConfig.class를 구성 정보로 사용한다. 

해당 클래스 안에서 "@Bean" 이 붙은 메서드를 모두 호출하여 return된 객체를 스프링 컨테이너(=DI 컨테이너)에 등록한다. 

(스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다)

 

자동으로 등록한 스프링 빈 사용하기 

public class OrderApp {
 public static void main(String[] args) {
    // AppConfig appConfig = new AppConfig();
    // MemberService memberService = appConfig.memberService();
    // OrderService orderService = appConfig.orderService();
    
     ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    
     MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
    
     OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
    
    
     long memberId = 1L;
     Member member = new Member(memberId, "memberA", Grade.VIP);
     memberService.join(member);
     Order order = orderService.createOrder(memberId, "itemA", 10000);
     System.out.println("order = " + order);
 }
}

ApplicationContext (인터페이스)를 스프링 컨테이너라고 하고, 애노테이션(@~~~) 기반의 자바 설정 클래스로 스프링 컨테이너를 구성하였다. 

구현을 위해 new AnnotationConfigApplicationContext()에 AppConfig.class를 인자로 받아 스프링 컨테이너를 생성한다.

이는 AppConfig.class를 구성정보로하여 스프링컨테이너를 생성하였다고 일컫는다.

반응형