애너테이션 기반 빈 등록 설정
스프링 컨테이너에서 애너테이션을 활용해 객체를 자동 관리하려면 패키지 스캔 기능을 먼저 활성화해야 합니다. XML 설정 파일에 context:component-scan 요소를 선언하면, 지정한 베이스 패키지 내에서 스테레오타입 애너테이션이 부착된 클래스를 탐색하여 빈(Bean)으로 자동 등록합니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 자동 스캔 대상 패키지 지정 -->
<context:component-scan base-package="com.example.repository, com.example.service" />
</beans>
핵심 스테레오타입 애너테이션
스프링은 애플리케이션의 계층형 아키텍처에 맞춰 클래스의 역할을 명시하는 전용 애너테이션을 제공합니다. 내부적으로는 모두 @Component의 메타 애너테이션으로 동작하지만, 코드 가독성과 유지보수, 그리고 계층별 특화 기능(예: 예외 변환)을 위해 적절한 애너테이션을 선택해야 합니다.
@Repository: 데이터 접근 계층(DAO)에 적용합니다. persistence 예외를 스프링의 일관된DataAccessException계층으로 자동 변환합니다.@Service: 비즈니스 로직을 담당하는 서비스 계층에 사용합니다. 트랜잭션 경계 설정과 함께 주로 활용됩니다.@Controller: 웹 MVC 구조에서 클라이언트 요청을 처리하는 프레젠테이션 계층에 적용합니다.@Component: 위 계층에 명확히 속하지 않는 유틸리티, 헬퍼, 일반 컴포넌트에 사용합니다.@Scope: 빈의 생명주기 범위(singleton, prototype, request 등)를 클래스 레벨에서 정의할 때 활용합니다.
계층별 구현 예제
데이터 접근 계층 (Repository)
인터페이스와 구현체를 분리하여 데이터 저장 로직을 정의합니다. 구현 클래스에 @Repository를 부착하여 스캔 대상에 포함시킵니다.
package com.example.repository;
public interface DataStore {
void persist(String payload);
}
package com.example.repository.impl;
import com.example.repository.DataStore;
import org.springframework.stereotype.Repository;
@Repository("jdbcDataStore")
public class JdbcDataStoreImpl implements DataStore {
@Override
public void persist(String payload) {
System.out.println("[DB] 데이터 저장 완료: " + payload);
}
}
비즈니스 로직 계층 (Service)
서비스 클래스에서 리포지토리 빈을 주입받습니다. @Autowired를 통해 의존성을 자동으로 연결하며, 클래스에는 @Service를 명시합니다.
package com.example.service;
public interface BusinessProcessor {
void executeTask(String input);
}
package com.example.service.impl;
import com.example.repository.DataStore;
import com.example.service.BusinessProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DefaultProcessorImpl implements BusinessProcessor {
private final DataStore dataStore;
@Autowired
public DefaultProcessorImpl(DataStore dataStore) {
this.dataStore = dataStore;
}
@Override
public void executeTask(String input) {
System.out.println("[Service] 비즈니스 로직 수행 중...");
dataStore.persist(input);
}
}
클라이언트 호출부 (Servlet)
웹 환경에서 스프링 컨텍스트를 로드하고 등록된 서비스 빈을 조회하여 메서드를 실행하는 과정입니다.
package com.example.web;
import com.example.service.BusinessProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TaskServlet extends HttpServlet {
private BusinessProcessor processor;
@Override
public void init() throws ServletException {
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
this.processor = ctx.getBean(BusinessProcessor.class);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
processor.executeTask("샘플_데이터_001");
resp.getWriter().write("작업이 완료되었습니다.");
}
}
실행 흐름 및 결과
서블릿이 초기화되면 ClassPathXmlApplicationContext가 XML 설정을 파싱합니다. 이때 component-scan 설정에 따라 com.example.repository와 com.example.service 패키지 내의 애너테이션이 스캔됩니다. 스프링 컨테이너는 JdbcDataStoreImpl과 DefaultProcessorImpl을 싱글톤 빈으로 생성하고, 생성자 주입을 통해 의존성을 자동으로 연결합니다. HTTP GET 요청이 들어오면 컨테이너에서 관리하는 서비스 빈의 executeTask 메서드가 호출되며, 콘솔에는 비즈니스 로직 시작 메시지와 함께 데이터 저장 완료 로그가 순차적으로 출력됩니다.