스프링 기반 애플리케이션 개발 시 제어 역전 (IoC) 컨테이너가 객체의 생성과 생명주기를 관리하도록 하는 것은 표준적인 패턴입니다. 일반적으로 컴포넌트 스캔을 통해 자동으로 빈을 등록하지만, 외부 라이브러리나 제어가 불가능한 클래스를 사용해야 할 때는 명시적인 설정이 필요합니다. 이 경우 Java 코드를 활용하여 XML 없이도 빈 정의를 수행하는 JavaConfig 방식을 사용합니다.
명시적 구성 클래스의 필요성
애플리케이션 내부 클래스에는 @Component 와 같은 애너테이션을 추가하여 자동 스캔 대상으로 쉽게 지정할 수 있습니다. 하지만 타사에서 제공하는 오픈소스 라이브러리 클래스에 직접 코드 수정이 불가능한 상황에서는 이러한 자동화가 통하지 않습니다. 이때 Java 설정 클래스를 작성하여 해당 객체를 수동으로 스프링 컨텍스트에 등록해야 합니다. JavaConfig 는 타입 안정성이 보장되고 IDE 지원이 우수하여 기존 XML 설정보다 권장됩니다.
설정용 클래스는 비즈니스 로직과는 격리된 패키지에 위치시켜야 합니다. 이는 순수 설정 담당 클래스임을 명확히 구분하기 위함입니다. @Configuration 애너테이션은 해당 클래스가 빈 정의의 원천임을 지시하며, 내부 메서드의 @Bean 은 반환 객체를 빈으로 등록하라는 의미를 갖습니다.
기본 빈 정의 구현
먼저 의존성 없이 독립적으로 작동하는 간단한 빈 예제를 살펴보겠습니다. 음원 데이터를 표현하는 도메인 클래스와 이를 구성하는 설정 클래스로 분리합니다. 아래 예시는 'MediaAsset' 라는 단순 객체를 등록하는 방식입니다.
// 도메인 모델 : 실제 데이터 보유
public class MediaAsset {
private String composer = "Unknown";
private String trackName = "Untitled";
public void perform() {
System.out.println("Executing track: " + trackName + " composed by " + composer);
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AudioConfiguration {
// 빈 Id 는 기본으로 메서드 이름인 asset 가 되며, 커스텀 이름을 지정하려면 name 속성 활용 가능
@Bean(name = "primaryMedia")
public MediaAsset createMediaAsset() {
return new MediaAsset();
}
}
의존성 주입 처리 전략
하나의 빈이 다른 빈에 의존하는 경우, 예를 들어 플레이어가 음원을 사용하는 구조라면 설정 클래스 내에서 어떻게 연결할지 정의해야 합니다. @Autowired 대신 설정 메서드 레벨에서 파라미터를 전달하거나, 다른 빈 생성 메서드를 호출하는 두 가지 방식이 가능합니다.
두 번째 방식인 파라미터 주입이 권장되는데, 이는 의존성 간의 결합도를 낮추고 유연하게 빈을 조립할 수 있기 때문입니다. 특정 빈이 반드시 같은 설정 클래스 내부에 있어야 한다는 제약이 사라집니다.
// 소비자 클래스 : 의존성을 받는 객체
public class PlaybackEngine {
private final MediaAsset source;
// 생성자 주입을 통해 의존성 확보
public PlaybackEngine(MediaAsset source) {
this.source = source;
}
public void run() {
source.perform();
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AudioConfiguration {
@Bean
public MediaAsset mediaData() {
return new MediaAsset();
}
// 옵션 A: 다른 빈 생성 메서드를 직접 호출
/*
@Bean
public PlaybackEngine playerA() {
return new PlaybackEngine(mediaData());
}
*/
// 옵션 B (권장): 빈 생성 메서드에 의존 대상 타입을 파라미터로 선언
@Bean
public PlaybackEngine playerInstance(MediaAsset data) {
return new PlaybackEngine(data);
}
}
구현 검증 테스트
설정의 정확성을 확인하기 위해 JUnit 기반 통합 테스트를 작성할 수 있습니다. 스프링 테스팅 모듈을 사용하여 애플리케이션 컨텍스트를 불러오고 빈들이 올바르게 주입되었는지 검증합니다.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AudioConfiguration.class)
public class AudioSystemVerification {
@Autowired
private MediaAsset loadedMedia;
@Autowired
private PlaybackEngine activePlayer;
@Test
public void validatePlaybackOperation() {
loadedMedia.perform();
activePlayer.run();
}
}