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

@Configuration과 싱글톤

by jint 2026. 4. 17.

AppConfig 코드의 이상한 점이 있는데,
memberService() 호출시 memberRepository() 호출되어 new MemoryMemberRepository() 호출
orderService() 호출시 memberRepository() 호출되어 new MemoryMemberRepository() 호출

각각 다른 2개의 MemoryMemberRepository 생성되어 싱글톤 깨지는 것처럼 보인다.
스프링 컨테이너는 이 문제를 어떻게 해결할까?


1. 검증 용도의 코드 추가
각 서비스 구현체에 MemberRepository 를 조회할 수 있는 메서드를 임시로 추가한다.

- src/main/java/hello/core/member/MemberServiceImpl.java

...
    // 테스트 용도
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }
...


- src/main/java/hello/core/order/OrderServiceImpl.java

...
    // 테스트 용도
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }
...



2. 테스트 코드
- src/test/java/hello/core/singleton/ConfigurationSingletonTest.java

package hello.core.singleton;

import hello.core.AppConfig;
import hello.core.member.MemberRepository;
import hello.core.member.MemberServiceImpl;
import hello.core.order.OrderServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import static org.assertj.core.api.Assertions.*;

class ConfigurationSingletonTest {

    @Test
    void configurationTest() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
        OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
        MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);

        // 모두 같은 인스턴스를 참고하고 있다.
        System.out.println("memberService -> memberRepository = " + memberService.getMemberRepository());
        System.out.println("orderService -> memberRepository = " + orderService.getMemberRepository());
        System.out.println("memberRepository = " + memberRepository);

        // 모두 같은 인스턴스를 참고하고 있다.
        assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
    }

}


- 콘솔

memberService -> memberRepository = hello.core.member.MemoryMemberRepository@27e5b378
orderService -> memberRepository = hello.core.member.MemoryMemberRepository@27e5b378
memberRepository = hello.core.member.MemoryMemberRepository@27e5b378


MemberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용된다.
하지만, AppConfig 의 자바 코드는 분명 각각 2번의 new MemoryMemberRepository() 호출해서 다른 인스턴스가 생성되어야 하는데 어떻게 된 일일까? 혹시 2번 호출이 안되는 것일까?


3. AppConfig에 호출 로그 남김
- src/main/java/hello/core/AppConfig.java

...
    @Bean
    public MemberService memberService() {
        // 1번
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService() {
        // 1번
        System.out.println("call AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        // 2번? 3번?
        System.out.println("call AppConfig.memberRepository");
        return new MemoryMemberRepository();
    }
...


스프링 컨테이너가 각각 @Bean을 호출해서 스프링 빈을 생성하니 memberRepository() 는 총 3번 호출될 것이 기대된다.
1) 스프링 빈 등록하기 위해 @Bean 붙어있는 memberRepository() 호출
2) memberService() 에서 호출
3) orderService() 에서 호출

하지만, 출력 결과는 모두 1번만 호출된다.

- 콘솔

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


@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,552명인 강의를 만나보세요. 스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다. 스프링 기본 기능, 스프

www.inflearn.com

댓글