본문 바로가기
백엔드

[Spring Boot] IoC, DI, AOP 정리

by 육빔 2024. 8. 11.
728x90
반응형

스프링 공부를 시작하면 가장 처음 접하는 3가지 주제이다. 확실하게 정리하고자 한다.

 

IoC (Inversion of Control, 제어의 역전)

  • 설명: IoC는 애플리케이션의 제어 흐름을 프레임워크나 컨테이너가 담당하도록 하는 프로그래밍 원칙입니다. 일반적으로 객체의 생성, 초기화, 라이프사이클 관리 등의 제어권을 개발자가 아닌 Spring과 같은 프레임워크에 위임합니다. 이로 인해 객체 간의 결합도가 낮아지고, 유연하고 테스트하기 쉬운 구조를 가지게 됩니다.

처음에 말을 이해하기가 쉽지 않았는데 간단하게 말하면

pubic class Family{
	son = new Son();
}

 

가족이 되려면 간단하게 아들을 새로 낳아야 되는데 낳는 과정, 관리하기 힘들기에

 

pubic class Family{
	private Son son;
}

 

단순히 스프링 컨테이너에서 처리를 해줘서 아들을 넣어주는 과정을 이해하면 될 것 같다.

 

DI (Dependency Injection, 의존성 주입)

  • 설명: DI는 객체가 필요한 의존성을 외부에서 주입받는 방식으로, 객체 간의 결합도를 줄이고 모듈화된 구조를 만드는 데 기여합니다. Spring에서는 DI를 통해 객체를 구성하고 관리하며, 이를 통해 애플리케이션의 유연성과 유지보수성을 향상시킵니다.

방식은 3가지가 있다.

 

  • 생성자 주입: 객체의 생성자 메서드를 통해 의존성을 주입합니다.
public class Car {
    private Engine engine;

    @Autowired
    public Car(Engine engine) {  // 생성자 주입
        this.engine = engine;
    }

    public void start() {
        engine.start();
    }
}

 

 

요새는 거의다 생성자 주입 방식을 사용한다. 그 이유는 레퍼런스 객체 없이는 개체를 초기화 할 수 없기 때문이다. 현재는 @Autowired 없이도 사용가능하다.

 

이러한 방법은 Lombok으로 더욱 더 간편하게 할 수 있는데

 

@RequiredArgsConstructor 초기화 되지 않은 final 필드와 @NonNull 어노테이션이 붙은 필드에 대한 생성자 생성

@AllArgsConstructor 모든 필드에 대한 생성자 생성.

@NoArgsConstructor : 파라미터가 없는 디폴트 생성자를 생성

 

이런식으로 생성자를 사용할 수 있다. 이렇게 종류가 많은 이유는 

점층적 생성자, 자바 빈즈, Builder 패턴

이걸 한 번 검색해보면 이해가 잘 될 것이다.

 

  • 세터 주입: 객체의 세터 메서드를 통해 의존성을 주입합니다.
public class Car {
    private Engine engine;

    @Autowired
    public void setEngine(Engine engine) {  // 세터 주입
        this.engine = engine;
    }
}

 

세터를 사용하는 것도 주의가 있는데 객체의 불변성이 깨지기때문에 변경되지 말아야 할 객체의 경우 사용하면 안된다.(주민등록번호)

 

  • 필드 주입: 객체의 필드에 직접 주입합니다(추천되지 않음).
public class Car {
    @Autowired
    private Engine engine;  // 필드 주입
}

 

 

거의 안쓰는 방식인데 편리해서 쓰는 사람들이 꽤 많다. 안 쓰는 이유는 

final을 사용하지 않아 객체의 불변성을 보장하지 못하고, 테스트 할 때도 있어서 스프링의 의존도가 상당히 높기 때문에 테스트하기 어렵다는 단점으로 잘 사용하지 않는다.

 

 

AOP (Aspect-Oriented Programming, 관점 지향 프로그래밍)

  • 설명: AOP는 핵심 비즈니스 로직과 부가적인 관심사(횡단 관심사)를 분리하여 코드를 모듈화하는 프로그래밍 패러다임입니다. 횡단 관심사란 로깅, 보안, 트랜잭션 관리 등 애플리케이션 전반에 걸쳐 공통적으로 사용되는 기능을 말합니다. AOP를 사용하면 이러한 공통 기능을 비즈니스 로직에 침투시키지 않고, 독립적인 모듈로 분리할 수 있습니다.

 

  • Aspect: 횡단 관심사를 모듈화한 것. 보통 @Aspect 애노테이션을 사용하여 정의합니다.
  • Join Point: Aspect가 적용될 수 있는 위치, 메서드 실행이나 예외 발생 등이 이에 해당합니다.
  • Advice: Join Point에서 실행될 실제 동작을 정의합니다(예: Before, After, Around).
  • Pointcut: Advice가 적용될 Join Point를 결정하는 표현식입니다.

 

보통의 진행과정은 다음과 같다

1. 의존성 추가

spring-boot-starter-aop

 

2. 서비스 클래스 작성

 

3. aspect 클래스 작성

@Aspect
@Component
public class LoggingAspect {

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

    // 메서드 실행 전에 로깅
    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    // 메서드가 정상적으로 실행된 후 로깅
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
        System.out.println("Returned value: " + result);
    }

    // 메서드 실행 중 예외 발생 시 로깅
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "error")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
        System.out.println("Exception in method: " + joinPoint.getSignature().getName());
        System.out.println("Exception: " + error);
    }

    // 메서드 실행 전후로 로깅
    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Around before method: " + joinPoint.getSignature().getName());
        Object result = joinPoint.proceed(); // 메서드 실행
        System.out.println("Around after method: " + joinPoint.getSignature().getName());
        return result;
    }
}

 

이러면 실행될때마다 로킹이 찍힌다. 분리해서 반복적인 작업을 줄일 수 있다.

728x90
반응형