스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
본 포스팅은 <스프링 핵심 원리 - 기본편> 강의 내용을 복습하기 위해 간략히 작성된 글이므로 전체 내용은 위 강의에서 확인 가능합니다.
2. 객체지향의 5가지 원칙(SOLID), 관심사의 분리
7. @Autowired 필드명 매칭, @Qualifier, @Primary
들어가기 전에
대부분의 스프링 어플리케이션은 웹 애플리케이션인데요. 웹 애플리케이션은 보통 여러 고객이 동시에 요청을 합니다. 만약 세 명의 클라이언트가 요청을 하면 객체는 세 번 생기는 걸까요?
public class SingletonTest {
@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContainer() {
AppConfig appConfig = new AppConfig();
//1. 조회: 호출할 때 마다 객체를 생성
MemberService memberService1 = appConfig.memberService();
//2. 조회: 호출할 때 마다 객체를 생성
MemberService memberService2 = appConfig.memberService();
//참조값이 다른 것을 확인
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
//memberService1 != memberService2
assertThat(memberService1).isNotSameAs(memberService2);
}
}
스프링이 없는 순수한 DI 컨테이너 코드에서는 요청을 할 때마다 아래처럼 새로운 객체가 생성되는 것을 확인할 수 있습니다.
이렇게 되면 메모리 낭비가 심하게 되기 때문에 객체가 딱 1개만 생성되고 공유되도록 설계해야 합니다. 바로 이 패턴이 그 유명한 “싱글톤 패턴"입니다.
싱글톤 패턴
싱글톤 패턴은 클래스의 인스턴스가 딱 1개만 생성하게 하는 디자인 패턴입니다.
public class SingletonService {
// 1. static 영역에 객체를 딱 1개만 생성
private static final SingletonService instance
= new SingletonService();
/// 2. 객체 인스턴스가 필요하면 이 static 메서드를 통해서만 조회하도록 허용(public)
public static SingletonService getInstance() {
return instance;
}
// 3. 외부에서 new 키워드를 사용한 객체 생성 방지(private)
private SingletonService() {}
}
위의 코드와 같이 싱글톤 패턴을 사용하면 고객이 요청할 때마다 객체를 생성하는 게 아니라 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있습니다.
하지만 효율적이라고 하는 이 싱글톤 패턴에도 문제점이 있습니다.
- 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
- 의존관계 상 클라이언트가 구체 클래스에 의존한다. → DIP 위반
- 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
- 테스트하기 어렵다.
- 내부 속성을 변경하거나 초기화하기 어렵다.
- private 생성자로 자식 클래스를 만들기 어렵다.
- 결론적으로 유연성이 떨어진다.
그래서 이 싱글톤 패턴의 문제점을 해결하기 위해 사용하는 것이 바로 싱글톤 컨테이너입니다.
싱글톤 컨테이너
스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리합니다. 스프링 컨테이너 생성 과정을 살펴보면 스프링의 기본 빈 등록 방식이 싱글톤이기 때문에 컨테이너가 객체를 하나만 생성해서 관리하는 것(싱글톤 레지스트리)을 볼 수가 있는데요. 그래서 싱글톤 컨테이너의 역할을 하게 되는 겁니다.
하지만 이렇게 메모리를 효율적으로 사용할 수 있는 싱글톤 방식에는 주의해야 할 점이 있습니다. 여러 고객이 공유하는 하나의 같은 객체 인스턴스가 상태를 계속 유지되면 어떻게 될까요? 고객A가 주문 객체를 이용해서 10000원 어치를 주문하고 고객B는 20000원 어치를 주문 했습니다. 고객A의 주문 금액을 조회하면 이 주문 객체를 얼마를 반환할까요?
상태가 계속 유지됐기 때문에 10000원이 아닌 20000원을 반환하게 됩니다. 그렇기 때문에 싱글톤 객체는 무상태(stateless)로 설계를 해야 합니다. 무상태 설계의 조건은 다음과 같습니다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기만 가능해야 한다.
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.
// 상태 유지
public class StatefulService {
private int price;
public void order(int price) {
this.price = price;
}
}
// 무상태
public class StatelessService {
public int order(int price) {
return price;
}
}
스프링 컨테이너에서는 싱글톤 컨테이너의 역할을 한다고 했는데 만약 동일한 객체를 호출하게 되면 어떻게 될까요? 그래도 싱글톤 객체를 생성할 수 있을까요? 아래의 코드는 결과적으로 MemoryMemberRepository를 두 번 호출하는 코드입니다. 스프링 컨테이너가 빈을 등록하기 위해 한 번 호출하는 게 더해진다고 생각하면 총 세 번의 호출이 될 거라고 예상이 되는데요.
예상으로는 첫 번째 호출은 스프링 컨테이너가 빈을 등록할 때, 두 번째 호출은 memberService()에서, 세 번째 호출은 orderService()에서 이루어지게 되겠죠.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository();
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
하지만 결과적으로 호출은 단 한 번 실행됩니다. 어떻게 된 걸까요?
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
이유는 @Configuration 애노테이션을 사용한 AppConfg 객체에 있습니다. 스프링이 CGLIB이라는 바이트코드 조작 라이브러리를 사용해서 AppConfig를 상속받는 설정클래스를 임의로 만들고(AppConfig@CGLIB) 그 다른 클래스를 스프링 빈으로 등록하는 것입니다.
이 임의의 클래스 안에는 이미 스프링 컨테이너에 등록되어있는 빈은 스프링 컨테이너에서 찾아서 반환하는 로직을 가지고 있기 때문에 싱글톤이 보장되는 것입니다. @Bean만 사용했을 때는 빈으로 등록은 가능하지만 싱글톤은 보장이 안되기 때문에 스프링 설정 정보는 항상 @Configuration을 사용해야 합니다.
'Spring' 카테고리의 다른 글
[Spring] @Autowired 필드명 매칭, @Qualifier, @Primary (0) | 2022.09.17 |
---|---|
[Spring] 컴포넌트 스캔 (0) | 2022.09.17 |
[Spring] 스프링 컨테이너와 스프링 빈 (0) | 2022.09.14 |
[Spring] 제어의 역전(IoC)과 의존관계 주입(DI) (0) | 2022.09.14 |
[Spring] 객체지향의 5가지 원칙(SOLID), 관심사의 분리 (0) | 2022.09.14 |