본문 바로가기
강의 실습/스프링 핵심 원리 - 기본편

@Configuration과 바이트코드 조작의 마법

by jint 2026. 4. 18.

스프링이 자바 코드까지 어떻게 하기는 어려우니, 클래스의 바이트코드를 조작하는 라이브러리를 사용한다.

- src/test/java/hello/core/singleton/ConfigurationSingletonTest.java

...
@Test
void configurationDeep() {
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    // AppConfig도 스프링 빈으로 등록된다.
    AppConfig bean = ac.getBean(AppConfig.class);

    System.out.println("bean = " + bean.getClass());
    // 출력: bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
}
...


AnnotationConfigApplicationContext 에 파라미터로 넘긴 값은 스프링 빈으로 등록되니 AppConfig 도 스프링 빈이 된다.

순수한 클래스라면 다음과 같이 출력되어야 한다.

class hello.core.AppConfig


- 콘솔

bean = class hello.core.AppConfig$$SpringCGLIB$$0


콘솔을 보면 클래스 명에 xxxCGLIB가 붙어 상당히 복잡해진 것을 볼 수 있다.
이것은 직접 만든 클래스가 아닌 스프링이 CGLIB라는 바이트 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다!

 

임의의 다른 클래스


임의의 다른 클래스가 싱글톤이 보장되도록 해준다.
아마도 다음과 같이 바이트 코드를 조작해서 작성되어 있을 가능성이 높다 (실제로는 CGLIB 내부 기술을 사용하는데 매우 복잡함)

AppConfig@CGLIB 예상 코드)

@Bean
public MemberRepository memberRepository() {
    if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
        return 스프링 컨테이너에서 찾아서 반환;
    } else { // 스프링 컨테이너에 없으면
        기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
        return 반환
    }
}


@Bean이 붙은 메서드마다 스프링 빈 존재 여부를 체크하여, 존재하면 존재하는 빈을 반환하고, 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다. 덕분에 싱글톤이 보장되는 것이다.
-> @Configuration 을 붙이면 바이트코드를 조작하는 CGLIB 기술을 사용해 싱글톤을 보장

* 참고
AppConfig@CGLIB는 AppConfig 의 자식 타입이므로, AppConfig 타입으로 조회 가능


1. @Configuration 을 적용하지 않고, @Bean 만 적용하면 어떻게 될까?
@Configuration 주석처리 후, ConfigurationSingletonTest 에서 다시 configurationDeep() 호출한다.

- src/main/java/hello/core/AppConfig.java

// @Configuration 삭제
public class AppConfig {
    ...
}


- 콘솔

bean = class hello.core.AppConfig


AppConfig가 CGLIB 기술 없이 순수한 AppConfig 로 스프링 빈에 등록된다.

- 콘솔

call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
call AppConfig.memberRepository


이 출력 결과를 통해서 MemberRepository 가 총 3번 호출된 것을 알 수 있다.
1번은 @Bean 에 의해 스프링 컨테이너에 등록되어 발생한 출력
2번, 3번은 각각 memberRepository() 를 호출하면서 발생한 출력

인스턴스가 같은지 테스트 결과)

ConfigurationSingletonTest 에서 configurationTest() 호출한다.

- 콘솔

memberService -> memberRepository = hello.core.member.MemoryMemberRepository@68217d41
orderService -> memberRepository = hello.core.member.MemoryMemberRepository@1ac45389
memberRepository = hello.core.member.MemoryMemberRepository@3e5d4f6b


인스턴스가 같은지 테스트하는 코드도 실패하고, 각각 전부 다른 MemoryMemberRepository 인스턴스를 가진다.
문제점 : 스프링 컨테이너에서 관리가 되지 않고, 스프링 빈도 아니다.

-> @Bean 만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않음
@Configuration 이 없어서 memberRepository() 처럼 의존관계 주입이 필요해서 메서드를 직접 호출할 때 싱글톤을 보장하지 않음
-> 스프링 설정 정보는 항상 @Configuration 사용하자!


참고링크 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8?cid=325969

 

스프링 핵심 원리 - 기본편| 김영한 - 인프런 강의

현재 평점 5.0점 수강생 49,554명인 강의를 만나보세요. 스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다. 스프링 기본 기능, 스프

www.inflearn.com

댓글