상세 컨텐츠

본문 제목

[스프링 1] AppConfig, 스프링 컨테이너, 싱글톤

22-23/22-23 Spring 1

by YUZ 유즈 2022. 10. 6. 10:31

본문

728x90

해당 포스트는 스프링 핵심 원리 - 기본편 강의 내용을 참고하였습니다.


❗ AppConfig

📌 역할

  • 사용 영역과 구성 영역을 분리 ➡ 관심사의 분리 (SRP 단일 책임 원칙 적용)
  • 그러므로 클라이언트 코드는 수정 없이 객체를 변경할 수 있다!
    예를 들어, 할인 정책을 바꾸고 싶을 때, AppConfig에서 간단하게 변경할 수 있다. 
    ➡  OCP 준수
  • 프로그램의 흐름을 AppConfig가 조종하므로 제어의 역전(IoC, Inversion of Control)이 일어난다.
    같은 말로 의존성 역전 원리(DIP, Dependency Inversion Principle) 라고 한다.
  • 이렇게 동적으로 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC 컨테이너 혹은 DI 컨테이너라고 부른다.

📌 리팩토링

1. 역할과 구현을 명확하게 분리

2. 역할이 잘 보이게 수정

// 역할이 잘 안보임
public MemberService memberService() {
    return new MemberServiceImpl(return new MemoryMemberRepository());
}

// 역할을 잘 보이기 위해 메소드로 추출
public MemberService memberService() {
    return new MemberServiceImpl(memberRepository());
}
public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
}​

 


❗ ApplicationContext - 스프링 컨테이너

  • 스프링 컨테이너 이자 인터페이스
  • 스프링 컨테이너에 Bean을 등록하고 스프링이 찾아서 가져오는 형태로 변경 (@Bean, @Configuration)
  • 개발자가 직접 자바코드로 가져오는 것이 아니고 스프링이 찾아오는 형태로 변경

📌 스프링 컨테이너 생성 방법

 

  1. 스프링 컨테이너 구성
    스프링 컨테이너를 생성할 때 전달하는 AppConfig.class를 보고 스프링 컨테이너를 구성한다.
  2. 스프링 빈 등록
    스프링 컨테이너에는 스프링 빈 저장소가 있고 그곳에 스프링 빈을 등록한다.
    저장소는 key value 쌍으로 이루어져있는데, key는 빈 이름, value는 빈 객체이다. ( key:value )
    빈 이름은 메서드 이름을 사용한다. 직접 부여할 수도 있다. 같은 이름을 부여하면 큰일난다. 덮어버리거나 오류가 발생할 수도 있다. 
  3. 스프링 빈 의존 관계 설정
    스프링 빈 저장소에 따라 빈을 생성하고 의존관계를 넣어준다.
    설정 정보(AppConfig)에 따라 의존관계를 주입(DI)한다.

이해를 위해서 단계 별로 나눴지만 2단계와 3단계는 한 번에 처리된다.

 


❗ BeanFactory 

  • 스프링 컨테이너의 최상위 인터페이스
  • ApplicationContext의 상위 인터페이스
  • 스프링 빈을 관리하고 조회하는 역할

ApplicationContext 상위 인터페이스가 있다고? [출처: 스프링 핵심 원리 - 기본편(인프런)]

📌 ApplicationContext

  • BeanFactory 기능을 모두 상속받아서 제공
  • 빈을 관리하고 조회하는 역할 + 편리한 부가 기능 제공
    언어 소스 관리, 환경 변수 관리, 어플리케이션 이벤트 관리, 사진 등의 리소스 관리 등의 부가 기능. 

BeanFactory를 직접 사용할 일은 거의 없기 때문에 BeanFactory와 ApplicationContext를 모두 스프링 컨테이너라고 칭한다.

 


❗ 구성 정보 변경까지 유연한 스프링

BeanDefinition 설명

이때까지는 AppConfig.class로 가져왔는데 이렇게 말고 xml이나 다른 파일로 변경하는 것도 아주 간단하다.
구현 클래스는 다르지만 ApplicationContext를 상속하는 다른 구현 클래스에서 각각 AppConfig.class나 appConfig.xml에서 구성 정보를 읽고 그 클래스에 전달해줄 때는 BeanDefinition이라는 빈 메타정보를 생성해서 전달해주기 때문이다.

 

📌 BeanDefinition

  • BeanDefinition은 구성 정보를 담는 추상화
  • 모두 같은 BeanDefinition을 스프링 컨테이너는 전달받기 때문에 자바 코드에서 받아온건지, xml인건지는 스프링 컨테이너는 알 필요 없다! ➡ 팩토리 메서드 방식

여기까지도 역할과 구현의 분리가 잘 되어있다!

 


❗ Singleton

// 요청을 받을 때 마다 새로운 객체를 만드는 것은 매우 비효율적
public MemberRepository memberRepository() {
    return new MemoryMemberRepository();
}

 

  • 요청을 받을 때 마다 새로운 객체를 만드는 것 ➡ 엄청난 메모리 낭비
  • 객체를 딱 1개만 생성하고 그것을 공유하는 솔루션 ➡ 싱글톤 패턴

 

싱글톤 패턴은 자원을 공유한다는 큰 장점을 갖고 있다.
그럼에도 불구하고 다양한 문제점 때문에 안티패턴이라고도 불린다.

1. 의존관계상 클라이언트가 구체 클래스에 의존한다. ➡ DIP 위반 (~Impl.getInstance() 이런식이 되기 때문에)
2. DIP를 위반하기 때문에 OCP를 위반할 가능성이 높다.
3. 유연성이 떨어진다.

 

But! 이 단점들을 스프링은 모두 보완해서 관리해준다!
또한 싱글톤 패턴을 우리가 직접 작성할 필요 없이 스프링 컨테이너에서 싱글톤으로 관리해준다!

 

📌 싱글톤 컨테이너

스프링 컨테이너에서 빈을 등록할 때 키와 값 쌍으로 관리를 한다. 그리고 그 관리를 싱글톤으로 한다.
이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 한다.

 

스프링 컨테이너에서 싱글톤이 디폴트이고 대부분 쓰이지만, 싱글톤이 아니고 요청할때마다 새로운 객체를 생성해서 주는 기능도 제공한다.

 

📌 싱글톤을 사용할 때 주의점

  • 상태를 유지(stateful)하게 설계하면 XXX
  • 무상태(stateless)로 설계해야한다. ➡ 스프링은 항상 무상태로 설계!
    특정 클라이언트가 값을 변경하면 모두가 공유하게 되니까 곤란해진다;
// stateful하게 코드를 작성하면 price를 다른 사용자와 공유하는 대참사가 발생한다.
private int price; // 상태를 유지하는 필드

public void order(String name, int price) {
	this.price = price;
}
// stateless
    public int order(String name, int price) {
            return price;
    }

 

📌 @Configuration

스프링 컨테이너를 구성할 때,  AppConfig.class를 전달하지만 그것 그대로 스프링 컨테이너가 쓰는게 아니다.
AppConfig@CGLIB라는 코드로 조작된 인스턴스가 스프링 컨테이너에 등록되고 사용된다.

이렇게 싱글톤이 유지될 수 있다.

AppConfig에 @Configuration을 안쓰고 구성하면, AppConfig@CGLIB가 아니라 hello.core.AppConfig라는 순수한 클래스를 사용하게 된다. 스프링 Bean은 등록되어 있지만 싱글톤을 유지할 수는 없게된다.


❔Quiz

  1. 스프링으로 코드를 작성할 때, ( stateful )하게 작성하면 싱글톤 객체를 사용하면서 문제가 생길 수 있다. 그러므로 ( stateless )하게 작성하야한다.
  2. 스프링 빈 메타정보를 갖는 파일은 ( BeanDefinition )으로 추상화이다.
  3. ( BeanFactory )은 스프링 컨테이너라고 불리며, ApplicationContext의 상위 인터페이스이다.
  4. 요청을 받을 때 마다 새로운 객체를 만들어서 반환하는 것은 매우 비효율적인 방법이다. 이를 보완하는 디자인 패턴으로 1개의 객체 인스턴스를 공유해서 사용하는 패턴을 ( 싱글톤 패턴 )이라고 한다.
  5. 스프링 컨테이너 안에 있는 스프링 빈 저장소는 ( 키:값 ) 형태로 구성되어 있다.
  6. 스프링의 구성 정보를 담는 파일임을 나타내는 annotation은 ( @Configuration )이다. 
  7. AppConfig를 사용하면서 사용 영역과 구성 영역을 구분하므로써 ( SRP 단일 책임 원칙 )을 지킬 수 있다.

Spring 1

EDITOR: OJO

728x90

관련글 더보기