카테고리 없음

[Spring_4기 본캠프] Spring 플러스 - AOP | Day 63

austindynasty 2025. 1. 22. 20:50

1. OOP 객체 지향 프로그래밍의 문제점이 떠오르다

- 특정 관심사 업무 코드에 트랜잭션, 보안, 로깅, 접근제어 등의 코드가 존재하게 되면서 핵심 업무와는 관련이 없지만 애플리케이션에 필수적인 부가 기능이 생김

- 관심사 관점에서 이러한 부가 기능들을 횡단 관심사로 부르기로 함

- 비즈니스 클래스에 핵심 기능과 부가 기능들이 공존하게 되면서 메서드의 복잡도가 증가하고 비즈니스 코드 파악이 어렵게 되었다.

- 부가 기능의 불특정 다수 메서드가 반복적으로 구현되면서 횡단 관심사의 모듈화가 어려워짐

- 중복되는 부가 기능에 수정 및 삭제가 필요해지면 사용되는 모든 곳에 수정 및 삭제 동작을 해줘야 함 

 

2. AOP (Aspect Oriented Programming) : 관점 지향 프로그래밍

* AOP 관련 개념 

- 어드바이스(Advice) : around, before, after, afterThrowing 등등 타켓에 제공할 부가기능을 담고 있는 모듈

- 포인트컷(Pointcut) : 어드바이스를 적용할 구체적인 범위를 표현하는 규칙 (범위지정.. 즉 파일 경로!)

- 타겟(Target) : 어드바이스가 적용될 객체 

- 조인포인트(타겟 안에 메서드) : 어드바이스가 적용되는 지점

- 어스팩트 (위의 것들이 모두 모여 만들어진 하나의 모듈) : 어드바이스와 포인트컷을 하나로 묶은 모듈

[출처] 나의 손 (피그잼)

* AOP 동작 원리

[출처] 나의 손 (피그잼)

 

- 자바 객체를 빈으로 등록할 때 빈 후처리기(BeanPostProcessor)를 거치게 됩니다. 이 때 AOP 설정이 적용된 객체를 감지하고 해당 객체를 프록시 객체로 감싼 후 스프링 컨테이너에 등록함

- Spring AOP 동작 과정 : AOP는 내부적으로 프록시 객체를 생성하고 이를 통해 Advice를 실행

① Pointcut 분석 : Spring은 애플리케이션의 모든 Bean을 분석하여, 설정된 Pointcut에 맞는 Join Point를 찾아냄

② 프록시 객체 생성 : AOP가 적용된 Bean에 대해 프록시 객체를 생성합니다. 이 프록시 객체는 실제 Target 객체의 대리 역할을 수행

③ Advice 실행 

     - Advice가 설정된 경우 먼저 Advice 코드가 실행됨

     - 실제 Target 객체의 메서드가 실행됨

     - 필요 시 후처리 Advice가 실행됨

 

→ Spring AOP는 프록시 객체를 사용해 Target 객체의 메서드 호출을 가로채고, 횡단 관심사를 처리한다! 

 

3. 그래서 AOP를 사용하는 이유가 뭘까?

- 정리하자면, 핵심 기능에 필수적이지만 반복되는 부가 기능들(트랜잭션, 로깅, 접근제어)을 따로 빼내서 관리해주자! 핵심 기능에만 집중할 수 있도록 부가 기능은 AOP가 처리해준다.

 

4. 프록시 프록시.. 프록시가 뭐에요?

1) 프록시 객체의 특징

- 지연 로딩 : 실제 엔티티를 데이터베이스에서 로드하지 않고 메서드 호출 시점에 데이터베이스에서 로드한다. 이는 초기 로딩 시점에 불필요한 데이터 로딩을 방지하고 성능을 최적화 하는 데에 도움을 준다.

- 프록시 생성 : JPA는 프록시 객체를 생성할 때 원래 엔티티 클래스를 상속받고, 실제 데이터는 필요할 때 로드한다.

- 트랜잭션 범위 : 프록시 객체는 트랜잭션이 유효한 범위 내에서만 동작하며, 트랜잭션이 종료되면 데이터 로딩이 불가능할 수 있다. 

→ 프록시 객체는 실제 엔티티에 대한 접근을 지연시키며 실제 엔티티가 필요할 때까지 데이터베이스 쿼리를 실행하지 않는다.

 

2) 프록시 객체의 동작

- 프록시 초기화 : 프록시 객체가 메서드를 호출할 때, 해당 메서드가 실제 엔티티를 필요로 하는 경우에 데이터베이스에서 데이터를 로드한다.

- 쿼리 실행 : 메서드를 호출하면 프록시 객체는 실제 엔티티를 로드하기 위해 데이터베이스 쿼리를 실행한다.

- 결과 반환

 

3) 프록시 객체의 장점과 단점

- 장점 

> 성능 최적화 : 데이터가 실제로 필요할 때만 로드되므로 불필요한 데이터 로딩 방지

> 메모리 절약 : 초기 로딩 시에 모든 연관 데이터를 로드하지 않아 메모리 사용 절약

- 단점

> N+1 : 여러 엔티티를 처리할 때 연관된 엔티티에 대한 지연 로딩이 반복적으로 발생

> 프록시 초기화 : 프록시 객체가 실제 데이터베이스 쿼리를 발생시키므로 주의 필요

 

5. Self-Invocation 문제 

- 대상 객체 내에서 자신의 메서드를 직접 호출하면 AOP가 적용되지 않는 문제

- 프록시에 외부에서 호출되는 경우에만 트랜잭션을 적용할 수 있다. 하지만 클래스 내부에서 메서드를 호출하면 프록시를 거치지 않고(트랜잭션이 적용되지 X) 실제 메서드를 바로 호출하게 되므로 트랜잭션이 제대로 적용되지 않는다.

- 해결법

> 클래스 분리하기(권장) : 트랜잭셔널이 적용된 메서드를 다른 클래스로 분리하면 해결 할 수 있다. 서로 다른 클래스에서 호출하면 프록시를 통해 트랜잭션이 적용된다.

> 자체 트랜잭션 호출을 AOP로 강제 : Spring AOP와 관련 설정을 이용해 내부 호출을 감지할 수 있도록 설정한다. (But 설정 까다로움)

> Proxy 직접 사용 : Spring이 생성한 프록시 객체를 직접 주입받아 호출하면 트랜잭션을 적용할 수 있다.