[Spring][개념]⚡ 스프링 DI와 빈 생명주기 비밀 전격 공개! 이것만 알면 당신도 스프링 고수!

김상욱·2024년 10월 16일
post-thumbnail

🎯 Dependency Injection (DI) 완벽 이해하기


🧩 1. 의존관계의 외부 주입

  • 객체 간의 의존 관계를 직접 관리하지 않고 외부 조립기(Spring Container)가 이를 수행합니다.
  • 이로 인해 객체는 외부에서 주입된 객체를 사용하게 되며, 의존성을 스스로 해결할 필요가 없습니다.

🔄 2. 제어의 역행(Inversion of Control, IoC)

  • IoC(Inversion of Control)는 객체 제어의 주도권을 외부 컨테이너에 넘기는 것을 의미합니다.
  • 원래는 객체가 직접 필요한 다른 객체를 생성했지만, 이제는 컨테이너가 대신 생성합니다.
  • 이로 인해 개발자는 객체의 생성과 생명주기 관리에서 벗어나 비즈니스 로직에 집중할 수 있습니다.

🚀 3. DI의 역할과 동작 원리

  • DI는 각 객체의 의존성을 외부에서 주입해주는 방식입니다.
    예를 들어, A 객체B 객체에 의존할 경우:
    1. A가 직접 B를 생성하지 않습니다.
    2. Spring Container가 B를 생성한 후, A에게 주입합니다.

📌 결론: 객체들이 서로 독립적으로 존재하면서도 외부의 조율에 따라 협력합니다.


🔓 4. 느슨한 결합(Loose Coupling)의 핵심

  • DI를 통해 객체 간 결합도가 낮아지며, 확장성과 유지보수성이 크게 향상됩니다.
  • 객체는 인터페이스에 의한 의존 관계만 알면 되고, 실제 구현체가 무엇인지는 몰라도 됩니다.
    즉, 다양한 구현체로 쉽게 대체할 수 있습니다.
// 예시: 인터페이스 정의
public interface PaymentService {
    void pay(int amount);
}

// 구현체 생성
public class CreditCardPaymentService implements PaymentService {
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card.");
    }
}

// DI를 이용한 주입
@Service
public class OrderService {
    private final PaymentService paymentService;

    @Autowired
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void processOrder(int amount) {
        paymentService.pay(amount);
    }
}
  • OrderServicePaymentService 인터페이스에 의존하며, 어떤 구현체(CreditCardPaymentService)를 사용할지는 외부 컨테이너가 결정합니다.
  • 이를 통해 코드는 구현체에 의존하지 않으며 언제든지 다른 구현체로 교체할 수 있습니다.

🛠 5. Dependency Injection의 주요 장점

  1. 유연성

    • 필요에 따라 구현체를 손쉽게 교체할 수 있습니다.
  2. 테스트 용이성

    • 실제 객체 대신 Mock 객체를 주입하여 단위 테스트를 쉽게 수행할 수 있습니다.
  3. 유지보수성 향상

    • 객체 간의 결합도가 낮아지기 때문에 수정이 발생해도 다른 부분에 영향을 최소화할 수 있습니다.

🌿 Spring의 DI(Dependency Injection) 지원 완벽 정리


🌱 1. Spring Container의 역할

  • Spring ContainerDI 조립기 역할을 하며 객체 간의 의존성을 관리합니다.
  • 이 컨테이너는 필요한 객체를 생성하고, 의존성 주입을 통해 객체 간 관계를 설정합니다.
  • 개발자는 직접 객체의 의존성을 관리할 필요 없이, 컨테이너가 모든 것을 대신 수행합니다.

🗂️ 2. 스프링 설정 파일을 통한 의존관계 설정

스프링에서는 객체 간의 의존 관계를 XML 설정 또는 Java Configuration을 통해 지정할 수 있습니다.

① XML 설정 파일 사용 예시

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- PaymentService 구현체 등록 -->
    <bean id="paymentService" class="com.example.CreditCardPaymentService" />

    <!-- OrderService에 PaymentService 주입 -->
    <bean id="orderService" class="com.example.OrderService">
        <constructor-arg ref="paymentService" />
    </bean>
</beans>
  • 이 코드에서는 OrderService 객체가 사용할 PaymentServiceXML 설정에서 주입합니다.
  • 스프링 컨테이너는 설정된 내용을 바탕으로 의존 객체를 생성하고 주입합니다.

② Java Configuration 사용 예시

@Configuration
public class AppConfig {

    @Bean
    public PaymentService paymentService() {
        return new CreditCardPaymentService();
    }

    @Bean
    public OrderService orderService() {
        return new OrderService(paymentService());
    }
}
  • Java Configuration에서는 @Configuration@Bean 애노테이션을 활용해 객체를 선언합니다.
  • 이 설정은 스프링 컨테이너에 의해 관리되며, 필요한 객체가 자동으로 생성 및 주입됩니다.

⚙️ 3. Spring Container가 제공하는 API 활용

  • 스프링 컨테이너는 ApplicationContext를 통해 객체를 생성하고 관리합니다.
  • 개발자는 이 API를 사용하여 쉽게 객체를 가져와 사용할 수 있습니다.

ApplicationContext 사용 예시

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

// OrderService 객체를 가져와 사용
OrderService orderService = context.getBean("orderService", OrderService.class);
orderService.processOrder(1000);
  • 이 코드는 ApplicationContext를 사용해 XML 설정을 로드하고 필요한 객체를 가져오는 예시입니다.
  • getBean() 메서드로 스프링 컨테이너에서 관리하는 OrderService 객체를 쉽게 호출할 수 있습니다.

🎯 4. Spring DI의 장점

  1. 생명주기 관리:
    스프링 컨테이너가 객체의 생성과 소멸을 자동으로 관리합니다.

  2. 유연한 설정:
    XML과 Java 설정을 모두 지원해 상황에 맞게 구성 변경이 용이합니다.

  3. 확장성:
    인터페이스 기반의 의존성 주입으로, 구현체 변경 시 코드 수정 없이 대체가 가능합니다.


🏗️ Spring 빈 생성 범위(Scope) 완벽 정리

💡 두 줄 미리보기

스프링 빈은 싱글톤과 프로토타입의 두 가지 스코프로 관리됩니다. 한 번 생성 후 공유할지, 요청할 때마다 새로 생성할지 지금 바로 알아보세요!


🧱 1. Singleton Bean (싱글톤 빈)

  • Singleton 스코프는 스프링의 기본 빈 관리 방식입니다.
  • 한 번 생성된 인스턴스가 애플리케이션 전역에서 재사용됩니다.
  • 모든 호출에서 동일한 객체가 반환됩니다.

예시: Singleton Bean 사용

@Component
public class HelloService {
    public String sayHello() {
        return "Hello, Singleton!";
    }
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

HelloService service1 = context.getBean(HelloService.class);
HelloService service2 = context.getBean(HelloService.class);

System.out.println(service1); // 같은 인스턴스 반환
System.out.println(service2); // 동일한 해시코드 출력

실행 결과:

com.example.HelloService@54afc369
com.example.HelloService@54afc369
  • 두 번의 getBean() 호출에서 같은 객체 인스턴스가 반환되었습니다.
  • Singleton 스코프는 애플리케이션 전체에서 동일한 인스턴스를 공유할 때 사용됩니다.

🔄 2. Prototype Bean (프로토타입 빈)

  • Prototype 스코프에서는 요청할 때마다 새로운 인스턴스가 생성됩니다.
  • 상태가 서로 다른 객체를 매번 생성해야 할 때 유용합니다.
  • 각 호출마다 다른 객체가 반환됩니다.

예시: Prototype Bean 사용

@Component
@Scope("prototype")
public class HelloService {
    public String sayHello() {
        return "Hello, Prototype!";
    }
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

HelloService service1 = context.getBean(HelloService.class);
HelloService service2 = context.getBean(HelloService.class);

System.out.println(service1); // 서로 다른 인스턴스 반환
System.out.println(service2); // 다른 해시코드 출력

실행 결과:

com.example.HelloService@1d6a883
com.example.HelloService@7b6fabc
  • 두 번의 getBean() 호출에서 다른 객체 인스턴스가 반환된 것을 볼 수 있습니다.
  • Prototype 스코프는 매번 새로 만들어지는 객체가 필요할 때 유용합니다.

🗂️ 3. XML로 빈 Scope 설정하기

  • 스프링에서는 XML 파일을 사용해 빈의 스코프를 설정할 수도 있습니다.
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Singleton 빈 -->
    <bean id="singletonService" class="com.example.HelloService" scope="singleton" />

    <!-- Prototype 빈 -->
    <bean id="prototypeService" class="com.example.HelloService" scope="prototype" />
</beans>
  • XML 파일을 통해서도 간편하게 스코프 설정이 가능합니다.
  • 상황에 맞게 Singleton 또는 Prototype 스코프를 유연하게 사용할 수 있습니다.

⚖️ 4. Singleton vs Prototype: 언제 사용해야 할까?

  1. Singleton

    • 공유가 필요한 객체 (예: 서비스, DAO 클래스)
    • 전역 인스턴스가 필요할 때 적합
  2. Prototype

    • 상태가 각기 다른 객체 (예: 폼 데이터, DTO)
    • 매번 새로운 객체가 필요할 때 사용

🌿 Spring 빈 생성 범위(Scope) 완벽 정리


🧱 1. Singleton Scope (싱글톤 스코프)

  • 기본 설정: 스프링 컨테이너당 단 하나의 인스턴스만 생성됩니다.
  • 여러 번 호출해도 동일한 객체가 반환되므로 재사용과 메모리 절약에 유리합니다.

Singleton 사용 예시

@Component
public class SingletonService {
    public void doSomething() {
        System.out.println("Singleton instance");
    }
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

SingletonService service1 = context.getBean(SingletonService.class);
SingletonService service2 = context.getBean(SingletonService.class);

System.out.println(service1); // 동일한 인스턴스
System.out.println(service2); // 동일한 인스턴스

실행 결과

com.example.SingletonService@54afc369
com.example.SingletonService@54afc369
  • 특징:
    • 애플리케이션 전역에서 같은 객체를 공유합니다.
    • 상태를 가지지 않는 서비스에서 주로 사용합니다.

🔄 2. Prototype Scope (프로토타입 스코프)

  • 새로운 인스턴스 생성: 요청할 때마다 새로운 객체가 반환됩니다.
  • 각 호출마다 다른 상태를 가진 객체가 필요할 때 사용합니다.

Prototype 사용 예시

@Component
@Scope("prototype")
public class PrototypeService {
    public void doSomething() {
        System.out.println("Prototype instance");
    }
}
PrototypeService service1 = context.getBean(PrototypeService.class);
PrototypeService service2 = context.getBean(PrototypeService.class);

System.out.println(service1); // 서로 다른 인스턴스
System.out.println(service2); // 서로 다른 해시코드

실행 결과

com.example.PrototypeService@1d6a883
com.example.PrototypeService@7b6fabc
  • 특징:
    • 임시 객체상태가 다른 객체가 필요할 때 유용합니다.
    • 인스턴스가 많아질 수 있으므로 메모리 관리에 신경 써야 합니다.

📩 3. Request Scope (HTTP 요청 스코프)

  • HTTP 요청마다 새로운 인스턴스를 생성합니다.
  • 각 요청 간 독립적인 객체가 필요할 때 사용합니다.

Request 사용 예시

@Component
@Scope("request")
public class RequestService {
    public void handleRequest() {
        System.out.println("New request instance");
    }
}
  • 특징:
    • HTTP 요청마다 고유한 인스턴스를 제공합니다.
    • 요청이 끝나면 객체가 파기되어 메모리 누수를 방지합니다.

🧳 4. Session Scope (HTTP 세션 스코프)

  • HTTP 세션마다 새로운 인스턴스를 생성합니다.
  • 세션이 유지되는 동안 같은 객체를 공유합니다.

Session 사용 예시

@Component
@Scope("session")
public class SessionService {
    public void manageSession() {
        System.out.println("New session instance");
    }
}
  • 특징:
    • 세션 간 독립적인 상태를 유지해야 할 때 유용합니다.
    • 세션 종료 시 객체가 파기됩니다.

🗂️ 5. 스프링 빈 생성 범위(Scope) 요약표

범위설명
singleton스프링 컨테이너당 하나의 인스턴스 빈 생성 (기본 설정)
prototype요청할 때마다 새로운 인스턴스 생성
requestHTTP Request마다 새로운 인스턴스 생성
sessionHTTP Session마다 새로운 인스턴스 생성

⚖️ Scope 선택 가이드: 언제 무엇을 사용할까?

  1. Singleton

    • 애플리케이션 전역에서 공유할 객체 (예: 서비스 클래스, DAO)
    • 상태를 가지지 않는 객체에 적합합니다.
  2. Prototype

    • 매번 다른 상태가 필요한 객체 (예: 폼 데이터, DTO)
    • 인스턴스 생성이 많아질 수 있으므로 메모리 관리 필요
  3. Request

    • 각 HTTP 요청마다 독립된 객체가 필요할 때 사용합니다.
    • 주로 웹 애플리케이션 컨트롤러에서 사용됩니다.
  4. Session

    • 사용자 세션독립적인 상태를 유지할 때 적합합니다.
    • 주로 로그인 상태를 관리하는 데 사용됩니다.

스프링 빈 설정: 메타 정보의 관리와 표현 방식

스프링 프레임워크에서 빈(Bean)은 애플리케이션의 핵심 구성 요소입니다. 스프링은 다양한 방식을 통해 빈을 정의하고, 이 정보는 IoC(Inversion of Control) Container가 관리합니다. 이 과정에서 Bean Definition Meta Data가 중요한 역할을 합니다. 아래에서 빈 설정의 메타 정보가 생성되고 전달되는 흐름을 설명합니다.


🛠️ 1. 빈 설정을 위한 표현 방식

스프링에서는 빈의 메타 정보를 세 가지 방식으로 표현할 수 있습니다:

  1. XML Document

    • XML 파일을 사용하여 스프링 빈을 정의하는 전통적인 방식입니다.
    • 주로 applicationContext.xml과 같은 설정 파일에서 빈의 생성, 의존성, 스코프를 정의합니다.

    XML 설정 예시:

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                               http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="myService" class="com.example.MyService" scope="singleton"/>
    </beans>
    • 장점: 구조적이고 명확하며, 설정이 외부에 존재하므로 코드와 분리됩니다.
    • 단점: 설정이 길어질수록 복잡해질 수 있습니다.

  1. Annotation (애노테이션)

    • Java 클래스에서 애노테이션을 사용해 빈을 정의하는 방식입니다.
    • 주로 @Component, @Service, @Controller, @Repository와 같은 스테레오타입 애노테이션을 사용합니다.

    애노테이션 설정 예시:

    @Component
    public class MyService {
        public void doService() {
            System.out.println("Service is running.");
        }
    }
    • 장점: 설정이 간결하며, 빈의 정의와 코드가 같은 파일에 존재하므로 가독성이 높습니다.
    • 단점: 설정과 비즈니스 로직이 혼재될 수 있어 코드가 복잡해질 수 있습니다.

  1. Java Code (자바 코드 설정)

    • Java 클래스에 @Configuration@Bean 애노테이션을 사용해 프로그램적으로 빈을 등록하는 방식입니다.
    • 이 방식은 자바 코드 내에서 동적으로 빈을 정의할 수 있습니다.

    Java 설정 예시:

    @Configuration
    public class AppConfig {
    
        @Bean
        public MyService myService() {
            return new MyService();
        }
    }
    • 장점: 코드로 빈을 정의하므로 동적 설정이 용이합니다.
    • 단점: 설정이 코드에 섞여 있어 관리가 복잡해질 수 있습니다.

📦 2. Bean Definition Meta Data (빈 정의 메타 데이터)

  • 스프링은 XML, 애노테이션, 자바 설정으로 정의된 정보를 Bean Definition Meta Data로 변환합니다.

  • 이 메타 데이터는 각 빈의 타입, 이름, 의존 관계, 스코프 등을 포함하며, IoC 컨테이너에 전달됩니다.

    Bean Definition Meta Data에 포함된 정보 예시:

    • 빈 이름: myService

    • 클래스: com.example.MyService

    • 스코프: singleton

    • 의존성 정보: 해당 클래스가 필요로 하는 다른 빈들

      이 정보는 스프링 컨테이너가 빈 생성과 의존성 주입을 관리하는 데 사용됩니다.


🌀 3. IoC Container (IoC 컨테이너)

  • 스프링의 IoC 컨테이너Bean Definition Meta Data를 바탕으로 객체를 생성하고 관리합니다.
  • 이 컨테이너는 의존성 주입(Dependency Injection)을 수행하며, 빈의 생명 주기를 제어합니다.

IoC 컨테이너의 역할:

  1. 메타 데이터를 읽고 빈 객체를 생성합니다.
  2. 의존성이 필요한 빈들에 의존성 주입을 수행합니다.
  3. 애플리케이션 종료 시 객체를 파기합니다.

🔄 전체 흐름 요약

  1. XML, 애노테이션, 자바 코드 중 하나를 사용해 빈의 정보를 정의합니다.
  2. 스프링은 이를 Bean Definition Meta Data로 변환합니다.
  3. IoC 컨테이너가 메타 데이터를 바탕으로 빈을 생성하고 관리합니다.

📝 Spring 빈 설정: XML로 메타 정보 정의하기

스프링 프레임워크에서 XML 파일은 애플리케이션의 빈(Bean)과 그 의존성을 구성하는 대표적인 방법입니다. XML 설정을 사용하면 코드와 설정을 분리해 구조화된 관리가 가능하며, 각 빈의 세부적인 속성을 명확히 기술할 수 있습니다. 아래에서 XML 빈 설정의 핵심 개념과 일반적인 예제를 살펴보겠습니다.


🛠️ 1. XML 문서로 빈 설정하기

XML을 통해 스프링의 각 빈을 정의하고 의존성 주입을 수행할 수 있습니다. 이 방법은 특히 코드와 설정을 분리해 관리하려는 경우에 유용합니다.

  • 장점:
    1. 외부 설정 관리가 용이합니다.
    2. XML의 구조적 표현으로 의존 관계를 명확하게 정의할 수 있습니다.
    3. <bean> 태그를 사용해 빈의 세밀한 제어가 가능합니다.

💡 2. 일반적인 XML 빈 설정 예제

예시: 의존성 주입이 포함된 XML 설정

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- DataSource 빈 설정 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>

    <!-- DAO 빈 설정 -->
    <bean id="userDao" class="com.example.dao.UserDaoImpl">
        <constructor-arg ref="dataSource"/>
    </bean>

    <!-- Service 빈 설정 -->
    <bean id="userService" class="com.example.service.UserServiceImpl">
        <constructor-arg ref="userDao"/>
    </bean>

    <!-- Controller 빈 설정 -->
    <bean id="userController" class="com.example.controller.UserController">
        <constructor-arg ref="userService"/>
    </bean>

</beans>

📌 3. 예제 코드 설명

  1. DataSource 빈 설정

    • SimpleDriverDataSource를 사용해 데이터베이스 연결 정보를 설정합니다.
    • property 태그를 사용해 데이터베이스 드라이버, URL, 사용자 이름, 비밀번호와 같은 속성을 설정합니다.
  2. DAO 빈 설정

    • UserDaoImpl 클래스는 데이터 접근을 담당합니다.
    • 생성자 주입을 사용해 dataSource 객체를 주입합니다.
  3. Service 빈 설정

    • UserServiceImpl은 비즈니스 로직을 담당합니다.
    • userDao를 생성자 주입 방식으로 받아서 사용합니다.
  4. Controller 빈 설정

    • UserController는 사용자 요청을 처리합니다.
    • userService를 생성자 주입 방식으로 전달받아 비즈니스 로직을 호출합니다.

🔄 4. XML 설정의 장단점

장점:

  • 설정이 코드와 분리되어 있으므로 유지보수가 쉽습니다.
  • 복잡한 의존 관계를 시각적으로 명확하게 정의할 수 있습니다.
  • 애플리케이션 배포 시 동적으로 설정 변경이 가능합니다.

단점:

  • 설정이 복잡해질수록 XML 파일의 길이가 길어집니다.
  • 코드와 설정이 분리되어 있어 가독성이 떨어질 수 있습니다.
  • Java 기반 설정이나 애노테이션에 비해 유연성이 떨어질 수 있습니다.

⚙️ 5. 스프링 애플리케이션에서의 동작 과정

  1. XML 파일 로딩:

    • 스프링 IoC 컨테이너는 applicationContext.xml과 같은 파일을 로드합니다.
  2. 빈 생성 및 의존성 주입:

    • 설정된 XML을 기반으로 빈을 생성하고 필요한 의존성을 주입합니다.
  3. 빈 관리:

    • 컨테이너가 빈의 생명주기를 관리하며, 필요한 시점에 객체를 제공합니다.

IoC 컨테이너 초기화 예시:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

UserController userController = context.getBean("userController", UserController.class);
userController.processRequest();

6. 정리: XML 설정 활용하기

스프링에서 XML 설정은 코드와 설정을 분리해 관리할 수 있는 좋은 방법입니다. 복잡한 의존성 관계를 명확하게 정의할 수 있으며, 대규모 애플리케이션에서 유용합니다.

XML 설정의 장점과 단점을 이해하고 상황에 맞게 활용하면 스프링 애플리케이션의 구조와 유지보수성을 크게 향상시킬 수 있습니다.


🏷️ Spring 빈 설정: Annotation으로 효율적인 빈 관리하기


📌 1. Annotation 기반 빈 설정의 개요

  • 애플리케이션이 커지면서 XML 파일로 모든 빈을 관리하는 것은 복잡해질 수 있습니다.
  • 애노테이션(Annotation)을 사용하면 클래스에 자동 등록을 위한 정보를 부여할 수 있습니다.
  • 빈 스캐닝을 통해 스프링이 자동으로 필요한 빈을 생성 및 등록합니다.

🛠️ 2. @Autowired를 통한 의존성 주입

  • @Autowired 애노테이션은 의존성 주입(Dependency Injection)을 수행합니다.
  • 생성자, 필드, 또는 메서드에 부여하여 해당 빈을 자동으로 스프링 컨테이너에서 주입합니다.

예시: @Autowired 사용

@Repository
public class UserDaoImpl implements UserDao {

    private final DataSource dataSource;
    private final DBUtil dbUtil;

    @Autowired
    public UserDaoImpl(DataSource dataSource, DBUtil dbUtil) {
        this.dataSource = dataSource;
        this.dbUtil = dbUtil;
    }
}
  • 위 예시에서는 생성자 주입 방식으로 DataSourceDBUtil 빈이 주입됩니다.
  • 필드 주입 또는 메서드 주입도 가능하지만, 생성자 주입이 권장됩니다.

🔄 3. Component Scan 설정하기

  • 애노테이션 기반 빈 등록을 사용하려면 Component Scan을 설정해야 합니다.
  • Component Scan은 특정 패키지 내의 모든 클래스에 대해 스캔하며, 등록된 애노테이션이 부여된 클래스를 빈으로 생성합니다.

XML에서 Component Scan 설정

<beans xmlns="http://www.springframework.org/schema/beans"
       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">

    <context:component-scan base-package="com.example"/>
</beans>
  • 위 설정에서는 com.example 패키지 아래의 모든 클래스가 스캔 대상이 됩니다.
  • @Repository, @Service, @Controller스테레오타입 애노테이션이 부여된 클래스를 자동으로 빈으로 등록합니다.

🏷️ 4. 주요 스테레오타입 애노테이션 종류

스테레오타입 애노테이션은 클래스의 역할에 따라 빈을 구분해 등록합니다.

Stereotype적용 대상
@Repository데이터 접근 계층(DAO) 클래스에 사용. AOP와 결합해 예외를 처리합니다.
@Service비즈니스 로직 계층(Service Layer) 클래스에 사용합니다.
@ControllerMVC 패턴의 컨트롤러로 사용, 웹 요청을 처리합니다.
@Component일반적으로 계층 구분이 어려운 클래스에 사용합니다.

예시: 다양한 애노테이션 사용

@Service
public class UserServiceImpl implements UserService {
    // 비즈니스 로직 구현
}

@Controller
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public String handleRequest() {
        return "User Request Handled";
    }
}

🔍 5. Annotation 설정의 장단점

장점:

  • 설정이 간결하며 코드와 빈의 정의가 하나로 관리됩니다.
  • 자동 스캔으로 XML 설정의 복잡도를 줄일 수 있습니다.
  • 코드 수정이 적고, 유지보수가 쉬워집니다.

단점:

  • 대규모 프로젝트에서는 모든 빈을 자동으로 스캔하는 것이 비효율적일 수 있습니다.
  • 코드와 설정이 동일한 파일에 존재해 구조가 복잡해질 수 있습니다.

⚙️ 6. Annotation과 XML 설정의 조합 사용

  • XML과 애노테이션을 조합해 사용할 수 있습니다.
  • 예를 들어, 데이터베이스와 같은 외부 자원을 XML로 설정하고, 비즈니스 로직은 애노테이션으로 설정할 수 있습니다.

7. 정리: 애노테이션으로 빈 관리 최적화

스프링의 Annotation 기반 빈 설정은 코드와 설정을 통합해 관리의 효율성을 높입니다. @Autowired와 같은 애노테이션을 통해 의존성 주입이 간단해지며, 스테레오타입 애노테이션을 사용해 계층별로 구조화된 애플리케이션을 만들 수 있습니다.


🏷️ Spring 빈 설정: Java Configuration File


🛠️ 1. Java Configuration의 특징

  • XML 설정을 대체해 자바 코드로 스프링 빈을 정의합니다.
  • 빈과 의존성을 동적으로 정의하고 코드로 제어할 수 있습니다.
  • @Configuration 클래스는 스프링 컨테이너가 빈으로 등록할 설정을 포함합니다.
  • 유연한 로직을 적용할 수 있어 XML보다 동적 구성이 용이합니다.

📄 2. Java Configuration 설정 예시

@Configuration
@ComponentScan(basePackages = {"com.example"})
public class ApplicationConfig {

    @Bean
    public DataSource getDataSource() {
        SimpleDriverDataSource sdds = new SimpleDriverDataSource();
        sdds.setDriverClass(com.mysql.cj.jdbc.Driver.class);
        sdds.setUrl("jdbc:mysql://127.0.0.1:3306/mydb?serverTimezone=UTC&useUnicode=yes&characterEncoding=UTF-8");
        sdds.setUsername("root");
        sdds.setPassword("password");
        return sdds;
    }
}

🔍 3. 코드 설명

  1. @Configuration:

    • 해당 클래스가 스프링 설정 파일임을 나타냅니다.
    • 이 클래스 내에서 정의된 메서드들은 빈 객체를 생성해 스프링 컨테이너에 등록됩니다.
  2. @ComponentScan:

    • 지정한 패키지 내의 빈을 자동으로 스캔합니다.
    • 예시에서는 com.example 패키지 아래에 있는 @Component, @Service, @Repository 등이 자동으로 등록됩니다.
  3. @Bean:

    • 메서드에 사용되어 빈 객체를 생성합니다.
    • 이 메서드에서 반환된 객체는 스프링 컨테이너에 의해 빈으로 등록됩니다.
  4. SimpleDriverDataSource 설정:

    • 데이터베이스와의 연결을 위한 DataSource 객체를 설정합니다.
    • JDBC 드라이버, DB URL, 사용자 정보를 설정해 DataSource를 빈으로 제공합니다.

🔄 4. Java Configuration을 사용하는 이유

  1. 유연한 구성:

    • 빈 설정에 로직을 추가할 수 있습니다.
    • 예를 들어, 조건문이나 반복문을 통해 빈의 동적 구성이 가능합니다.
  2. 유지보수 용이성:

    • 설정 파일이 코드와 동일한 프로젝트 내에 존재해 관리가 쉽습니다.
  3. XML의 대체:

    • XML 설정과 달리 타입 안전성이 보장됩니다.
    • 컴파일 타임에 오류를 확인할 수 있습니다.

⚖️ 5. XML 설정과의 비교

XML 설정Java Configuration 설정
외부 XML 파일에 빈을 정의합니다.자바 코드에서 빈을 정의합니다.
구성이 명확하지만 복잡해질 수 있습니다.코드 내에서 유연한 구성이 가능합니다.
타입 안전성이 부족합니다.컴파일 시 타입 오류 확인이 가능합니다.

6. 정리

Java Configuration은 자바 코드로 스프링 빈을 정의하여 더 유연하고 안전한 구성을 제공합니다. XML 설정의 복잡도를 줄이고, 조건에 따라 동적으로 빈을 구성할 수 있습니다. @Configuration@Bean을 적절히 활용하면 강력한 애플리케이션 구조를 구현할 수 있습니다.


📝 Spring 설정: XML로 빈 구성 완벽 정리

🌱 1. XML 설정 파일 개요

  • XML 파일은 스프링 애플리케이션에서 빈의 정의와 의존성을 관리하는 데 사용됩니다.
  • 스프링의 IoC 컨테이너는 XML 파일에 정의된 설정을 읽어 필요한 빈을 생성하고 관리합니다.

XML의 주요 구성 요소

  • Root 태그: <beans> 태그가 모든 빈 정의를 감쌉니다.
  • 파일명: 보통 applicationContext.xml로 설정합니다.

기본 XML 템플릿

<?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">
</beans>

🛠️ 2. 빈 객체 생성과 의존성 주입 (기본 설정)

  • 빈(Bean)은 스프링 컨테이너가 관리하는 객체입니다.
  • XML 파일에서 <bean> 태그를 사용해 빈을 정의하고, 필요한 의존성을 주입할 수 있습니다.

일반적인 빈 설정 예시

<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

<bean id="userDao" class="com.example.dao.UserDaoImpl">
    <constructor-arg ref="dataSource"/>
</bean>

<bean id="userService" class="com.example.service.UserServiceImpl">
    <constructor-arg ref="userDao"/>
</bean>

<bean id="userController" class="com.example.controller.UserController">
    <constructor-arg ref="userService"/>
</bean>

🔍 3. 주요 태그와 속성 설명

  1. <bean> 태그

    • 스프링 컨테이너가 관리할 빈을 정의합니다.
  2. 기본 속성:

    • id: 빈의 고유한 이름입니다.
    • class: 빈으로 등록할 클래스의 경로입니다.
  3. <constructor-arg> 태그:

    • 생성자 주입 방식으로 의존성을 주입합니다.
    • ref 속성을 통해 다른 빈을 참조합니다.

⚙️ 4. 스프링 애플리케이션의 동작 흐름

  1. XML 파일 로딩:

    • 스프링 컨테이너가 applicationContext.xml 파일을 로드합니다.
  2. 빈 생성 및 의존성 주입:

    • XML에 정의된 빈과 의존성을 생성자 주입 방식으로 설정합니다.
  3. 빈 사용:

    • 애플리케이션에서 컨테이너로부터 빈을 가져와 사용합니다.

Java 코드에서 빈 사용 예시

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

UserController userController = context.getBean("userController", UserController.class);
userController.handleRequest();
  • 설명:
    위 코드는 UserController 빈을 가져와 웹 요청을 처리합니다.

🔄 5. XML 설정의 장단점

장점:

  • 설정과 코드 분리유지보수성 향상.
  • 복잡한 의존 관계를 명확하게 정의 가능.
  • 여러 환경에서 설정 파일을 교체해 쉽게 구성 변경 가능.

단점:

  • XML 파일이 커질수록 복잡해질 수 있습니다.
  • 타입 안전성 부족: XML에서는 컴파일 시 타입 검증이 어렵습니다.
  • Java Configuration에 비해 유연성이 떨어질 수 있습니다.

🌟 6. 정리: XML 설정으로 구조화된 스프링 애플리케이션 구현

스프링의 XML 설정은 빈과 의존성을 명확하게 정의하고 코드와 설정을 분리할 수 있는 좋은 방법입니다. 특히 대규모 애플리케이션에서 복잡한 의존 관계를 쉽게 관리할 수 있습니다. 하지만, XML 설정의 복잡도를 줄이기 위해 Java Configuration 또는 Annotation 설정과 함께 사용하는 것이 좋습니다.


🚀 7. 결론 및 활용 팁

이제 XML 설정을 활용해 스프링 애플리케이션을 더 구조화된 형태로 만들어보세요!
빈 생성과 의존성 주입을 명확하게 정의해 유지보수와 확장성이 뛰어난 코드를 작성할 수 있습니다. 🌱


📝 Spring 설정: XML을 통한 빈 객체 생성과 의존성 주입

스프링에서는 XML 파일을 활용해 빈 객체를 생성하고 의존 관계를 설정할 수 있습니다. 아래에서는 빈 객체를 가져오는 방법과 생성자 주입(Dependency Injection)을 통한 의존성 설정을 설명합니다.


아래는 일반적인 예제로 수정한 내용입니다.


🌿 1. 빈 객체 가져오기

스프링의 IoC 컨테이너는 XML 설정 파일에 정의된 대로 빈 객체를 생성하고 의존성을 주입합니다. 이를 통해 애플리케이션에서 빈을 가져와 사용할 수 있습니다.

예시: XML 파일을 사용해 빈 가져오기

ApplicationContext context = 
    new ClassPathXmlApplicationContext("com/example/configuration/applicationContext.xml");

UserController userController = context.getBean("userController", UserController.class);
userController.processRequest();
  • 설명:
    1. ClassPathXmlApplicationContext를 통해 스프링 XML 설정 파일을 로드합니다.
    2. XML에 정의된 userController 빈을 가져옵니다.
    3. 가져온 빈을 사용해 애플리케이션의 요청을 처리합니다.

🔄 2. XML에서의 생성자 주입 (Constructor Injection)

생성자 주입은 객체가 생성될 때 필수 의존성을 주입하는 방식입니다. XML 파일에서 <constructor-arg> 태그를 통해 의존성을 정의할 수 있습니다.


일반적인 XML 설정 예시

<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

<bean id="userDao" class="com.example.dao.UserDaoImpl">
    <constructor-arg ref="dataSource"/>
</bean>

<bean id="userService" class="com.example.service.UserServiceImpl">
    <constructor-arg ref="userDao"/>
</bean>

<bean id="userController" class="com.example.controller.UserController">
    <constructor-arg ref="userService"/>
</bean>
  • 설명:
    1. userDaodataSource 객체를 생성자 주입받습니다.
    2. userServiceuserDao를 의존성으로 주입받습니다.
    3. userControlleruserService를 주입받아 사용자 요청을 처리합니다.

생성자 주입을 통한 의존성 설정 코드

public class UserServiceImpl implements UserService {

    private final UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public String processUserData(String userId) {
        return userDao.getUserById(userId);
    }
}
  • 설명:
    • UserServiceImpl 클래스는 생성자를 통해 UserDao 객체를 주입받습니다.
    • 생성자 주입은 객체가 생성될 때 필요한 모든 의존성을 보장합니다.
    • processUserData() 메서드는 주입받은 userDao 객체를 사용해 사용자 데이터를 처리합니다.

⚙️ 3. 전체 동작 과정

  1. 스프링 컨테이너가 XML 설정 파일을 로드하여 필요한 빈 객체를 생성합니다.
  2. 생성자 주입을 통해 객체 간의 의존성을 설정합니다.
  3. 애플리케이션에서 getBean() 메서드를 통해 빈을 가져와 사용합니다.

📝 Spring XML 설정: Property Injection (프로퍼티 주입) 정리

스프링에서는 XML 설정을 통해 객체의 의존성을 주입할 수 있으며, 이 중 하나가 Property Injection(프로퍼티 주입)입니다. 프로퍼티 주입은 Setter 메서드를 통해 객체에 값을 설정하는 방식으로, 주로 단순한 데이터나 빈 객체를 주입할 때 사용됩니다.


🌿 1. Property Injection의 개념

  • Property Injectionsetter 메서드를 통해 객체의 의존성을 주입하는 방식입니다.
  • 스프링은 XML 설정 파일에서 <property> 태그를 사용해 해당 객체에 값을 주입합니다.
  • 주의: 하나의 프로퍼티에는 단일 값만 설정할 수 있습니다.

🛠️ 2. 일반적인 Property Injection 예제

XML 설정: String 값과 객체 주입

<bean id="userService" class="com.example.service.UserServiceImpl">
    <property name="serviceName" value="User Management Service"/>
    <property name="userDao" ref="userDao"/>
</bean>

<bean id="userDao" class="com.example.dao.UserDaoImpl">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>
  • 설명:
    1. userService 객체는 serviceName에 문자열 값을 주입받고, userDao 객체를 참조합니다.
    2. userDao 객체는 dataSource 객체를 참조합니다.
    3. dataSource 객체는 데이터베이스 연결 정보를 주입받습니다.

🔄 3. XML 설정: 하위 태그와 속성 활용

  1. 하위 태그(ref, value) 사용:

    • 객체 주입 시:
      <property name="userDao" ref="userDao"/>
    • 문자열 값 주입 시:
      <value>Some String Value</value>
  2. 속성 방식 사용:

    • 객체 주입 시:
      <property name="userDao" ref="userDao"/>
    • 문자열 값 주입 시:
      <property name="serviceName" value="User Service"/>
  3. XML Namespace를 사용한 설정:

    • XML 스키마 설정에 p 네임스페이스를 등록하면, 더 간결하게 설정할 수 있습니다.
    <bean id="adminService" class="com.example.AdminServiceImpl"
          p:name="Admin Service" p:adminDao-ref="adminDao"/>

🧑‍💻 4. Java 코드에서의 Property Injection

public class UserServiceImpl implements UserService {

    private String serviceName;
    private UserDao userDao;

    // Setter for serviceName
    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    // Setter for userDao
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void process() {
        System.out.println("Service: " + serviceName);
        userDao.getUser();
    }
}
  • 설명:
    • UserServiceImpl 클래스는 setter 메서드를 통해 serviceNameuserDao를 주입받습니다.
    • 프로퍼티 주입 방식은 객체 생성 후 값이 변경될 가능성이 있을 때 유용합니다.

🔍 5. Property Injection의 장단점

장점:

  • 객체 생성 후에도 값을 변경할 수 있어 유연성이 있습니다.
  • 단순한 의존성이나 기본 데이터 타입을 쉽게 주입할 수 있습니다.

단점:

  • 필수 의존성 보장이 어렵습니다. (생성자 주입이 권장되는 이유)
  • 객체가 생성된 이후에 의존성이 주입되므로, 주입되지 않으면 런타임 오류가 발생할 수 있습니다.

⚙️ 6. 전체 동작 과정

  1. 스프링 컨테이너가 XML 설정 파일을 로드하여 빈 객체를 생성합니다.
  2. 프로퍼티 주입 방식으로 필요한 값을 설정합니다.
  3. 애플리케이션에서 빈 객체를 가져와 사용합니다.

필수 의존성 주입(Required Dependency Injection)이란?

필수 의존성 주입이란, 객체가 정상적으로 동작하기 위해 반드시 필요한 의존 객체를 주입하는 것을 의미합니다. 해당 의존성이 주입되지 않으면 객체가 제대로 작동하지 않기 때문에 주입이 필수적입니다. 필수 의존성은 주로 생성자 주입(Constructor Injection)을 통해 강제되며, 주입이 누락되면 컴파일 시점 혹은 애플리케이션 초기화 시점에 오류가 발생합니다.


🌱 필수 의존성 주입이 필요한 이유

  • 안정성 보장: 객체가 동작하기 위해 반드시 필요한 의존 객체가 누락되지 않도록 보장합니다.
  • 불변성 유지: 생성자 주입으로 주입된 객체는 생성 후 변경이 불가능하므로 안전합니다.
  • 주입 누락 방지: 의존성이 주입되지 않은 상태에서 객체가 사용되는 것을 방지합니다.

🔄 필수 의존성 주입: 생성자 주입(Constructor Injection)

생성자 주입은 객체가 생성될 때 필수적으로 필요한 의존성을 전달하는 방법입니다. 스프링에서 생성자 주입은 주입할 객체가 없으면 애플리케이션이 실행되지 않도록 설계됩니다.

생성자 주입의 예제

XML 설정:

<bean id="userDao" class="com.example.dao.UserDaoImpl"/>

<bean id="userService" class="com.example.service.UserServiceImpl">
    <constructor-arg ref="userDao"/>
</bean>

Java 클래스:

public class UserServiceImpl implements UserService {

    private final UserDao userDao;

    // 생성자 주입: 필수 의존성 보장
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public void performService() {
        System.out.println("Using UserDao: " + userDao);
    }
}
  • 설명:
    1. UserServiceImpl 클래스는 생성자를 통해 UserDao 의존성을 필수로 주입받습니다.
    2. 만약 userDao가 주입되지 않으면 스프링 컨테이너가 객체를 생성할 수 없으므로 오류가 발생합니다.
    3. 생성된 후에는 의존성이 변경될 수 없으므로 안전합니다.

🔍 필수 의존성 주입 vs. 선택적 의존성 주입

  1. 필수 의존성 주입 (생성자 주입 사용):

    • 필수 객체가 주입되지 않으면 애플리케이션이 실행되지 않습니다.
    • 의존성이 주입된 후 변경 불가합니다.
  2. 선택적 의존성 주입 (Setter 주입 사용):

    • 객체 생성 후 필요에 따라 선택적으로 주입할 수 있습니다.
    • 주입이 누락되면 런타임 오류가 발생할 수 있으므로 주의가 필요합니다.

Setter 주입 예시:

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void performService() {
        if (userDao == null) {
            System.out.println("UserDao not initialized!");
        } else {
            System.out.println("Using UserDao: " + userDao);
        }
    }
}
  • 설명:
    • Setter 주입에서는 의존성을 선택적으로 주입할 수 있지만, 주입이 누락될 가능성이 있어 안전하지 않습니다.

⚙️ 생성자 주입을 통한 필수 의존성의 장점

  1. 안정성 보장: 객체가 생성될 때 의존성을 강제하므로 누락될 가능성이 없습니다.
  2. 불변 객체 설계: 주입된 의존성이 변경되지 않으므로 안전합니다.
  3. 테스트 용이성: 모든 의존성을 생성자로 주입받기 때문에 테스트 시 모의 객체(Mock Object)를 쉽게 주입할 수 있습니다.

📝 스프링 빈 의존 관계 설정: Annotation을 통한 의존성 주입 정리

스프링 프레임워크에서는 Annotation을 사용하여 의존성 주입(Dependency Injection, DI)을 간결하고 명확하게 수행할 수 있습니다. 애노테이션은 Setter 메서드 작성 없이도 멤버 변수나 생성자에 직접 주입할 수 있으며, 주로 @Autowired, @Resource, @Inject 같은 애노테이션이 사용됩니다. 각 애노테이션의 특성과 사용 방법을 아래에서 자세히 설명합니다.


🌟 1. 주요 의존성 주입 애노테이션 소개

Annotation설명
@Autowired스프링 전용 애노테이션. 스프링 컨텍스트에서 자동으로 의존성을 주입합니다.
@ResourceJSR-250 표준 애노테이션. 자원을 이름으로 주입하며, 주로 JNDI 리소스 주입에 사용됩니다.
@InjectJSR-330 표준 애노테이션. 특정 프레임워크에 종속되지 않는 의존성 주입을 수행합니다.

🛠️ 2. @Autowired: 스프링 전용 의존성 주입

@Autowired스프링 프레임워크에서 제공하는 의존성 주입 애노테이션으로, 타입에 맞는 빈을 자동으로 찾아 주입합니다.

특징:

  • Required 속성: 기본적으로 주입할 빈이 존재하지 않으면 오류를 발생시킵니다.
  • 주입 위치: 멤버 변수, 생성자, Setter 메서드 등 다양한 곳에 사용 가능합니다.
  • 타입 매칭: 같은 타입의 빈이 여러 개일 경우, Qualifier 애노테이션을 사용해 구체적으로 지정할 수 있습니다.

예시 코드:

@Service
public class UserService {

    @Autowired
    private UserDao userDao; // 타입 매칭으로 의존성 주입

    public void performService() {
        userDao.getUser();
    }
}

🧑‍🏫 3. @Resource: JNDI 리소스와 이름 기반 주입

@ResourceJSR-250 표준 애노테이션으로, 스프링 2.5부터 지원됩니다. 주로 JNDI 리소스환경 설정 값과 같은 자원을 주입할 때 사용됩니다. 이름으로 매칭하여 주입되므로 주입하려는 빈의 이름을 명확히 설정해야 합니다.

특징:

  • JNDI 리소스와 연계할 수 있어 데이터베이스 연결 등에서 사용됩니다.
  • 이름 기반 매칭: 매칭할 빈의 이름이 일치해야 합니다.

예시 코드:

@Service
public class UserService {

    @Resource(name = "userDao") // 이름으로 빈 매칭
    private UserDao userDao;

    public void performService() {
        userDao.getUser();
    }
}

🔄 4. @Inject: JSR-330 표준 의존성 주입

@InjectJSR-330 표준에 따른 애노테이션으로, 특정 프레임워크에 종속되지 않는 애플리케이션을 설계할 때 사용됩니다. 스프링뿐만 아니라 다른 DI 프레임워크에서도 사용할 수 있습니다.

특징:

  • 타입 기반 매칭: 스프링의 @Autowired와 유사하게 타입에 따라 주입합니다.
  • 자바 표준: 스프링 이외의 다른 DI 컨테이너와도 호환됩니다.
  • 추가 라이브러리: 사용 시 클래스패스에 javax.inject 라이브러리가 필요합니다.

예시 코드:

public class UserService {

    @Inject
    private UserDao userDao;

    public void performService() {
        userDao.getUser();
    }
}

🌐 5. XML에서의 Annotation 설정

스프링에서 Annotation을 활성화하려면 XML 설정 파일에서 관련 설정을 추가해야 합니다.

예시: XML 설정에서 @Autowired 활성화

<beans xmlns="http://www.springframework.org/schema/beans"
       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:annotation-config/>  <!-- 애노테이션 설정 활성화 -->
</beans>

⚙️ 6. @Autowired vs. @Resource vs. @Inject 비교

Annotation지원 버전매칭 기준주요 사용처종속성
@Autowired스프링 2.5+타입 매칭스프링 프레임워크 내부스프링 종속
@Resource스프링 2.5+, JSR-250이름 매칭JNDI 리소스 또는 환경 설정과 연동JNDI 종속
@Inject스프링 3.0+, JSR-330타입 매칭프레임워크 비종속 애플리케이션 설계Java 표준

📝 Spring 설정: @Annotation을 통한 빈 의존 관계 설정

스프링에서는 Annotation을 통해 빈 객체의 생성 및 의존성 주입을 간단하게 관리할 수 있습니다. Annotation 기반 설정은 유지보수가 쉽고 직관적이며, 코드와 설정의 결합을 최소화할 수 있습니다. 아래에서 다양한 상황에서 Annotation을 사용하는 예제와 함께 설정 방법을 설명합니다.


🌟 1. Component Scan으로 빈 객체 자동 등록

@ComponentScan은 스프링이 지정된 패키지 내의 클래스들을 자동으로 스캔하고 빈으로 등록하게 합니다.

예시: XML 설정에서 Component Scan 적용

<context:component-scan base-package="com.example"/>

Java 설정에서 Component Scan 적용

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

🧑‍🏫 2. 주요 Annotation과 역할

스프링에서는 여러 Annotation을 제공해 객체를 빈으로 등록하고 주입할 수 있습니다. 주요 Annotation과 그 역할은 다음과 같습니다.

@Component

  • 일반적인 빈 객체를 등록할 때 사용합니다.
@Component
public class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending email: " + message);
    }
}

@Service

  • 비즈니스 로직을 구현하는 클래스에 사용합니다.
@Service
public class UserService {
    public String getUserInfo(String userId) {
        return "User Info: " + userId;
    }
}

@Repository

  • 데이터 접근 계층(DAO) 클래스에 사용합니다.
@Repository
public class UserRepository {
    public void saveUser(String user) {
        System.out.println("User saved: " + user);
    }
}

@Controller

  • 웹 요청을 처리하는 컨트롤러 클래스에 사용합니다.
@Controller
public class UserController {
    @Autowired
    private UserService userService;

    public String handleRequest(String userId) {
        return userService.getUserInfo(userId);
    }
}

🔄 3. @Autowired를 사용한 의존성 주입

1) 필드 주입

@Service
public class OrderService {
    
    @Autowired
    private UserRepository userRepository;

    public void processOrder(String orderId) {
        userRepository.saveUser(orderId);
    }
}
  • 설명: 필드에 직접 의존성을 주입합니다. 주로 사용되지만 테스트 및 유지보수에서는 생성자 주입이 권장됩니다.

2) 생성자 주입

@Repository
public class ProductRepository {
    private final DataSource dataSource;

    @Autowired
    public ProductRepository(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}
  • 설명: 생성자에 의존성을 주입해 객체가 생성될 때 필요한 모든 의존성을 강제합니다. 불변 객체 설계를 위해 권장됩니다.

3) Setter 주입

@Component
public class PaymentService {

    private DataSource dataSource;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
}
  • 설명: Setter 메서드를 통해 의존성을 주입합니다. 선택적인 의존성을 주입할 때 유용합니다.

🧩 4. @Qualifier로 여러 개의 동일 타입 빈 구분

만약 동일한 타입의 빈이 여러 개 있을 경우, @Qualifier를 사용해 특정 빈을 선택할 수 있습니다.

@Service
public class CustomerService {

    @Autowired
    @Qualifier("premiumCustomerRepository")
    private CustomerRepository customerRepository;
}
  • 설명: @Qualifier를 사용해 특정 빈을 명시합니다.

⚙️ 5. 전체 설정 및 주입 흐름

  1. Component Scan으로 지정된 패키지 내의 클래스를 스캔하여 빈을 자동으로 등록합니다.
  2. @Autowired와 같은 Annotation을 사용해 의존성을 주입합니다.
  3. 필요한 경우 @Qualifier로 특정 빈을 구분합니다.

📝 Spring 설정: 빈(Bean) 객체의 생성 범위와 Factory Method 활용

스프링에서 빈(Bean) 객체의 생성 단위(Scope)Factory Method를 활용한 객체 생성은 유연한 애플리케이션 설계를 돕습니다. 아래에서 범위(Scope)의 종류Factory Method를 통한 빈 생성 방법을 일반적인 예시와 함께 자세히 설명합니다.


🌱 1. 스프링 빈(Bean) 객체의 생성 단위 (Scope)

스프링은 빈의 범위(Scope)를 설정해 객체가 어떻게 생성되고 관리될지를 정의합니다. 기본적으로 singleton 범위를 사용하지만, 상황에 따라 prototype, request, session 범위를 설정할 수 있습니다.

Scope설명
singleton컨테이너당 하나의 인스턴스만 생성하여 사용합니다. (기본값)
prototype빈을 요청할 때마다 새로운 인스턴스가 생성됩니다.
requestHTTP Request마다 새로운 인스턴스가 생성됩니다.
sessionHTTP Session마다 새로운 인스턴스가 생성됩니다.

requestsession 범위는 웹 애플리케이션 컨텍스트(WebApplicationContext)에서만 사용할 수 있습니다.


🔄 Scope 예제: XML을 통한 설정

XML 설정 예시

<bean id="userService" class="com.example.UserService" scope="singleton"/>
<bean id="orderService" class="com.example.OrderService" scope="prototype"/>
  • 설명:
    • userService 빈은 singleton 범위로 설정되어 컨테이너 내에서 하나의 인스턴스만 사용됩니다.
    • orderService 빈은 prototype 범위로 설정되어 빈을 요청할 때마다 새로운 인스턴스가 생성됩니다.

Java 코드에서 Scope 활용

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

UserService userService1 = context.getBean("userService", UserService.class);
UserService userService2 = context.getBean("userService", UserService.class);
System.out.println(userService1 == userService2); // true (singleton)

OrderService orderService1 = context.getBean("orderService", OrderService.class);
OrderService orderService2 = context.getBean("orderService", OrderService.class);
System.out.println(orderService1 == orderService2); // false (prototype)

🏭 2. Factory Method를 통한 객체 생성

스프링에서는 Factory Method를 사용해 객체를 생성할 수 있습니다. 이 방법은 Singleton 패턴을 사용해 한 개의 인스턴스만 생성하거나, 복잡한 초기화 과정을 수행할 때 유용합니다.

Factory Method 패턴 예제

public class DatabaseConnection {

    private static DatabaseConnection instance;

    // Private 생성자: 외부에서 직접 생성 불가
    private DatabaseConnection() {}

    // Static Factory Method를 통한 객체 생성
    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
}
  • 설명:
    • getInstance() 메서드는 Singleton 패턴을 구현하여 하나의 인스턴스만 생성합니다.
    • 외부에서는 이 메서드를 통해서만 객체를 가져올 수 있습니다.

🔄 Factory Method를 활용한 XML 설정

<bean id="dbConnection" class="com.example.DatabaseConnection" factory-method="getInstance"/>
  • 설명:
    • 스프링 컨테이너가 XML 설정에 따라 getInstance() 메서드를 호출해 객체를 생성합니다.
    • 이 방식은 Singleton 패턴과 잘 어울리며, 초기화가 복잡한 객체 생성에 적합합니다.

🧑‍💻 Factory Method를 통한 빈 사용 예제

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

DatabaseConnection dbConnection1 = context.getBean("dbConnection", DatabaseConnection.class);
DatabaseConnection dbConnection2 = context.getBean("dbConnection", DatabaseConnection.class);

System.out.println(dbConnection1 == dbConnection2); // true (같은 인스턴스)
  • 설명:
    • dbConnection 빈은 Factory Method를 통해 생성되며, Singleton 패턴에 따라 동일한 인스턴스를 반환합니다.

📝 스프링 빈(Bean)의 생명 주기 (Life Cycle)

스프링 컨테이너에 의해 관리되는 빈(Bean) 객체는 특정한 생명 주기를 따릅니다. 빈의 생명 주기는 객체가 생성되고 의존성이 주입된 후, 애플리케이션이 종료될 때까지 다양한 단계로 구성됩니다. 스프링은 이 생명 주기 동안 개발자가 필요한 메서드를 호출할 수 있게 하여 초기화와 소멸 등의 과정을 관리합니다.

아래에서는 스프링 빈의 생명 주기를 전체적으로 설명하고, 각 단계가 어떤 역할을 하는지 자세히 다룹니다.


🌱 1. 스프링 빈의 생명 주기 단계

🔄 주요 생명 주기 단계

  1. 빈 생성:

    • 스프링 컨테이너가 빈 객체를 new 연산자를 통해 인스턴스화합니다.
    • XML 설정 또는 자바 설정 파일을 바탕으로 빈 객체가 생성됩니다.
  2. 의존성 주입:

    • 필요한 의존성(Dependency)이 주입됩니다.
    • Setter 주입, 생성자 주입, 필드 주입 등 다양한 주입 방식이 사용될 수 있습니다.
  3. 초기화 메서드(init-method) 실행:

    • 빈 객체가 초기화 작업을 수행합니다.
    • 개발자가 초기화 로직을 정의한 메서드(@PostConstruct 혹은 XML의 init-method)가 호출됩니다.
  4. 빈 사용:

    • 애플리케이션에 의해 빈 객체가 사용됩니다.
    • 이 단계에서 빈은 필요한 작업을 수행하며 요청을 처리합니다.
  5. 소멸 메서드(destroy-method) 실행:

    • 애플리케이션이 종료될 때 빈 객체를 정리하는 메서드가 실행됩니다.
    • @PreDestroy 어노테이션이나 XML의 destroy-method를 사용합니다.
  6. 빈 소멸:

    • 스프링 컨테이너가 종료되고 빈 객체가 메모리에서 제거됩니다.

📋 2. 스프링 빈의 생명 주기 흐름 (상세 단계)

  1. 설정 파일 읽기:

    • 스프링 컨테이너가 XML 설정 파일이나 자바 구성 파일을 읽어 빈 정의를 로드합니다.
  2. 빈 속성 설정:

    • 스프링은 설정된 프로퍼티(property) 값을 빈 객체에 주입합니다. 이때 의존성이 함께 설정됩니다.
  3. setBeanName() 호출:

    • 빈 객체가 BeanNameAware 인터페이스를 구현한 경우, 해당 메서드가 호출됩니다.
    • 이 메서드는 현재 빈의 이름을 제공받을 수 있게 합니다.
  4. setBeanFactory() 또는 setApplicationContext() 호출:

    • 빈이 BeanFactoryAware 또는 ApplicationContextAware 인터페이스를 구현한 경우 호출됩니다.
    • 이 단계에서 빈은 자신이 속한 컨테이너 정보를 알 수 있습니다.
  5. postProcessBeforeInitialization() 호출:

    • BeanPostProcessor를 통해 초기화 직전에 빈을 조작할 수 있습니다.
  6. afterPropertiesSet() 호출:

    • 빈 객체가 InitializingBean 인터페이스를 구현했다면, 이 메서드가 호출됩니다.
  7. 커스텀 초기화 메서드 실행:

    • 설정된 init-method@PostConstruct 메서드가 실행됩니다.
  8. 빈 사용:

    • 빈이 준비되면 애플리케이션에서 해당 빈을 사용할 수 있습니다.
  9. postProcessAfterInitialization() 호출:

    • 초기화 후 추가 처리가 필요한 경우 BeanPostProcessor가 실행됩니다.
  10. destroy() 호출 및 소멸 메서드 실행:

  • 스프링 컨테이너가 종료되면, 빈의 destroy() 메서드@PreDestroy 메서드가 호출됩니다.

🔄 3. 스프링 생명 주기 메서드와 인터페이스 사용 예제

Java 설정에서 @PostConstruct와 @PreDestroy 사용

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class UserService {

    @PostConstruct
    public void init() {
        System.out.println("UserService 초기화 완료!");
    }

    public void useService() {
        System.out.println("UserService 사용 중...");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("UserService 소멸 중...");
    }
}

XML에서 init-method와 destroy-method 설정

<bean id="userService" class="com.example.UserService" 
      init-method="init" destroy-method="destroy"/>
  • 설명:
    1. init() 메서드는 빈이 생성된 후 초기화 작업을 수행합니다.
    2. destroy() 메서드는 애플리케이션 종료 시 빈을 정리합니다.

🌟 4. BeanPostProcessor를 활용한 초기화 전후 처리

public class CustomBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("Before Initialization: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("After Initialization: " + beanName);
        return bean;
    }
}
  • 설명:
    • BeanPostProcessor를 구현하면 빈의 초기화 전후에 추가 작업을 수행할 수 있습니다.

🔍 5. 정리 및 생명 주기의 중요성

스프링의 빈 생명 주기는 객체가 생성되고 소멸될 때까지의 모든 과정을 포함합니다. 생명 주기 중에 개발자가 초기화 작업소멸 작업을 정의할 수 있으며, 이를 통해 애플리케이션의 안정성과 유지보수성을 높일 수 있습니다.

📝 주요 정리:

  • 초기화 메서드: 객체 생성 후 필요한 작업 수행.
  • 소멸 메서드: 객체 소멸 전 리소스 정리.
  • BeanPostProcessor: 빈의 초기화 전후에 추가 작업 수행 가능.

스프링의 생명 주기 관리를 활용해 애플리케이션의 자원 효율성을 극대화하세요! 🚀

0개의 댓글