스프링 AOP와 DI

Dear·2025년 6월 20일

TIL

목록 보기
45/74

💙 DI(Dependency Injection)

의존 관계 주입

의존성

어떤 클래스가 자신의 임무를 다하기 위해 필요한 값(필드 값)이나 사용할 다른 클래스와의 관계

주입

어떤 클래스의 인스턴스에 대해 외부로부터 '의존성'을 설정하는 것

의존 관계 주입 컨테이너

어떤 클래스가 필요로 하는 값이나 인스턴스를 생성, 취득하고, 그 클래스의 인스턴스에 대해 설정하는 역할

인스턴스를 생성, 취득하는 코드를 직접 만들지 않아도 된다.
그 결과 클래스간 관계가 느슨한 결합이 되어 의존성은 약해진다.

💙 스프링의 주입

1. 생성자를 통한 주입

필요한 의존성을 포함하는 클래스의 생성자를 만들고 이를 통해 의존성을 주입

public class Dog {

    private final Animal animal;

    // 생성자 주입
    public Dog(Animal animal) {
        this.animal = animal;
    }

    public void speak() {
        animal.makeSound();
    }
}

2. 설정 메서드를 통한 주입

Setter 메소드를 이용한 의존성 삽입

public class Dog {

    private Animal animal;

    // 설정자 메서드 (setter)를 통해 주입
    @Autowired
    public void setAnimal(Animal animal) {
        this.animal = animal;
    }

    public void speak() {
        animal.makeSound();
    }
}

💙 DI 용어

빈(Bean)

스프링이 IoC 방식으로 관리하는 객체

스프링이 직접 생성과 제어를 담당하는 객체를 Bean이라고 부름

빈 팩토리(Bean Factory)

스프링의 IoC를 담당하는 핵심 컨네이너

Bean을 등록, 생성 조회, 반환하는 기능을 담당
이 Bean Factory를 바로 사용하지 않고 이를 확장한 ApplicationContext를 주로 이용

애플리케이션 컨텍스트(Application Context)

BeanFactory를 확장한 IoC 컨테이너

Bean을 등록하고 관리하는 기능은 BeanFactory와 동일하지만 스프링이 제공하는 각종 부가 서비스를 추가로 제공
스프링에서는 ApplicationContext를 BeanFactory 보다 더 많이 사용

설정 메타정보(Configuration metadata)

ApplicationContext 또는 BeanFactory가 IoC를 적용하기 위해 사용되는 메타 정보

설정 메타정보는 IoC 컨테이너에 의해 관리되는 Bean 객체를 생성하고 구성할 때 사용

💙 컴포넌트 스캔(Component Scan)

스프링(Spring)이 자동으로 빈(Bean)을 찾아서 등록해주는 기능

Bean을 등록하고 Property를 추가하고 Constructor-arg를 사용하는 것은 수동 DI

개발자가 일일이 설정 파일에 Bean을 등록하지 않아도, 특정 패키지 이하에 있는 클래스 중에서 어노테이션(@Component 등)이 붙은 클래스들을 자동으로 스프링 컨테이너에 등록한다.

애너테이션용도 설명
@Component일반적인 스프링 컴포넌트
@Controller웹 계층 (MVC의 Controller 역할)
@Service서비스 계층
@Repository데이터 접근 계층 (DAO)

스캔 범위

@SpringBootApplication 또는 @ComponentScan이 붙은 클래스의 하위 패키지들을 스캔

@SpringBootApplication  // 내부에 @ComponentScan 포함
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

위 클래스가 com.example에 있으면, com.example 하위의 모든 패키지를 스캔

직접 경로 지정도 가능

@ComponentScan(basePackages = "com.example.service")

💙 빈 생명주기(Bean Lifecycle)

스프링 컨테이너가 Bean을 생성하고, 의존성 주입하고 초기화한 뒤, 소멸하는 전체 과정

  1. 객체 생성
    new 키워드로 객체가 생성(예: 생성자 호출)

  2. 의존성 주입
    @Autowired 또는 <property> 등으로 필요한 의존 객체가 주입됨

  3. 초기화 콜백
    빈이 생성된 후 초기화 작업 수행
    셋 중 하나 사용
    - @PostConstruct
    - InitializingBeanafterPropertiesSet()
    - XML 설정의 init-method

  4. 빈 사용 (애플리케이션 로직 수행)

  5. 소멸 전 콜백
    컨테이너 종료 직전에 정리 작업 수행
    셋 중 하나 사용
    - @PreDestroy
    - DisposableBean의 destroy()
    - XML 설정의 destroy-method

방식초기화소멸
애너테이션@PostConstruct@PreDestroy
인터페이스 구현InitializingBeanDisposableBean
XML 방식init-methoddestroy-method
@Component
public class MyBean {

    @PostConstruct
    public void init() {
        System.out.println("초기화 로직 실행");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("소멸 전 정리 작업");
    }
}

💙 AOP (Aspect-Oriented Programming)

관점 지향 프로그래밍이라는 뜻으로 공통 기능(로깅, 보안, 트랜잭션 등)을 핵심 비즈니스 로직과 분리해서 코드를 깔끔하고 재사용성 높게 만드는 프로그래밍 방식

AOP는 공통 관심사를 분리해서 코드 중복을 줄이고 유지보수를 쉽게 만들어wnsek.

주요 용어

용어설명
Aspect공통 기능(로깅, 트랜잭션 등)을 담는 모듈 클래스
AdviceAspect 안의 실제 실행 코드 (언제 실행할지 설정 가능)
Join PointAdvice가 실행될 수 있는 지점 (예: 메서드 실행)
PointcutAdvice가 실행될 지점 조건을 지정
WeavingAdvice를 실제 코드에 **삽입(적용)**하는 행위

AOP 사용 시기

적용 사례설명
로깅모든 메서드 실행 전/후 로그 출력
트랜잭션 처리메서드 시작~끝 사이에 트랜잭션 열고 닫기
보안 인증/인가실행 전에 사용자 권한 검사
성능 측정 (Timer)메서드 실행 시간 측정
예외 처리 공통화예외 발생 시 공통 로직 수행

AOP 적용 전

public class OrderService {
    public void placeOrder() {
        System.out.println("[공통] 로그: 주문 시작");
        // 주문 처리 핵심 로직
        System.out.println("주문 처리 중...");
        System.out.println("[공통] 로그: 주문 완료");
    }
}

중복이 많아지고 유지보수가 어려워진다.

AOP 적용 후

// 핵심 로직(Service)
@Service
public class OrderService {
    public void placeOrder() {
        System.out.println("주문 처리 중...");
    }
}


// 공통 로직(Aspect)
@Aspect
@Component
public class LogAspect {

    // 어느 메서드에 적용할지: com.example.service 패키지의 모든 메서드
    @Pointcut("execution(* com.example.service.OrderService.*(..))")
    public void orderServiceMethods() {}

    // 메서드 실행 전에 로그 찍기
    @Before("orderServiceMethods()")
    public void logBefore() {
        System.out.println("[공통] 로그: 주문 시작");
    }

    // 메서드 실행 후에 로그 찍기
    @After("orderServiceMethods()")
    public void logAfter() {
        System.out.println("[공통] 로그: 주문 완료");
    }
}

🤍 회고

오늘은 스프링의 핵심 개념인 DI(의존성 주입)과 AOP(관점 지향 프로그래밍)를 학습했다.
DI를 통해 객체 간의 의존성을 스프링이 대신 관리해준다는 개념을 이해할 수 있었고, 이를 통해 코드의 결합도를 낮추고 테스트나 유지보수가 훨씬 쉬워진다는 장점을 체감할 수 있었다.

profile
친애하는 개발자

0개의 댓글