본문 바로가기

WINK-(Web & App)/Spring Boot 스터디

[2024 Spring Boot 스터디] 남윤찬#2 주차 - 4~5 섹션 (작성 중)

반응형

섹션 4. 스프링 컨테이너와 스프링 빈

 

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

위 코드를 통해 스프링 컨테이너를 생성한다.

스프링 컨테이너를 생성할 때는 AppConfig와 같은 구성 정보를 지정해준다.

이처럼 구성 정보를 통해 스프링 컨테이너가 객체들의 의존 관계를 주입해준다.

그리고 스프링 컨테이너는 BeanFactory라는 스프링 컨테이너의 최상위 인터페이스를 상속하여, 이것이 제공하는 getBean() 메서드를 사용해 다음과 같이 컨테이너 내의 Bean을 조회할 수 있다.

class ApplicationContextBasicFindTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 이름으로 조회")
    void findBeanByName() {
        MemberService memberService = ac.getBean("memberService", MemberService.class);
        assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
    }

}

또한 스프링 컨테이너로 사용하는 ApplicationContext 인터페이스는 BeanFactory를 포함하여 메시지소스, 환경변수, 애플리케이션 이벤트, 리소스 조회와 같은 부가 기능 인터페이스를 상속받아서 사용자에게 제공한다.

 

섹션 5. 싱글톤 컨테이너

싱글톤 패턴

클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴

스프링에서 왜 싱글톤 패턴?

대부분의 스프링 애플리케이션은 웹 애플리케이션이다. 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 하는데, 요청이 들어올 때 마다 인스턴스를 생성하게 되면 메모리 낭비가 심해진다. 그래서 등장한 것이 한 jvm 안에서 하나의 객체만을 생성하고 이를 공유하도록 만드는 ‘싱글톤’ 패턴이다.

하지만 싱글톤 패턴이라고 만능이 아니라, 다음과 같은 문제점들을 가지고 있다.

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
  • 의존관계상 클라이언트가 구체 클래스에 의존한다. → DIP를 위반한다.
  • 클라이언트가 구체 클래스에 의존한다. → OCP 원칙을 위반할 가능성이 높다.
  • 테스트하기 어렵다.
  • 내부 속성을 변경하거나 초기화 하기 어렵다.
  • private 생성자로 자식 클래스를 만들기 어렵다.
  • 결론적으로 유연성이 떨어진다.
  • 안티패턴으로 불리기도 한다.

이러한 문제들을 해결해주면서도 싱글톤 패턴을 지킨 것이 아래와 같은 스프링 컨테이너이다. 스프링은 싱글톤을 적용시키지 않아도 객체를 하나만 생성해서 관리하기 때문에 싱글톤 패턴을 유지하면서도 그 단점들을 모두 해결할 수 있다.

스프링이 모든 단점을 해결해주지만, 그럼에도 딱 한 가지 지켜야 할 것이 있다.

싱글톤 인스턴스는 무조건 무상태(stateless)로 설계해야 한다.

특정 클라이언트에 의존적이거나, 클라이언트가 값을 변경할 수 있는 필드가 있는 등의 설계는 프로젝트에 큰 문제를 일으킬 수 있다.

@Configuration과 싱글톤

아래 AppConfig를 보면 MemberRepository는 3번 new로 호출되는데, 이것만 보아서는 싱글톤이 깨지는 것처럼 보인다.

package hello.core;

...

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository()); //memberRepository 호출1
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository(); //memberRepository 호출2
    }

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

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

}

하지만 확인해보면 MemberService, MemberRepository, OrderService 모두 하나의 memberRepository를 공유하여 싱글톤 패턴이 지켜지고 있다.

이는 @Configuration 어노테이션에 의해 실현되고 있다. 스프링 컨테이너는 CGLIB이라는 바이트코드 조작 라이브러리를 사용해 @Configuration 어노테이션을 가지고 있는 객체를 상속하여 새로운 객체를 생성하고 이를 빈에 등록하여 컨테이너 구성 정보에 사용한다.

CGLIB 자체는 매우 어려운 기술이므로, 현재 단계에서는 구성 정보 객체에는 @Configuration 어노테이션을 붙여준다는 것만 기억하고 있으면 된다.

 

 

 

반응형