자바 백엔드 입문 19편 — @Configuration @Bean Java Config

2026-05-16자바 백엔드 입문

자바 백엔드 입문 19편. @Configuration·@Bean으로 Bean을 명시적으로 등록하는 Java Config 방식. 외부 라이브러리 클래스를 Bean으로 만드는 가장 흔한 패턴을 요리 레시피 비유로 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 19편 — @Configuration @Bean Java Config

이 글은 자바 백엔드 입문 시리즈 59편 중 19편이에요. 18편에서 @Component·@Autowired 어노테이션 기반 설정을 다뤘다면, 이번 19편은 또 다른 Bean 등록 방식 — @Configuration·@Bean Java Config를 풀어 갑니다. "내가 직접 작성한 클래스가 아니라 외부 라이브러리의 객체를 Bean으로 만들고 싶을 때" 가 가장 흔한 사용처예요.

Java Config가 어렵게 들리는 이유

12편의 @Component 방식이 익숙해진 후 13편을 만나면 "왜 또 다른 방식?" 가 헷갈려요.

첫째, @Component 만으로는 안 되는 시나리오가 있어요. 외부 라이브러리(Jackson·RestTemplate·DataSource)의 클래스에는 @Component 를 박을 수 없거든요. 그 클래스 코드 자체를 우리가 못 고치니까.

둘째, @Configuration@Bean 의 역할 분담이 처음엔 안 잡혀요.

이 글에서는 요리 레시피 비유로 풀어 가요. @Configuration = "이 클래스는 요리 레시피 모음집", @Bean = "이 메서드는 한 가지 요리(객체) 만드는 레시피". 끝까지 따라오시면 두 어노테이션이 한 그림에 들어와요.

@Configuration — 레시피 모음집

@Configuration"이 클래스는 Bean을 만드는 레시피들의 모음집이다" 선언이에요. 평범한 자바 클래스에 박으면 — Spring이 시작 시점에 "이 안의 @Bean 메서드들을 다 호출해서 그 반환값을 Bean으로 등록" 하는 처리를 합니다.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        return mapper;
    }
}

이 코드를 풀면 — AppConfig 클래스 안에 두 개의 @Bean 메서드가 박혀 있어요. Spring이 시작될 때 두 메서드를 호출해서 반환값(RestTemplate 객체, ObjectMapper 객체)을 컨테이너에 Bean으로 등록해요. Bean 이름은 기본으로 메서드 이름과 동일 — restTemplate, objectMapper.

@Bean — 한 레시피 한 메서드

@Bean"이 메서드의 반환값을 Bean으로 등록해 줘" 메모예요. 메서드 안에서 우리가 직접 new 로 객체를 만들고 설정 처리한 뒤 반환하면, 그 반환값이 Spring 컨테이너에 들어갑니다.

@Component 와의 결정적 차이 — @Component 는 클래스 자체에 박는 메모, @Bean 은 메서드에 박는 메모. 그래서 외부 라이브러리 클래스(우리 코드가 아닌 것)도 Bean으로 만들 수 있어요. "코드 안에서 new RestTemplate() 하고 그 결과를 반환" 하면 되니까요.

@Component vs @Bean — 언제 어느 걸?

이 둘이 가장 헷갈리는 부분이에요. 룰은 단순.

시나리오 권장 방식
내가 작성한 클래스가 Spring Bean이 돼야 할 때 @Component/@Service/@Repository
외부 라이브러리 클래스(Jackson·DataSource 등)를 Bean으로 등록할 때 @Configuration + @Bean
Bean 생성 로직이 복잡한 경우 (다른 객체 참고·조건 분기) @Configuration + @Bean
같은 클래스로 여러 Bean 만들고 싶을 때 (커넥션 풀 2개 등) @Configuration + @Bean 여러 개

90% 케이스에서 우리가 작성한 클래스 = @Component, 외부 라이브러리 = @Bean. 거의 다른 선택지가 없어요.

의존성 주입을 @Bean 메서드에서 받기

@Bean 메서드 안에서 다른 Bean이 필요하면 — 메서드 매개변수로 받으면 됩니다.

@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        HikariConfig cfg = new HikariConfig();
        cfg.setJdbcUrl("jdbc:postgresql://localhost:5432/myshop");
        return new HikariDataSource(cfg);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {   // ← DataSource 자동 주입
        return new JdbcTemplate(dataSource);
    }
}

jdbcTemplate 메서드가 DataSource 매개변수를 선언하면, Spring이 컨테이너에서 DataSource Bean을 찾아 자동으로 넘겨줘요. 이 안에 @Autowired 같은 어노테이션 박을 필요 X — Java Config 메서드 매개변수는 자동으로 의존성 주입 대상.

💡 @Configuration 안에서 다른 @Bean 호출 시

@Configuration 클래스 안에서 jdbcTemplate(dataSource()) 같이 다른 @Bean 메서드를 직접 호출해도 — Spring이 마법처럼 같은 Bean을 반환합니다. 평범한 메서드 호출처럼 새 객체를 만드는 게 아니에요. CGLib 프록시가 가로채서 컨테이너에서 꺼내준 결과. 신기하지만 입문에선 매개변수로 받는 패턴이 더 깔끔해요.

@Bean 메서드의 다양한 옵션

@Bean 메서드에 여러 옵션을 박을 수 있어요. 자주 쓰이는 4가지.

@Bean(name = "primaryDataSource")    // Bean 이름 직접 지정
@Bean(initMethod = "init")           // 초기화 호출할 메서드
@Bean(destroyMethod = "close")       // 소멸 시 호출할 메서드
@Bean
@Primary                              // 기본 Bean으로 지정
@Scope("prototype")                   // 매번 새 객체 (14편에서)
public DataSource dataSource() { ... }

destroyMethod = "close""컨테이너 종료 시 이 메서드 자동 호출" — 커넥션 풀 같은 리소스가 정상 종료되게 보장해줘요. AutoCloseable 구현 클래스는 자동으로 처리되기도 합니다.

@Configuration vs 일반 클래스 + @Bean

@Configuration 안 박힌 평범한 @Component 클래스에 @Bean 메서드 박아도 — Bean 등록은 됩니다. 다만 CGLib 프록시 처리가 안 돼서, 클래스 내부에서 다른 @Bean 메서드 직접 호출 시 "매번 새 객체" 가 만들어져요.

// 비권장 패턴
@Component
public class BadConfig {
    @Bean public Foo foo() { return new Foo(); }
    @Bean public Bar bar() { return new Bar(foo()); }   // foo() 호출 시 새 Foo
}

// 권장 패턴
@Configuration
public class GoodConfig {
    @Bean public Foo foo() { return new Foo(); }
    @Bean public Bar bar(Foo foo) { return new Bar(foo); }   // 매개변수로 받음
}

@Configuration + 매개변수로 받기. 이게 표준이에요.

@Import — Configuration 합치기

여러 @Configuration 클래스로 나눈 후, 메인 Config에서 다 가져올 때 @Import 를 써요.

@Configuration
@Import({DataSourceConfig.class, WebConfig.class, SecurityConfig.class})
public class AppConfig {
    @Bean public RestTemplate restTemplate() { return new RestTemplate(); }
}

이러면 AppConfig 하나만 등록해도 DataSourceConfig·WebConfig·SecurityConfig 안의 모든 @Bean 이 자동 합쳐져요. 대형 시스템에서 "역할별로 Config 파일 분리 + 메인에서 통합" 패턴이 표준입니다.

@ComponentScan과 Java Config 함께 쓰기

@Component@Bean 은 같이 써요. 한 프로젝트에서 자주 보는 패턴:

@Configuration
@ComponentScan(basePackages = "com.example.myshop")
public class AppConfig {

    // 외부 라이브러리 객체 = @Bean
    @Bean public RestTemplate restTemplate() { return new RestTemplate(); }
    @Bean public DataSource dataSource() { ... }

    // 우리 클래스(OrderService 등)는 @Component 박혀 있고 자동 스캔됨
}

@SpringBootApplication 안에 @ComponentScan + @Configuration 이 이미 들어 있어요. 그래서 Spring Boot 프로젝트는 메인 클래스 한 개로 두 방식이 자동 결합됩니다. 우리는 "내가 작성한 건 @Service, 외부 라이브러리는 메인 클래스 안에 @Bean 메서드 추가" 만 신경 쓰면 끝.

한 줄 정리 — @Configuration = Bean 레시피 모음집, @Bean = 한 객체 만드는 메서드. 외부 라이브러리 객체를 Bean으로 등록할 때 표준. @Component 와 둘 다 같이 자주 씀.

시험 직전 한 번 더 — Java Config 입문자가 매번 헷갈리는 것

  • @Configuration = Bean 레시피 모음집 선언
  • @Bean = 메서드의 반환값을 Bean으로 등록하는 메모
  • @Component = 클래스 자체에 박는 메모, @Bean = 메서드에 박는 메모
  • 내 클래스 = @Component/@Service/@Repository/@Controller
  • 외부 라이브러리 클래스 = @Configuration + @Bean
  • Bean 이름 기본 = 메서드 이름 그대로
  • @Bean(name = "...") 으로 이름 직접 지정
  • 의존성 주입 = @Bean 메서드 매개변수로 받음 (자동 주입)
  • @Configuration + 매개변수 패턴이 권장 표준
  • @Configuration 안의 @Bean 메서드를 직접 호출해도 — CGLib 프록시 덕분에 컨테이너 Bean 반환
  • 일반 @Component 클래스 안 @Bean 메서드 직접 호출 시 — 매번 새 객체 만들어짐 (비권장)
  • @Bean(initMethod = "init", destroyMethod = "close") — 생명주기 콜백 지정
  • @Bean + @Primary — 같은 타입 여러 개일 때 기본값 지정
  • @Bean + @Scope("prototype") — 매번 새 객체 (14편에서 깊이)
  • @Import({Config1.class, Config2.class}) = 여러 Config 합치기
  • 대형 시스템 표준 = 역할별 Config 분리 + 메인 @Import
  • @SpringBootApplication 안에 @Configuration + @ComponentScan 이미 들어 있음
  • 따라서 메인 클래스 자체에 @Bean 메서드 박아도 OK
  • 같은 컨테이너에 @Component@Bean Bean이 섞여 있는 게 일반 패턴
  • 가장 흔한 @Bean 등록 예 = RestTemplate·ObjectMapper·DataSource·PasswordEncoder

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!