Spring: Part 5. Spring Configuration

2026. 7. 29. 15:34·🍃SpringBoot

Part 5. Spring Configuration

5.1 Java Config와 XML Config

앞에서 Spring은 Bean을 생성하고 관리하며, Scope를 통해 Bean의 생명주기를 제어한다는 것을 살펴보았습니다. 그렇다면 Spring은 이러한 Bean 정보를 어디에 정의할까요? Spring은 Bean을 생성하고 관리하기 위해 설정(Configuration)이 필요합니다. 초기 Spring 프로젝트에서는 XML을 통해 Bean을 등록했지만, 현재는 Java 기반의 설정(Java Config)이 표준으로 사용됩니다. 이번 장에서는 XML Config와 Java Config의 차이점을 살펴보고, Spring Boot가 Java Config를 기본으로 사용하는 이유를 알아봅니다.

1. Spring Configuration이란?

Spring Configuration은 Spring 컨테이너에 어떤 Bean을 등록하고 어떻게 생성 및 주입할 것인지를 정의하는 설정 정보를 의미합니다. Configuration은 컨테이너에 다음과 같은 질문에 관한 답을 제공합니다.

  • 어떤 객체를 Bean으로 등록할 것인가?
  • 객체는 어떤 과정과 생성자로 생성할 것인가?
  • 객체 간 의존성(Dependency)은 어떻게 연결할 것인가?

2. XML Config와 Java Config 비교

1) XML Config

Spring 초기 방식으로, XML 파일 내부에 `<beans>` 및 `<bean>` 태그를 이용해 Bean의 이름, 클래스, 의존성을 직접 명시합니다.

<beans>
    <bean id="memberRepository" class="com.example.MemberRepository"/>
    <bean id="memberService" class="com.example.MemberService">
        <constructor-arg ref="memberRepository"/>
    </bean>
</beans>
  • 장점: 코드 수정 없이 XML 변경만으로 설정 수정 가능, 비즈니스 코드와 설정 코드의 명확한 분리.
  • 단점: 프로젝트 규모 증가 시 XML 파일 비대화, 타입 안전성 부족(문자열 기반 클래스 지정), 컴파일 시점 오타 감지 불가능, 자동 완성/리팩터링 등 IDE 지원 제한.

2) Java Config

Java 클래스에 @Configuration과 @Bean 어노테이션을 사용하여 Bean을 등록하는 최신 표준 방식입니다.

@Configuration
public class AppConfig {

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

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
}
  • 장점: 타입 안전성(컴파일 시점에 오류 확인), 뛰어난 IDE 지원(자동 완성, 안전한 리팩터링, 코드 이동), 메서드/조건문/반복문 활용 등 객체지향적인 설정 가능.

3. XML vs Java Config 종합 비교

항목 XML Config Java Config
설정 방식 XML 문서 파일 Java 코드 + 어노테이션
타입 안전성 낮음 (문자열 기반) 높음
컴파일 타임 검사 불가능 (런타임에 오류 발견) 가능
IDE 지원 (리팩터링/자동완성) 제한적 우수
현재 사용 빈도 매우 낮음 매우 높음 (표준)

4. Spring Boot와 실무에서의 활용

Spring Boot가 Java Config를 사용하는 이유

Spring Boot는 Java Config, Component Scan, Auto Configuration(자동 설정)을 기본으로 설계되었습니다. 별도의 XML 파일 작성 없이 메인 클래스의 @SpringBootApplication 어노테이션 하나로 필요한 수많은 Bean을 자동으로 탐색하고 등록합니다.

실무에서의 Bean 등록 패턴

실무에서는 다음과 같은 조합을 주로 사용합니다.

  1. 대부분의 일반 Bean: @Component, @Service, @Repository, @Controller 등을 활용한 Component Scan으로 자동 등록합니다.
  2. 외부 라이브러리나 특별한 Bean: @Configuration 클래스 내부에서 @Bean 메서드로 직접 등록합니다.
  3. XML의 위치: 레거시(Spring Legacy) 프로젝트나 오래된 외부 라이브러리 유지보수 목적 외에 신규 프로젝트에서는 XML을 거의 작성하지 않습니다.

정리

  • Spring Configuration: 컨테이너가 Bean을 생성·관리하기 위해 필요한 설정 정보입니다.
  • Java Config의 우수성: 초기의 XML 방식과 달리 컴파일 시점의 타입 검사가 가능하며, IDE 자동 완성 및 안전한 리팩터링을 제공합니다.
  • Spring Boot 표준: Java Config, Component Scan, Auto Configuration을 기반으로 구동됩니다.

다음 장에서는 Java Config의 핵심 어노테이션인 @Configuration이 실제로 어떤 역할을 수행하는지와 싱글톤을 보장하는 내부 동작 원리(CGLIB Proxy)에 대해 자세히 살펴보겠습니다.

핵심 질문

Q1. Spring Configuration이란 무엇인가요?

 Spring 컨테이너에 어떤 Bean을 등록하고 어떻게 생성 및 의존관계를 주입할 것인지를 정의하는 설정 정보입니다.

 

Q2. XML Config와 Java Config의 가장 큰 차이점은 무엇인가요?

XML Config는 XML 파일의 문자열 기반으로 Bean을 정의하여 컴파일 시점 오류 검증이 어렵지만, Java Config는 Java 코드를 활용하므로 타입 안전성과 IDE의 리팩터링/자동 완성 지원이 뛰어납니다.

 

Q3. 현재 Spring Boot에서 기본으로 사용하는 설정 방식은 무엇인가요?

Java Config를 기본으로 사용하며, Component Scan과 Auto Configuration을 결합하여 Bean을 자동으로 탐색하고 등록합니다.

 

Q4. XML Config를 여전히 알아야 하는 이유는 무엇인가요?

신규 프로젝트에는 거의 쓰이지 않지만, 레거시 시스템 유지보수나 오래된 Spring 기반 모듈 및 라이브러리 설정을 다룰 때 접할 수 있기 때문입니다.

5.2 @Configuration의 동작 원리

앞에서 Java Config는 Java 코드를 이용하여 Spring Bean을 등록하는 방식이라는 것을 살펴보았습니다. Java Config의 중심에는 @Configuration과 @Bean이 있습니다. 그렇다면 @Configuration은 단순히 설정 클래스임을 표시하는 어노테이션일까요? 실제로는 그보다 훨씬 중요한 역할을 수행합니다. 이번 장에서는 @Configuration의 역할과 내부 동작 원리를 살펴보고, 왜 반드시 필요한지 알아봅니다.

1. @Configuration이란?

@Configuration은 Spring의 설정 클래스(Configuration Class)임을 나타내는 어노테이션입니다.
Spring은 이 클래스를 읽어 Bean을 생성하고 컨테이너에 등록합니다.

@Configuration
public class AppConfig {

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

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

Spring은 AppConfig를 일반 클래스가 아닌 설정 클래스로 인식하며, 각 @Bean 메서드를 실행하여 반환된 객체를 Spring 컨테이너에 등록합니다.

2. @Configuration이 없다면? (Singleton 파괴)

만약 다음과 같이 @Configuration을 제거하고 일반 클래스처럼 작성하면 어떻게 될까요?

// @Configuration 없음!
public class AppConfig {

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

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

    @Bean
    public OrderService orderService() {
        return new OrderService(memberRepository());
    }
}

문제점: 싱글톤(Singleton)이 깨짐

겉으로 보기에는 하나의 MemberRepository를 공유할 것처럼 보입니다.
하지만 @Configuration이 없으면 해당 클래스는 일반 Java 클래스로 동작합니다.

  • memberService() 호출 ➔ memberRepository() 실행 ➔ 새 객체 A 생성
  • orderService() 호출 ➔ memberRepository() 실행 ➔ 새 객체 B 생성

즉, MemberRepository가 호출될 때마다 새로운 객체가 생성되어 Spring의 핵심 원칙인 Singleton이 깨지는 문제가 발생합니다.

3. Spring의 해결책: 바이트코드 조작 프록시 (CGLIB Proxy)

@Configuration이 붙으면 Spring은 설정 클래스를 그대로 사용하지 않고,
CGLIB 바이트코드 조작 라이브러리를 사용하여 설정 클래스를 상속받은 프록시 객체(Proxy Object)를 생성합니다.

프록시의 @Bean 메서드 호출 가로채기

프록시 객체는 @Bean 메서드 호출을 가로채서 다음과 같이 동작합니다.

  1. 컨테이너 조회: @Bean 메서드(예: memberRepository())가 호출되면 먼저 Spring 컨테이너에 해당 Bean이 이미 등록되어 있는지 확인합니다.
  2. 이미 존재하는 경우: 새로 생성하지 않고 기존에 등록된 Bean을 그대로 반환합니다.
  3. 최초 호출인 경우: 실제 AppConfig에 정의된 생성 로직을 호출하여 객체를 생성 ➔ 컨테이너에 저장 ➔ 반환합니다.

이러한 메커니즘을 통해 개발자가 Java 메서드를 직접 호출하는 것처럼 작성하더라도 항상 싱글톤이 보장됩니다.

4. @Component와 @Configuration의 차이

@Configuration도 내부적으로 @Component를 포함하고 있어 Component Scan의 대상이 됩니다.
하지만 프록시 생성 여부에서 명확한 차이가 있습니다.

구분 @Component @Configuration
목적 일반 Bean 등록 설정 클래스 등록
프록시 생성 여부 프록시 생성 안 함 CGLIB 프록시 생성
@Bean 메서드 간 호출 시 싱글톤 보장 안 됨 (새 객체 생성) 싱글톤 보장됨 (컨테이너 조회)

따라서 @Bean 메서드가 서로를 호출하여 의존관계를 주입하는 설정 클래스라면 반드시 @Configuration을 사용해야 합니다.

5. @Configuration(proxyBeanMethods = false)

Spring Boot의 자동 설정(Auto Configuration) 클래스에서는 다음과 같은 옵션을 자주 볼 수 있습니다.

@Configuration(proxyBeanMethods = false)
public class AppConfig {
    // ...
}
  • 역할: CGLIB 프록시 객체를 생성하지 않고 메서드를 직접 호출하게 만듭니다.
  • 장점: 프록시 생성 단계를 생략하므로 애플리케이션 구동 시 약간의 성능 최적화를 기대할 수 있습니다.
  • 주의사항: @Bean 메서드 간에 서로를 직접 호출하여 의존관계를 주입하는 로직이 있다면 싱글톤이 깨지므로 절대 사용하면 안 됩니다. @Bean 메서드가 서로를 호출하지 않는 독립적인 설정일 때만 안전하게 사용할 수 있습니다.

정리

  • @Configuration의 핵심 역할: 설정 클래스로 지정함과 동시에 CGLIB 프록시를 생성하여 @Bean 메서드의 싱글톤을 보장합니다.
  • 프록시 동작: @Bean 메서드 호출을 가로채 이미 컨테이너에 등록된 Bean이 있으면 해당 Bean을 반환하고, 없으면 새로 생성하여 컨테이너에 저장합니다.
  • proxyBeanMethods = false: @Bean 메서드 간 호출이 없는 독립적인 설정 클래스에서 프록시 생성 비용을 아끼기 위한 최적화 옵션입니다.

다음 장에서는 설정 클래스에서 실제 Bean을 등록하는 핵심 어노테이션인 @Bean의 동작 방식과 활용 방법을 자세히 살펴보겠습니다.

핵심 질문

Q1. @Configuration의 역할은 무엇인가?

Spring의 설정 클래스를 정의하는 어노테이션이며, CGLIB 프록시를 생성하여 @Bean 메서드를 통해 등록되는 객체의 Singleton을 보장하는 역할을 한다.

 

Q2. @Configuration이 일반 @Component와 다른 점은 무엇인가?

@Configuration은 CGLIB 프록시를 생성하여 @Bean 메서드 호출을 가로채 Singleton을 보장한다. 일반 @Component는 이러한 프록시 생성 기능을 제공하지 않는다.

 

Q3. @Configuration 없이 @Bean만 사용하면 어떤 문제가 발생할 수 있는가?

@Bean 메서드 간 직접 호출이 일어날 경우 메서드가 일반 Java 메서드처럼 실행되어 호출될 때마다 새로운 객체가 생성되므로 Singleton이 깨질 수 있다.

 

Q4. proxyBeanMethods = false는 언제 사용하는가?

@Bean 메서드가 서로를 직접 호출하지 않는 설정 클래스에서 프록시 생성을 생략하여 애플리케이션의 구동 성능을 최적화하고자 할 때 사용한다.

5.3 @Bean의 동작 방식

앞에서 @Configuration은 설정 클래스를 정의하고, @Bean 메서드의 호출을 관리하여 Singleton을 보장한다는 것을 살펴보았습니다. 그렇다면 실제로 Spring 컨테이너에 Bean을 등록하는 핵심 주체는 무엇일까요? 바로 @Bean입니다. @Bean은 Java Config에서 가장 핵심적인 어노테이션이며, 개발자가 객체 생성 과정을 직접 제어할 수 있도록 해줍니다. 이번 장에서는 @Bean의 역할과 동작 방식, 그리고 언제 사용하는지 살펴봅니다.

1. @Bean이란?

@Bean은 메서드의 반환 객체를 Spring Bean으로 등록하는 어노테이션입니다. Spring은 애플리케이션이 시작될 때 @Bean이 붙은 메서드를 실행한 뒤 반환된 객체를 Spring 컨테이너에 등록합니다.

@Configuration
public class AppConfig {

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

Bean 등록 과정

  1. Spring 시작
  2. @Configuration 설정 클래스 검색
  3. @Bean 메서드 실행
  4. 객체 생성 및 반환
  5. Spring Container에 등록

등록된 Bean은 이후 다른 Bean에서 자유롭게 의존관계를 주입받아 사용할 수 있습니다.

2. Bean 이름 결정 방식

1) 기본 이름

특별한 설정이 없으면 메서드 이름이 Bean 이름으로 사용됩니다.

@Bean
public MemberRepository memberRepository() {
    return new MemberRepository();
}
// Bean 이름: memberRepository

2) 이름 직접 지정

필요에 따라 이름을 직접 지정할 수도 있습니다.

@Bean("repository")
public MemberRepository memberRepository() {
    return new MemberRepository();
}
// Bean 이름: repository

 

실무 Tip: 특별한 이유가 없는 한 규칙성을 유지하기 위해 기본 이름(메서드명)을 사용하는 것이 일반적입니다.

 

3. @Bean vs @Component

두 어노테이션은 모두 Spring Bean을 등록하지만, 등록 방식에서 명확한 차이가 있습니다.

구분 @Component @Bean
등록 방식 Component Scan을 통한 자동 등록 개발자가 직접 코드 작성하는 수동 등록
적용 대상 클래스 (Class) 메서드 (Method)
주요 용도 직접 개발한 서비스, 리포지토리, 컨트롤러 외부 라이브러리, 복잡한 생성 로직을 가진 객체

4. 언제 @Bean을 사용할까?

실무에서는 대부분 @Component를 사용하지만, 다음과 같은 경우엔 @Bean 수동 등록이 적합합니다.

 1) 외부 라이브러리 등록

외부 라이브러리 클래스(예: Jackson의 ObjectMapper)는 소스 코드를 수정할 수 없으므로 @Component를 붙일 수 없습니다.

@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper();
}

2) 객체 생성 과정이 복잡한 경우

객체 생성 전 추가 설정이나 여러 파라미터 조립이 필요한 경우 메서드 내부에서 자유롭게 제어할 수 있습니다.

@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    // 추가 설정 (Timeout, Interceptor 등)
    return restTemplate;
}

3) 조건에 따라 동적으로 Bean 생성

Java 코드이므로 조건문(if), 환경 변수 등에 따라 실행 시점에 다른 객체를 반환하도록 구성할 수 있습니다.

@Bean
public PaymentService paymentService() {
    if (useKakao()) {
        return new KakaoPaymentService();
    }
    return new TossPaymentService();
}

5. @Bean 메서드의 의존성 주입 및 생명주기

1) 메서드 매개변수를 통한 의존성 주입

@Bean 메서드의 매개변수로 필요한 타입을 선언하면, Spring 컨테이너가 해당하는 Bean을 찾아서 자동으로 전달해 줍니다 (생성자 주입과 동일).

@Bean
public MemberService memberService(MemberRepository repository) {
    return new MemberService(repository);
}

2) @Bean 메서드 간 직접 호출

@Configuration 설정 클래스 내부라면 메서드를 직접 호출하더라도 CGLIB 프록시가 가로채어 싱글톤을 보장합니다.

@Bean
public MemberService memberService() {
    return new MemberService(memberRepository()); // 항상 같은 memberRepository Bean 반환
}

3) 생명주기(Lifecycle)

@Bean으로 등록된 객체도 일반 Bean과 마찬가지로 동일한 생명주기를 거칩니다.

 

실무 Scope 선택 및 사용 기준

  • @Component 사용: Service, Repository, Controller, Component 등 직접 개발한 일반적인 클래스
  • @Bean 사용: 외부 라이브러리 객체, 공통 Configuration 객체, 동적 조건이나 복잡한 설정으로 생성되는 객체

정리

  • @Bean의 정의: 메서드의 반환 객체를 Spring Bean으로 수동 등록하는 어노테이션입니다.
  • 수동 등록의 목적: 외부 라이브러리 등록, 복잡한 초기화 설정, 조건부 객체 생성 시 유용하게 쓰입니다.
  • 싱글톤 보장: @Configuration과 함께 사용 시 메서드 호출을 프록시가 제어하여 항상 동일한 인스턴스를 반환합니다.

다음 장에서는 @Configuration의 핵심 기술인 CGLIB와 설정 클래스 프록시를 통해 Spring이 Singleton을 어떻게 보장하는지 내부 구현 관점에서 자세히 살펴보겠습니다.

핵심 질문

Q1. @Bean의 역할은 무엇인가요?

메서드의 반환 객체를 Spring 컨테이너에 Bean으로 등록하는 역할을 합니다.

 

Q2. @Bean과 @Component의 차이점은 무엇인가요?

@Component는 Component Scan을 통해 클래스 단위로 자동으로 Bean을 등록하고, @Bean은 메서드 단위에서 개발자가 객체 생성 및 초기화 과정을 직접 제어하여 수동으로 Bean을 등록합니다.

 

Q3. 어떤 경우에 @Bean을 사용해야 하나요?

소스 코드를 수정할 수 없는 외부 라이브러리 객체를 등록하거나, 객체 생성 과정이 복잡한 경우, 또는 실행 조건에 따라 다른 객체를 동적으로 생성해야 하는 경우에 사용합니다.

 

Q4. @Configuration과 함께 @Bean을 사용하면 어떤 장점이 있나요?

Spring이 CGLIB 프록시를 통해 @Bean 메서드 호출을 가로채므로, 메서드를 직접 호출하더라도 새로운 객체가 생성되지 않고 동일한 Singleton 인스턴스가 보장됩니다.

 

Q5. @Bean 메서드의 매개변수에 선언된 객체는 어떻게 주입되나요?

Spring 컨테이너가 해당 매개변수 타입에 맞는 Bean을 탐색하여 자동으로 전달해 주며,이는 생성자 주입 방식과 동일하게 동작합니다.

 

Q6. @Bean으로 등록한 객체에서도 @PostConstruct나 @PreDestroy 같은 생명주기 콜백이 동작하나요?

네, @Bean으로 등록된 객체도 Spring 컨테이너가 생명주기를 관리하므로 동일하게 @PostConstruct 및 @PreDestroy 콜백이 정상적으로 동작합니다.

5.4 CGLIB와 설정 클래스 프록시

앞에서 @Configuration과 @Bean을 함께 사용하면 Spring이 Singleton을 보장한다는 것을 살펴보았습니다.

하지만 Java 코드 자체만 보면 이상한 점이 있습니다.

@Configuration
public class AppConfig {

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

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

    @Bean
    public OrderService orderService() {
        return new OrderService(memberRepository());
    }
}

memberService()와 orderService()는 모두 memberRepository()를 direct 호출하고 있습니다. 일반적인 Java 코드라면 memberRepository()가 호출될 때마다 새로운 객체가 생성되어야 합니다. 하지만 실제로는 단 하나의 MemberRepository Bean만 생성되어 공유됩니다. Spring은 이를 어떻게 보장할까요? 이번 장에서는 Spring이 사용하는 CGLIB 기반 설정 클래스 프록시의 동작 원리를 깊이 있게 살펴봅니다.

1. 프록시가 필요한 이유

먼저 어노테이션이 없는 순수 Java 클래스의 동작을 생각해 봅시다.

public class AppConfig {

    public MemberRepository memberRepository() {
        return new MemberRepository();
    }

    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

    public OrderService orderService() {
        return new OrderService(memberRepository());
    }
}

이 클래스의 메서드들을 호출하면 다음과 같이 실행됩니다.

  • memberService() 호출 ➔ memberRepository() 실행 ➔ 새 객체 A 생성
  • orderService() 호출 ➔ memberRepository() 실행 ➔ 새 객체 B 생성

결과적으로 2개의 MemberRepository 인스턴스가 생성되며 Singleton 원칙이 깨지게 됩니다.

2. Spring의 해결책: CGLIB (Code Generation Library)

Spring은 @Configuration이 선언된 AppConfig 클래스를 있는 그대로 사용하지 않습니다. 대신 CGLIB라는 바이트코드 조작 라이브러리를 통해 AppConfig를 상속받은 자식 프록시 클래스를 런타임에 동적으로 생성합니다. 실제 Spring IoC 컨테이너에는 개발자가 작성한 원본 AppConfig가 아닌, CGLIB가 생성한 자식 프록시 객체가 등록됩니다.

3. CGLIB 프록시의 내부 동작 원리

CGLIB 프록시 클래스는 원본 클래스의 @Bean 메서드들을 오버라이딩(Override)하여 메서드 호출을 가로챕니다.

  1. 컨테이너 조회: 프록시 메서드가 호출되면 먼저 Spring 컨테이너에 해당 Bean이 이미 존재하는지 확인합니다.
  2. 기존 Bean 반환: 이미 컨테이너에 등록된 Bean이 있다면 기존 인스턴스를 즉시 반환합니다. (새 객체 생성 안 함)
  3. 최초 생성: 컨테이너에 Bean이 없다면 원본 클래스의 생성 로직을 실행하여 객체를 생성한 뒤 컨테이너에 저장하고 반환합니다.

이 가로채기 메커니즘을 통해 @Bean 메서드를 몇 번을 직접 호출하더라도 싱글톤이 철저히 보장됩니다.

4. JDK Dynamic Proxy vs CGLIB

Spring에서 사용하는 대표적인 프록시 생성 기술 2가지를 비교해 봅시다.

구분 JDK Dynamic Proxy CGLIB
적용 방식 인터페이스(Interface) 기반 클래스 상속(Inheritance) 기반
적용 대상 인터페이스를 구현한 클래스 인터페이스가 없는 일반 클래스
설정 클래스 적용 여부 불가능 (설정 클래스는 인터페이스가 없음) 사용 (표준)

AppConfig와 같은 설정 클래스는 대부분 구체 클래스(Concrete Class)이며 인터페이스를 구현하지 않습니다. 따라서 인터페이스가 필수인 JDK Dynamic Proxy를 사용할 수 없으므로, Spring은 설정 클래스에 CGLIB를 적용합니다.

5. `@Configuration(proxyBeanMethods = false)`의 이해

만약 설정 클래스 내부의 @Bean 메서드들끼리 서로를 직접 호출하는 로직이 전혀 없다면 어떻게 할까요?

@Configuration(proxyBeanMethods = false)
public class AppConfig {

    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
  • proxyBeanMethods = false: CGLIB 프록시 생성 단계를 생략하고 메서드를 직접 호출하는 Lite Mode로 작동합니다.
  • 장점: CGLIB 바이트코드 생성 비용을 아껴 애플리케이션 시작 및 메모리 성능을 미세하게 최적화할 수 있습니다.
  • 주의점: 메서드 간 직접 호출 시 싱글톤이 깨지므로, @Bean 메서드 간 간섭이 없는 경우에만 사용해야 합니다. (Spring Boot의 자동 설정 클래스에서 널리 활용됨)

정리

  • CGLIB 프록시: Spring은 @Configuration 클래스를 상속받은 자식 프록시 객체를 만들어 컨테이너에 등록합니다.
  • 싱글톤 유지: 프록시는 @Bean 메서드 호출을 가로채어 이미 등록된 Bean이 있으면 반환하고, 없으면 생성하여 저장하는 방식으로 싱글톤을 보장합니다.
  • CGLIB 채택 이유: Java 설정 클래스는 인터페이스가 없는 일반 클래스이므로 상속 기반의 CGLIB를 사용합니다.
  • proxyBeanMethods = false: 메서드 간 direct 호출이 없는 독립적 Bean 설정 시 프록시 생성을 끄는 성능 최적화 옵션입니다.

다음 장에서는 지금까지 학습한 IoC 컨테이너를 다시 정리하는 의미에서 BeanFactory와 ApplicationContext의 역할과 차이점을 심화하여 살펴봅니다.

핵심 질문

Q1. Spring은 @Configuration 클래스에 왜 CGLIB를 사용하나요?

@Bean 메서드 호출을 가로채 이미 컨테이너에 생성된 Bean이 존재하면 기존 인스턴스를 반환함으로써 동일한 Bean이 중복 생성되지 않도록 싱글톤(Singleton)을 보장하기 위해서입니다.

 

Q2. CGLIB는 어떤 방식으로 프록시를 생성하나요?

CGLIB는 대상 클래스를 상속(extends)받아 런타임에 새로운 자식 프록시 클래스를 생성하며, 오버라이딩된 메서드 내부에서 컨테이너 조회 및 싱글톤 보장 로직을 수행합니다.

 

Q3. proxyBeanMethods = false는 어떤 경우에 사용할 수 있나요?

 @Bean 메서드가 서로를 직접 호출하여 의존관계를 주입하지 않는 독립적인 설정 클래스에서, CGLIB 프록시 생성 과정을 생략하여 애플리케이션 구동 성능을 최적화하고자 할 때 사용할 수 있습니다.

 

Q4. 설정 클래스 프록시 생성 시 JDK Dynamic Proxy 대신 CGLIB를 사용하는 이유는 무엇인가요?

Java 설정 클래스는 인터페이스 없이 구현된 일반 클래스인 경우가 대부분입니다. JDK Dynamic Proxy는 인터페이스 기반으로만 프록시를 생성할 수 있지만, CGLIB는 클래스 상속 기반이므로 인터페이스가 없는 구체 클래스에도 프록시를 생성할 수 있기 때문입니다.

 

Q5. CGLIB 프록시 동작 시 설정 클래스의 제약 사항에는 무엇이 있나요?

 CGLIB는 클래스 상속 및 메서드 오버라이딩을 이용하므로, 설정 클래스 및 @Bean 메서드에 final 키워드가 붙어 있으면 상속이나 오버라이딩이 불가능하여 프록시가 정상적으로 생성되지 않습니다.

 

Q6. @Configuration이 없는 클래스에서 @Bean 메서드를 선언하면(Lite Mode) 어떻게 동작하나요?

CGLIB 프록시가 생성되지 않으므로 일반 Java 메서드처럼 동작합니다. 따라서 해당 @Bean 메서드를 다른 메서드에서 직접 호출할 경우 호출될 때마다 새로운 인스턴스가 생성되어 싱글톤이 파괴됩니다.

5.5 BeanFactory와 ApplicationContext 다시 보기

Part 2에서는 Spring IoC 컨테이너의 핵심 인터페이스인 BeanFactory와 ApplicationContext를 간단히 살펴보았습니다. 이후 Bean의 생명주기와 Scope, Java Config, @Configuration, @Bean의 동작 원리를 학습하면서 Spring 컨테이너가 실제로 어떤 역할을 수행하는지 이해하게 되었습니다. 이번 장에서는 지금까지 배운 내용을 바탕으로 BeanFactory와 ApplicationContext를 다시 정리하고, 두 인터페이스의 관계와 차이점을 심화하여 살펴봅니다.

1. Spring IoC 컨테이너

Spring IoC 컨테이너는 애플리케이션에서 사용하는 객체를 생성하고 관리하는 핵심 구성 요소입니다.

컨테이너는 다음과 같은 역할을 수행합니다.

  • Bean 생성
  • 의존성 주입(DI)
  • 생명주기 관리
  • Bean 조회
  • Singleton 관리
  • 이벤트 처리
  • 환경 정보 관리

이러한 기능을 제공하는 대표적인 인터페이스가 BeanFactory와 ApplicationContext입니다.

2. BeanFactory란?

BeanFactory는 Spring IoC 컨테이너의 가장 기본이 되는 최상위 인터페이스입니다.
가장 중요한 기능은 Bean을 생성하고 조회하는 것입니다.

BeanFactory beanFactory = ...;

MemberService service = beanFactory.getBean(MemberService.class);

필요한 Bean을 요청하면 컨테이너에서 객체를 찾아 반환합니다.

BeanFactory가 제공하는 기능

  • Bean 생성
  • Bean 조회
  • 의존성 관리
  • Singleton 관리
  • Bean 생명주기 관리

즉, Spring IoC의 핵심 기능은 모두 BeanFactory에서 시작됩니다.

3. ApplicationContext란?

ApplicationContext는 BeanFactory를 상속하는 인터페이스입니다. 즉, ApplicationContext는 BeanFactory의 모든 기능을 포함하면서 부가적인 엔터프라이즈 기능들을 추가로 제공합니다. 실제 Spring Boot 애플리케이션에서는 대부분 ApplicationContext를 사용합니다.

4. ApplicationContext가 제공하는 부가 기능

ApplicationContext는 Bean 관리뿐만 아니라 애플리케이션 전체를 관리하는 프레임워크 수준의 기능을 추가로 제공합니다.

 1) 국제화 지원 (MessageSource)

Spring은 다국어/국제화(i18n) 기능을 제공합니다. messages.properties, messages_ko.properties, messages_en.properties 등의 파일에서 현재 사용자의 Locale에 부합하는 메시지를 반환합니다.

String message = applicationContext.getMessage("welcome", null, locale);

2) 환경 정보 관리 (Environment)

application.yml이나 application.properties 등 애플리케이션 실행 환경의 설정값(Profiles 및 Properties)을 쉽게 조회할 수 있습니다.

Environment environment = applicationContext.getEnvironment();

3) 리소스 조회 (ResourceLoader)

classpath:, file:, http: 등 위치에 상관없이 동일한 Unified API로 리소스 파일을 읽어올 수 있습니다.

Resource resource = applicationContext.getResource("classpath:data.txt");

 4) 이벤트 발행 및 구독 (ApplicationEventPublisher)

이벤트 기반 프로그래밍을 지원합니다.
예를 들어 회원가입 완료 후 이메일 발송, 포인트 지급 등의 비동기/동기 이벤트를 손쉽게 발행할 수 있습니다.

applicationContext.publishEvent(new MemberRegisteredEvent(member));

5. BeanFactory vs ApplicationContext 비교

두 인터페이스의 기능을 비교하면 다음과 같습니다.

기능 / 역할 BeanFactory ApplicationContext
Bean 생성 & 조회 O O
의존성 주입 (DI) O O
Singleton 관리 O O
국제화 (MessageSource) X O
환경 변수/프로파일 (Environment) X O
리소스 로딩 (ResourceLoader) X O
이벤트 발행 (ApplicationEventPublisher) X O
Bean 로딩 시점 지연 로딩 (Lazy Loading) 조기 로딩 (Eager Loading)

 

Eager Loading이란?

ApplicationContext는 애플리케이션 구동 시점에 모든 Singleton Bean을 미리 생성하고 주입받으므로,
구동 시점에 설정 오류를 즉시 감지할 수 있다는 큰 장점이 있습니다.

핵심 정리

이번 Part 5. Spring Configuration에서는 Spring의 Java 기반 설정 방식을 학습하였습니다.

  • @Configuration: 설정 클래스를 정의하며 CGLIB 프록시를 생성하여 @Bean 메서드의 싱글톤을 보장합니다.
  • @Bean: 수동으로 객체 생성 과정을 제어하여 Spring Bean으로 등록합니다.
  • ApplicationContext: BeanFactory의 Bean 관리 기능뿐만 아니라 국제화, 환경 변수, 리소스 로딩, 이벤트 처리 등 애플리케이션 전반의 핵심 기능을 담당합니다.

핵심 질문

Q1. BeanFactory와 ApplicationContext의 가장 큰 차이점은 무엇인가요?

BeanFactory는 Bean의 생성, 조회, DI 등 핵심 기능만 담당하는 기본 IoC 컨테이너인 반면, ApplicationContext는 BeanFactory를 상속 및 확장하여 국제화(MessageSource), 환경 정보(Environment), 리소스 로딩(ResourceLoader), 이벤트 발행(ApplicationEventPublisher) 등의 엔터프라이즈 부가 기능을 추가로 제공하는 컨테이너입니다.

 

Q2. Spring Boot에서는 일반적으로 어떤 컨테이너를 사용하나요?

 ApplicationContext를 주로 사용합니다. 애플리케이션 시작 시 SpringApplication.run()이 실행되면서 ApplicationContext가 생성되고 필요한 모든 Bean을 초기화 및 사전 등록(Eager Loading)하게 됩니다.

 

Q3. ApplicationContext가 제공하는 대표적인 추가 기능은 무엇인가요?

Locale 기반 국제화 메시지를 처리하는 MessageSource, 실행 환경의 설정값과 프로파일을 다루는 Environment, classpath나 file 자원을 일관되게 다루는 ResourceLoader, 그리고 이벤트 기반 프로그래밍을 지원하는 ApplicationEventPublisher가 있습니다.

 

Q4. 실무에서 ApplicationContext를 코드 내에서 직접 사용할 때는 언제인가요?

대부분의 비즈니스 로직에서는 의존성 주입(DI)을 통해 Bean을 제공받으므로 직접 다룰 일이 없지만, 실행 시점에 동적으로 Bean을 구해야 할 때, Spring 이벤트를 발행할 때, 환경 정보 및 리소스 파일에 직접 접근할 때 사용됩니다.

 

Q5. Bean 로딩 시점 관점에서 BeanFactory와 ApplicationContext의 차이는 무엇인가요?

BeanFactory는 Bean이 실제로 요청될 때 객체를 생성하는 지연 로딩(Lazy Loading) 방식을 기본으로 사용하는 반면, ApplicationContext는 애플리케이션 구동 시점에 모든 Singleton Bean을 미리 생성하는 조기 로딩(Eager Loading) 방식을 사용하여 구동 시점에 오류를 미리 검증할 수 있습니다.

 

Q6. ApplicationContext의 상속 구조 및 계층 구조는 어떻게 구성되어 있나요?

ApplicationContext는 BeanFactory를 상속할 뿐만 아니라 MessageSource, EnvironmentCapable, ResourcePatternResolver, ApplicationEventPublisher 등의 여러 인터페이스를 다중 상속받아 하나의 통합된 애플리케이션 컨테이너 인터페이스로 제공됩니다.

 

'🍃SpringBoot' 카테고리의 다른 글

Spring: Part 4. Bean 생명주기(Lifecycle)  (1) 2026.07.29
Spring: Part 3. 의존성 주입(DI)과 활용  (0) 2026.07.29
Spring: Part 2. IoC 컨테이너와 Bean  (0) 2026.07.29
Spring: Part 1. Spring Framework의 이해  (0) 2026.07.28
Spring IoC & DI 완전 정복: Chapter 8. Proxy와 Spring AOP  (1) 2026.07.22
'🍃SpringBoot' 카테고리의 다른 글
  • Spring: Part 4. Bean 생명주기(Lifecycle)
  • Spring: Part 3. 의존성 주입(DI)과 활용
  • Spring: Part 2. IoC 컨테이너와 Bean
  • Spring: Part 1. Spring Framework의 이해
limdaeil
limdaeil
limdaeil 님의 블로그 입니다.
  • limdaeil
    limdaeil
    limdaeil
  • 전체
    오늘
    어제
    • 분류 전체보기 (83) N
      • 💭Retrospective (21)
      • 🥕FrontEnd (0)
      • 🐬MySQL (2) N
      • 🐍Python (5)
      • 🍃SpringBoot (33) N
      • ☕Java (1)
      • ♾️Devops (1)
      • 🌎Network (2)
      • 📚Read & 👨‍🏫Course (10)
      • Programmers (1)
      • 🧪Test (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Mockito
    jwt
    한빛아카데미
    회고
    spring boot
    Concurrency
    redis
    gradle
    한빛미디어
    MySQL
    junit
    DI
    IoC
    서평단
    나는리뷰어다
    Python
    optimistic rock
    책
    distributed lock
    Spring
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
limdaeil
Spring: Part 5. Spring Configuration
상단으로

티스토리툴바