BeanFactory와 ApplicationContext는 무엇이 다를까?

대현·6일 전

JAVA 와 Spring

목록 보기
3/3
post-thumbnail

BeanFactory와 ApplicationContext는 무엇이 다를까?

Spring Container에 대해 공부하다 보면
계속해서 등장하는 두 가지 이름이 있다.

BeanFactory

ApplicationContext

처음에는 이런 생각이 들었다.

둘 다 Spring Bean을 관리하는 것 같은데 대체 무슨 차이지?

지금까지 내가 작성했던 코드에서는 대부분 다음과 같이
ApplicationContext를 사용하고 있었다.

ApplicationContext ac =
        new AnnotationConfigApplicationContext(AppConfig.class);

그리고 Bean을 가져올 때는

MemberService memberService =
        ac.getBean("memberService", MemberService.class);

와 같이 사용했다.

그렇다면 BeanFactory는 무엇이고,
우리가 지금까지 사용하던 ApplicationContext와는 어떤 차이가 있을까?

이번 글에서는

BeanFactory

ApplicationContext

ApplicationContext가 제공하는 부가기능

Spring Container가 다양한 설정 형식을 지원하는 방법

까지 정리해보고자 한다.

BeanDefinition에 대한 내용은 다음 글에서 이어서 다룰 예정이다.


먼저 Spring Container란?

앞에서 Spring Bean을 공부하면서
Spring Container에 대해서도 간단하게 살펴봤다.

예를 들어

ApplicationContext applicationContext =
        new AnnotationConfigApplicationContext(AppConfig.class);

라는 코드를 작성하면

AppConfig.class
        ↓
Spring Container 생성
        ↓
설정 정보 확인
        ↓
Spring Bean 등록
        ↓
Bean 간 의존관계 설정

과 같은 과정이 진행된다.

쉽게 말하면 Spring Container는

애플리케이션에서 사용할 객체들을 생성하고 관리하며, 객체 사이의 관계까지 관리하는 공간

이라고 생각할 수 있다.

그런데 조금 더 정확하게 들어가면
Spring Container와 관련된 대표적인 인터페이스가 두 개 등장한다.

BeanFactory

ApplicationContext

BeanFactory란?

먼저 BeanFactory부터 살펴보자.

BeanFactory

Spring Container의 최상위 인터페이스

라고 할 수 있다.

Spring Bean을 관리하고 조회하는 가장 기본적인 기능을 제공한다.

예를 들어 지금까지 계속 사용했던

getBean()

BeanFactory에서 제공하는 대표적인 기능이다.

MemberService memberService =
        ac.getBean(MemberService.class);

이 코드에서 수행하는

Spring Container에서

MemberService 타입의 Bean을 찾아서

객체를 반환

하는 기능의 핵심이 BeanFactory에 정의되어 있는 것이다.

BeanFactory의 가장 중요한 역할을 간단하게 정리하면

Spring Bean 관리

Spring Bean 조회

라고 할 수 있다.


BeanFactory라는 이름 그대로 생각해보자

이름을 그대로 보면 조금 더 이해하기 쉽다.

Bean + Factory

Bean을 만들고 관리하는 Factory

라고 생각할 수 있다.

Spring Container 내부에 다음 Bean들이 있다고 해보자.

Spring Container
│
├── memberService
├── memberRepository
├── orderService
└── discountPolicy

BeanFactory는 이런 Bean들을 관리하고

getBean()

을 통해 필요한 Bean을 찾을 수 있도록 해주는 핵심 인터페이스다.

그래서 지금까지 우리가 Spring Bean을 조회하면서 사용했던
많은 기능들은 사실 BeanFactory가 제공하는 기능이었다.


그렇다면 ApplicationContext는 무엇일까?

이제 우리가 실제 코드에서 더 자주 봤던
ApplicationContext를 살펴보자.

ApplicationContextBeanFactory의 기능을 이어받는다.

구조를 아주 단순하게 표현하면 다음과 같다.

BeanFactory
     ↑
     │
ApplicationContext

즉,

BeanFactory가 할 수 있는 일

Spring Bean 관리
Spring Bean 조회
getBean()
...

ApplicationContext도 할 수 있다.

그래서 다음 코드가 가능했던 것이다.

ApplicationContext ac =
        new AnnotationConfigApplicationContext(AppConfig.class);

MemberService memberService =
        ac.getBean(MemberService.class);

ApplicationContext 역시
Bean을 조회할 수 있다.


그런데 둘이 똑같다면 왜 ApplicationContext가 있을까?

여기서 자연스럽게 이런 의문이 생겼다.

Bean 관리와 조회를 BeanFactory가 이미 해주는데
ApplicationContext는 왜 따로 존재하지?

애플리케이션을 실제로 개발하다 보면
객체를 만들고 찾는 기능만으로는 부족하기 때문이다.

예를 들어 실제 서비스를 만든다고 생각해보자.

단순히

객체 생성

객체 조회

만 필요한 것이 아니다.

개발 환경과 운영 환경을 구분하고 싶다.

사용자의 언어에 따라 다른 메시지를 보여주고 싶다.

특정 이벤트가 발생했을 때 다른 객체에게 알리고 싶다.

파일이나 외부 Resource를 읽고 싶다.

같은 기능도 필요하다.

그래서 ApplicationContext

BeanFactory의 Bean 관리 기능

+

애플리케이션 개발에 필요한 여러 부가기능

을 제공한다.


ApplicationContext가 제공하는 부가기능

강의에서는 대표적으로 다음 네 가지를 다뤘다.

메시지 소스를 활용한 국제화

환경 변수

애플리케이션 이벤트

편리한 Resource 조회

하나씩 살펴보자.


1. 메시지 소스를 활용한 국제화

첫 번째는 국제화 기능이다.

예를 들어 하나의 서비스를
한국과 미국에서 제공한다고 생각해보자.

한국 사용자에게는

안녕하세요.

라고 보여주고 싶고,

영어권 사용자에게는

Hello.

라고 보여주고 싶다.

물론 직접 코드로

if (country.equals("KR")) {
    return "안녕하세요.";
}

if (country.equals("US")) {
    return "Hello.";
}

와 같이 작성할 수도 있다.

하지만 언어가 많아질수록 관리하기 어려워진다.

Spring에서는 MessageSource와 관련된 기능을 이용해
이러한 국제화 기능을 지원할 수 있다.

쉽게 생각하면

한국 사용자
↓
한국어 Message

미국 사용자
↓
영어 Message

처럼 상황에 맞는 메시지를 가져올 수 있게 해주는 것이다.


2. 환경 변수

두 번째는 환경 정보 관리다.

애플리케이션을 개발하다 보면
같은 프로그램이라도 실행되는 환경에 따라 설정값이 달라질 수 있다.

예를 들어 데이터베이스를 생각해보자.

내 컴퓨터에서 개발할 때

localhost DB

를 사용하다가

운영 서버

실제 운영 DB

를 사용해야 할 수도 있다.

즉 같은 애플리케이션이라도

Local

Development

Test

Production

환경에 따라 다른 설정을 사용할 필요가 있다.

ApplicationContext는 이런 환경 정보를 다룰 수 있는 기능을 제공한다.

Spring을 공부하다 보면 나중에

Profile

Environment

application.properties

application.yml

같은 개념들을 만나게 되는데
이 부분과도 연결되는 내용이라고 볼 수 있다.


3. 애플리케이션 이벤트

세 번째는 Application Event다.

애플리케이션 내부에서 어떤 일이 발생했을 때
다른 객체에게 이를 알려야 하는 상황이 있을 수 있다.

예를 들어 주문이 완료됐다고 해보자.

주문 완료

라는 하나의 사건이 발생하면

재고 감소

사용자 알림 전송

주문 기록 저장

통계 데이터 업데이트

같은 여러 작업이 필요할 수도 있다.

물론 주문 코드 안에서 모든 객체를 직접 호출할 수도 있다.

하지만 이벤트 방식을 사용하면

주문 완료
    ↓
Event 발행
    ↓
┌─────────────┬─────────────┬─────────────┐
↓             ↓             ↓
재고 처리      알림 처리      통계 처리

처럼 이벤트를 발행하고 구독하는 방식으로 설계할 수 있다.

ApplicationContext는 이러한 애플리케이션 이벤트 기능을 편리하게 지원한다.


4. 편리한 Resource 조회

마지막은 Resource 조회 기능이다.

애플리케이션을 개발하다 보면
다양한 위치의 파일이나 데이터를 읽어야 할 수 있다.

예를 들어

파일 시스템의 파일

classpath 내부 파일

외부 URL의 Resource

등이 있다.

ApplicationContext는 이런 Resource들을
일관된 방식으로 조회할 수 있도록 도와준다.

쉽게 말하면

"이 파일이 classpath에 있나?"

"외부 파일인가?"

"URL Resource인가?"

를 각각 완전히 다른 방식으로 처리하기보다는
Spring의 Resource 기능을 통해 편리하게 접근할 수 있도록 하는 것이다.


ApplicationContext의 구조를 보면

조금 더 정확하게 보면
ApplicationContext는 단순히 BeanFactory 하나만 가지고 있는 것이 아니다.

여러 기능을 제공하는 인터페이스들의 역할을 함께 가진다.

개념적으로 보면 다음과 같다.

                       BeanFactory
                           ↑
                           │

MessageSource  ←  ApplicationContext  →  ResourceLoader
                           │
                           │
                  Environment 관련 기능
                           │
                           │
                  Application Event

그래서 ApplicationContext 하나를 사용하면

Bean 관리

Bean 조회

국제화

환경 정보

이벤트

Resource 조회

같은 기능들을 함께 사용할 수 있다.


BeanFactory VS ApplicationContext

이제 둘의 차이를 비교해보면 생각보다 단순하다.

구분BeanFactoryApplicationContext
Spring Bean 관리OO
Spring Bean 조회OO
getBean()OO
국제화기본 핵심 기능 아님O
환경 정보기본 핵심 기능 아님O
Application Event기본 핵심 기능 아님O
Resource 조회기본 핵심 기능 아님O

쉽게 표현하면

BeanFactory

Bean 관리 + Bean 조회

이고,

ApplicationContext

BeanFactory 기능
+
애플리케이션 개발에 필요한 여러 부가기능

이라고 생각할 수 있다.


그래서 우리는 무엇을 사용할까?

실제로 Spring 애플리케이션을 개발하면서
BeanFactory를 직접 사용할 일은 거의 없다고 한다.

대부분은 ApplicationContext를 사용한다.

왜냐하면

Bean 관리

+

환경 설정

+

이벤트

+

Resource

+

국제화

같은 실제 애플리케이션 개발에 필요한 기능들을 함께 제공하기 때문이다.

그래서 지금까지 우리가 사용했던 코드도

ApplicationContext ac =
        new AnnotationConfigApplicationContext(AppConfig.class);

였다.

또는 구체 구현체를 직접 사용해서

AnnotationConfigApplicationContext ac =
        new AnnotationConfigApplicationContext(AppConfig.class);

라고 작성하기도 했다.


AnnotationConfigApplicationContext는 또 무엇일까?

여기서 이름이 하나 더 등장한다.

AnnotationConfigApplicationContext

처음 보면 이름부터 상당히 길다...

하지만 구조를 보면 어렵지 않다.

BeanFactory
      ↑
ApplicationContext
      ↑
AnnotationConfigApplicationContext

BeanFactoryApplicationContext는 인터페이스이고,

AnnotationConfigApplicationContext
애노테이션 기반의 Java 설정을 사용할 수 있는
ApplicationContext 구현체 중 하나라고 이해하면 된다.

그래서

new AnnotationConfigApplicationContext(AppConfig.class);

라고 작성하면

AppConfig.class
↓
Java 설정 정보 읽기
↓
Spring Container 생성
↓
Spring Bean 등록

이 진행된다.


결국 둘 다 Spring Container라고 부른다

여기까지 공부하면 용어가 조금 헷갈릴 수 있다.

BeanFactory

ApplicationContext

Spring Container

의 관계가 무엇일까?

강의에서는

BeanFactory나 ApplicationContext를 Spring Container라고 한다.

라고 정리한다.

다만 실제 개발에서는
BeanFactory를 직접 사용하는 경우가 거의 없기 때문에

ApplicationContext
=
Spring Container

처럼 이야기하는 경우가 많다.

그래서 앞으로 Spring Container라는 표현이 나오면
대부분 ApplicationContext를 생각해도 크게 문제없을 것 같다.


정리

지금까지 내용을 한 번 정리해보자.

BeanFactory
↓
Spring Container의 최상위 인터페이스
↓
Bean 관리
Bean 조회
getBean()

그리고

ApplicationContext
↓
BeanFactory의 기능을 이어받음
↓
Bean 관리
Bean 조회
+
국제화
환경 정보
Application Event
Resource 조회

결론적으로

ApplicationContext는 BeanFactory의 Bean 관리 기능에 애플리케이션 개발에 필요한 여러 부가기능을 더한 인터페이스라고 볼 수 있다.

그리고 실무에서는 대부분

ApplicationContext

를 사용한다.


그런데 Spring은 설정 정보를 어떻게 읽을까?

여기서 또 하나 재미있는 특징이 있다.

Spring Container는
설정 정보를 반드시 Java 코드로만 받아야 하는 것이 아니다.

지금까지는 다음 방식만 사용했다.

new AnnotationConfigApplicationContext(AppConfig.class);

그리고 AppConfig

@Configuration
public class AppConfig {

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

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

처럼 Java 코드로 작성되어 있었다.

그런데 Spring은 설정 형식에 상당히 유연하게 설계되어 있다.

강의에서는 대표적으로

Java Code

XML

Groovy

기타 형식

등의 설정 정보를 사용할 수 있다고 설명한다.


Java 설정

우리가 지금까지 사용했던 것이 바로
애노테이션 기반 Java 설정이다.

@Configuration
public class AppConfig {

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

그리고 이 설정을

new AnnotationConfigApplicationContext(AppConfig.class);

에 넘긴다.

AppConfig.class
↓
AnnotationConfigApplicationContext
↓
Spring Container

지금 Spring을 공부하면서 가장 자주 보게 되는 방식이기도 하다.


XML 설정

Spring에서는 XML을 이용해서도
Bean 설정 정보를 작성할 수 있다.

예를 들어 Java에서

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

라고 작성했던 것을 XML에서는 대략 다음처럼 표현할 수 있다.

<bean id="memberRepository"
      class="hello.core.member.MemoryMemberRepository" />

또 의존관계 역시 XML로 정의할 수 있다.

<bean id="memberService"
      class="hello.core.member.MemberServiceImpl">

    <constructor-arg
        name="memberRepository"
        ref="memberRepository" />

</bean>

결국 Java 설정에서 했던

new MemberServiceImpl(memberRepository());

라는 객체 구성 정보를
XML로 표현한 것이라고 볼 수 있다.


XML 설정을 사용하려면?

Java 설정에서는

AnnotationConfigApplicationContext

를 사용했다.

XML 설정에서는

GenericXmlApplicationContext

를 사용할 수 있다.

예를 들어

ApplicationContext ac =
        new GenericXmlApplicationContext("appConfig.xml");

처럼 XML 설정 파일을 넘길 수 있다.

그리고 이후 Bean을 조회하는 방법은 똑같다.

MemberService memberService =
        ac.getBean("memberService", MemberService.class);

여기서 꽤 중요한 점이 보인다.

설정 방식이

Java

이든

XML

이든

우리가 Bean을 사용하는 방식은 거의 달라지지 않는다.

ac.getBean(...)

을 그대로 사용할 수 있다.


그런데 요즘도 XML을 사용할까?

최근 Spring Boot 기반 프로젝트에서는
XML 설정을 예전만큼 많이 사용하지 않는다.

대부분

@Configuration

@Bean

@Component

@Service

@Repository

@RestController

같은 Java와 Annotation 기반의 설정을 많이 사용한다.

그래서 처음 Spring을 배우는 입장에서는

"XML은 안 배우면 되는 것 아닌가?"

라는 생각도 들었다.

하지만 오래된 Spring 프로젝트, 즉 Legacy Project에서는
아직 XML 기반의 설정을 만날 수 있다.

또 XML은 Java 코드를 다시 컴파일하지 않고도
설정 정보를 변경할 수 있다는 특징도 있다.

따라서

XML을 주력으로 사용해야 한다.

기보다는

Spring은 XML로도 설정할 수 있고, 레거시 프로젝트에서는 실제로 만날 수도 있다.

정도로 이해하고 넘어가면 될 것 같다.


여기서 생긴 가장 큰 의문

그런데 생각해보면 조금 이상하다.

Java에서는

@Configuration
@Bean

으로 설정한다.

XML에서는

<bean>

으로 설정한다.

형식이 완전히 다르다.

그런데 Spring Container에서는 결국 둘 다

Spring Bean

으로 똑같이 관리한다.

구조를 보면

AppConfig.class
        ↓
AnnotationConfigApplicationContext
        ↓
ApplicationContext

또는

appConfig.xml
        ↓
GenericXmlApplicationContext
        ↓
ApplicationContext

가 된다.

그렇다면 이런 의문이 생긴다.

Spring은 Java 코드와 XML처럼 완전히 다른 형식의 설정 정보를 어떻게 똑같이 처리할 수 있는 걸까?

개인적으로 이 부분이 꽤 흥미로웠다.

Spring Container 입장에서는

Java 설정인지

XML 설정인지

Groovy 설정인지

를 각각 전부 다르게 처리한다면
설정 형식이 하나 추가될 때마다 Container 자체도 계속 변경되어야 할 것 같다.

그런데 Spring은 이 문제를
하나의 추상화를 통해 해결한다.

그 중심에 있는 것이 바로

BeanDefinition

이다.


다음 글: BeanDefinition

이번 글에서는 여기까지만 정리하고자 한다.

이번에 공부한 내용을 가장 짧게 정리하면

BeanFactory
→ Spring Bean 관리와 조회의 핵심 기능

ApplicationContext
→ BeanFactory 기능
  + 실제 애플리케이션에 필요한 다양한 부가기능

이라고 할 수 있다.

그리고

BeanFactory

ApplicationContext

둘 모두 Spring Container라고 부를 수 있지만,
실제 개발에서는 대부분 ApplicationContext를 사용한다고 보면 된다.

또 하나 중요한 점은

Spring Container는 특정 설정 형식 하나에 묶여 있지 않다.

는 것이다.

Java

XML

Groovy

...

와 같이 서로 다른 형태의 설정 정보를 받아들일 수 있도록
유연하게 설계되어 있다.

그런데 대체 어떻게 이런 것이 가능할까?

Java 설정
        ↘
         
          ???
         
        ↗
XML 설정

        ↓

Spring Container

여기에서 ???에 들어가는 핵심 개념이
바로 BeanDefinition이다.

다음 글에서는 Spring이 다양한 설정 정보를 어떻게 하나의 형태로 추상화하는지,
그리고 Spring Bean 설정 메타정보인 BeanDefinition이 무엇인지 정리해보고자 한다.

profile
도전을 멈추지 않는 개발자

0개의 댓글