본문 바로가기

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

[2024 Spring Boot 스터디] 정성원 #2 주차 - 스프링 컨테이너

반응형

1. 스프링 컨테이너

이전 주차에서 이야기했던 컨테이너에 대해 자세히 알아보자.

일단은 ApplicationContext 인터페이스가 스프링 컨테이너라고 알고 가자.

// 스프링 컨테이너 생성하기
ApplicationContext applicationContext = new AnnoationConfigApplicationContext(AppConfig.class);

 

ApplicationContext는 위와 같은 방식으로 생성할 수 있다. AppConfig는 지난 주차에 만들었던 클래스를 참고하자.
스프링 컨테이너는 XML을 기반으로 만들 수도 있고, 위 예시처럼 어노테이션 기반의 자바 설정 클래스로도 만들 수 있다.

스프링 컨테이너의 생성 과정

스프링 컨테이너가 어떻게 생성되는지 알아보자.

먼저 스프링 빈 저장소 테이블을 가지는 스프링 컨테이너를 생성한다.
그 다음 매개변수로 넘겨 준 빈 구성 정보가 담긴 클래스를 토대로 스프링 컨테이너의 빈 저장소에 빈을 저장한다.

 

구성 정보로 넘긴 클래스에 명시된 내용에 따라, 빈 이름을 지정하고 인스턴스를 스프링이 생성,
빈 저장소에 인스턴스 참조를 등록한다. 이것이 스프링 빈 등록이다.

2. 스프링 빈

앞서 스프링 빈에 대한 언급을 했었는데, “그래서 스프링 빈이 무엇인가?"에 대한 질문에는 아직 답하지 않았다. 이제 이에 대한 이야기를 해 보자.

 

스프링 빈이란, “스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트”다.

즉, 스프링 컨테이너가 관리하는 인스턴스를 말한다.
근데 그냥 사용자가 인스턴스를 관리하면 되지 않은가? 굳이 스프링 빈을 사용하는 이유가 무엇일까?

 

이는 스프링 빈에서 다 해주기 때문이다.
이전에 언급했던 의존성 주입(DI; Dependency Injection)제어의 역전(Inversion of Control)도 관련이 있다.
개발자가 모두 할 수 있는 일이지만, 프레임워크가 이를 모두 맡아 처리해주므로,

우리는 복잡한 실행 흐름을 관리할 필요 없이 실제 필요한 비즈니스 로직을 만드는 것에 더 집중할 수 있다.

스프링 빈 이름

그렇다면 빈 저장소에 빈 정보를 어떻게 등록하는가?

 

빈 저장소 테이블을 보면, 빈 이름과 빈 객체 컬럼이 있다.

 

기본적으로 빈 이름은 구성 정보 클래스에 적힌 메소드 이름을 사용한다. 하지만 메소드 이름 대신 다른 이름을 직접 부여할 수도 있다. @Bean(name="memberService2) 와 같이 name 키를 사용해 지정하면 된다. 주의해야 할 점은 빈 이름은 Unique해야 한다는 것이다. 여러 빈에 같은 이름을 부여하면 설정에 따라 빈 하나만 등록되어 런타임에 오류가 발생하거나 스프링 실행 시 오류를 뱉어낸다.

 

빈 객체는 일반적으로 메소드에서 반환하는 리턴값을 그대로 사용한다. 다만 특정 상황에서는 우리가 설정한 클래스가 그대로 인스턴스로 생성되는 것이 아니라 Proxy 형태로 감싸진 새 클래스를 생성하기도 한다. 이는 좀 깊은 내용이니 궁금하면 따로 찾아보도록 하자.

@See also: Spring Bean 과 CGLIB proxy

 

Spring Bean 과 CGLIB proxy

AbstractAutoProxyCreatorSpring 에서는 AOP 기능을 수행하기위해 객체를 빈으로 등록하기 전에 Proxy 로 감싸진 클래스를 생성하여 빈으로 관리한다. 이때 사용되는 Proxy Library 가 CGLIB 와 Dynamic Proxy 이다.

woooongs.tistory.com

 

스프링 빈 조회

일단 스프링 컨테이너 ApplicationContext ac = new ... 로 생성했다는 전제를 가져간다.

  • ac.getBeanDefinitionNames() : 스프링에 등록된 모든 빈 이름을 조회한다.
  • ac.getBean : 빈 이름으로 빈 객체(인스턴스)를 조회한다.
    • ac.getBean(빈이름, 타입)
    • ac.getBean(타입)
    • 조회된 빈이 없으면 NoSuchBeanDefinitionException 이 발생한다.
  • ac.getBeansOfType(타입) : 빈 타입으로 빈을 조회한다.
  • 사용자가 정의한 빈과 스프링이 내부에서 사용하는 빈은 getRole() 로 구분할 수 있다.
    • ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
    • ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈

3. BeanFactory와 ApplicationContext

이전에 ApplicationContext가 스프링 컨테이너라고 했던 것 기억하는가?

사실 정확히 말하자면 BeanFactory가 최상위 인터페이스고, ApplicationContext는 이를 상속받은 클래스다.

그렇다면 왜 BeanFactory 대신 ApplicationContext를 사용하는가?

 

이 그림을 보자. ApplicationContext는 BeanFactory의 빈 관리 및 조회 기능뿐만 아니라 각 인터페이스의 기능인 국제화, 환경변수, 이벤트 관리, 리소스 조회 등 많은 편리한 부가기능을 제공하고 있다. 그래서 빈 관리 자체는 BeanFactory지만 부가기능이 많은 ApplicationContext를 사용하고, 이를 컨테이너라고 부른다.

4. 싱글톤

스프링은 온라인 서비스를 위해 만들어진 프레임워크다. 대부분이 웹서버 백엔드 애플리케이션이지만, 다른 플랫폼도 얼마든지 가능하다. 그래도 가장 많이 사용되는 웹을 기준으로 이야기해보자.

 

웹 애플리케이션에서 중요한 건 많은 클라이언트로부터 동시에 요청을 받아 처리해야 한다는 것이다.

그런데 만약 매 요청마다 서비스 로직이 담긴 객체를 생성한다고 가정해보자. 초당 100개의 트래픽이 들어온다고 할 때, 매 초마다 100개의 객체가 생성되고 소멸할 것이다. 트래픽의 크기가 더 커진다면? 가히 상상할 수 없을 정도의 비용과 부하가 걸릴 것이 뻔하다.

 

그런데 우리가 만든 서비스 로직은 굳이 새로운 객체를 계속 만들 필요가 없다. 딱 하나만 생성하고 이를 공유해서 사용하면 객체 생성 및 소멸에 따른 리소스 낭비가 없지 않겠는가? 우리는 이런 방식의 인스턴스 관리를 싱글톤 패턴이라고 한다.

@See also: 싱글톤(Singleton) 패턴이란?

 

싱글톤(Singleton) 패턴이란?

이번 글에서는 디자인 패턴의 종류 중 하나인 싱글톤 패턴에 대해 알아보자. 싱글톤 패턴이 무엇인지, 패턴 구현 시 주의할 점은 무엇인지에 대해 알아보는 것만으로도 많은 도움이 될 것이라

tecoble.techcourse.co.kr

 

말은 좋아 보이지만 궁극적으로 전체 코드 구조의 유연성을 해치는 단점도 있으므로 패턴 적용시에는 신중할 필요가 있다. 싱글톤 패턴의 장단점은 위 참조 게시글을 읽어보자.

그래서 갑자기 싱글톤 이야기를 왜 했냐?

바로 스프링 컨테이너의 빈이 싱글톤으로 관리되기 때문이다. (설정에 따라 변경 가능하다.)

 

웹 서버로서의 기능을 효율적으로 수행하기 위해 싱글톤으로 인스턴스를 생성했으니, 이 인스턴스는 무조건 무상태(Stateless)로 설계해야만 한다. 중요하니 두 번 말한다.

 

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

 

싱글톤 인스턴스가 상태를 가지는걸 비유하자면, 여러 사람이 하나의 장부를 동시에 쓰는 셈이다.

A가 장부에 1만원 지출을 표시했다. 그런데 B가 거의 완전히 동일한 시점에 A가 적어둔 칸에 2만원 지출을 같이 표시하고 있다. 같은 칸에 두 사람이 볼펜으로 필기한다면, 그 칸에 적힌 값이 항상 정상적인 값이라고 할 수는 없을 것이다. A 입장에선 장부에 1만원 외상을 걸어두고 집에서 돈을 가져왔더니, 장부에 내 외상 값이 2만원이 되어 있으니, 황당할 노릇일 것이다.

 

중요하니 세 번 말한다. 싱글톤 인스턴스는 무조건 무상태(Stateless)로 설계해야만 한다.

5. @Configuration

다음 코드를 보자.

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

 

위 코드에서 new memberRepository()는 총 3번 호출된다.

  1. public MemberRepository memberRepository()
  2. orderService() 내 생성자 파라미터 memberRepository()
  3. memberService() 내 생성자 파라미터 memberRepository()

new를 3번이나 사용했으니, 3개의 MemoryMemberRepository가 생성되어 싱글톤이 깨지는 것처럼 보인다. Java 코드 자체에서 메모리를 할당하고 있지 않은가? 스프링은 이를 어떻게 해결할까?

 

스프링은 CGLIB이라는 바이트코드 조작 라이브러리를 사용해 @Configuration이 붙은 클래스를 상속하는 임의의 클래스를 만들어, 그 클래스의 인스턴스를 스프링 빈으로 등록한다. 이때 만든 임의의 클래스가 인스턴스의 싱글톤을 보장해준다.

@See also: [Spring] 싱글톤 컨테이너 : CGLIB

 

[Spring] 싱글톤 컨테이너 : CGLIB

스프링 컨테이너에 빈(Bean)을 등록하면 기본적으로 싱글톤 패턴으로 빈을 관리하게 된다. 따라서 해당 빈을 요청하게 되면 항상 동일한 빈을 반환해주는 것을 확인할 수 있다. 물론 기본 빈 등록

sorjfkrh5078.tistory.com

 

사실 이건 굉장히 복잡하고 어려운 내용이니, 간략하게 기억하자.

스프링 설정 정보는 항상 @Configuration을 붙여두자.

 


지난 포스트

 

[2024 Spring Boot 스터디] 정성원 #1 주차 - 객체 지향 설계와 Spring

0. 들어가며  Spring은 Java 개발자라면 한번쯤은 들어본 단어다. 그래서 Spring은 무엇인가?관련 공부를 하지 않았다면 대략 “Java로 만들어진 Web Backend Framework” 라고만 알고 있을 것이다. 이번 스

cs-kookmin-club.tistory.com

 

반응형