💡 AOP(Aspect-Oriented Programming)는 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 모듈화하여 재사용할 수 있도록 지원하는 것입니다.

각각의 Service를 핵심적인 관점으로 보았을 때는 User와 Order의 공통된 요소가 없지만 부가적인 관점으로 보았을 때는 공통된 요소를 볼 수 있습니다.

부가적인 관점에서 바라보면 공통된 메서드를 확인할 수 있습니다.
기존에 OOP에서 바라보는 관점을 다르게 하여 부가기능적인 측면에서 보았을 때 공통된 요소를 추출 하자는 것입니다. 이때 공통된 부분을 잘라내는 것을 AOP를 크로스 컷팅(Cross-Cutting)이라고 부르기도 합니다.

OOP: 비지니스 로직의 모듈화

  • 모둘화의 핵심 단위는 비지니스 로직

AOP: 인프라 혹은 부가기능의 모듈화

  • 대표적인 예: 모니터링 및 로깅, 오류 검사 및 처리, 동기화, 성능 최적화(캐싱) 등
  • 모듈화된 모듈들의 주 목적 외에 필요한 부가적인 기능들을 모듈화 하는 것

간단하게 한줄로 AOP를 정리해보자면 AOP는 공통된 기능을 모듈화하여 재사용하는 기법입니다.
OOP에선 공통된 기능을 재사용하는 방법으로 상속이나 위임을 사용합니다.
하지만 전체 애플리케이션에서 부가기능들을 상속이나 위임을 처리하기에는 깔끔한 모듈화가 어렵습니다.
그래서 등장한 것이 바로 AOP입니다.

AOP 장점

  • 애플리케이션 전체에 흩어진 공통 기능을 하나의 장소에서 관리할 수 있어서 유지보수가 좋습니다.
  • 핵심 로직과 부가 기능 로직은 분리하기 때문에 핵심 로직은 자신의 목적만 신경 쓸 수 있습니다.

AOP 주요 용어

  • Join point
    • Advice가 적용될 위치, 끼어들 수 있는 지점.
    • 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
  • PointCut
    • Join point 중에서 advice가 적용될 위치를 선별하는 기능
    • Spring AOP는 proxy 기반이기 때문에 Join point와 PointCut은 메서드 실행 시점에서만 가능
  • Target
    • advice의 대상이 되는 객체
    • PointCut으로 결정
  • advice
    • 실질적인 부가 기능 로직을 정의하는 곳
    • 특정 Join point에서 Aspect에 의해 취해지는 조치
  • Aspect
    • advice + PointCut를 모듈화 한 것
    • @Aspect와 같은 의미

AOP 적용 방식

AOP 적용 방식에는 3가지 방법이 있습니다.

  • 컴파일 시점
    • .java 파일을 컴파일러를 통해 .class를 만드는 시점에 부가 기능 로직을 추가하는 방식
    • 모든 지점에 적용 가능
    • AspectJ가 제공하는 특별한 컴파일러를 사용해야 하기 때문에 특별한 컴파일러가 필요한 점과 복잡하다는 단점
  • 클래스 로딩 시점
    • .class 파일을 JVM 내부의 클래스 로더에 보관하기 전에 조작하여 부가 기능 로직 추가하는 방식
    • 모든 지점에 적용 가능
    • 특별한 옵션과 클래스 로더 조작기를 지정해야하므로 운영하므로 어려움
  • 런타임 시점
    • 스프링이 사용하는 방식
    • 컴파일이 다 끝나고 자바가 실행된 다음에 동작하는 런타임 방식
    • 실제 대상 코드는 그대로 유지되고 프록시를 통해 부가 기능이 적용
    • 프록시는 메서드 오버라이딩 개념으로 동작하기 때문에 메서드에만 적용 가능스프링 빈에만 AOP를 적용 가능
    • 스프링만 있으면 AOP를 적용할 수 있기 때문에 스프링 AOP는 런타임 방식을 사용

Spring AOP

  • Spring에서 제공하는 Spring AOP는 proxy AOP 기반의 AOP 구현체이다.
  • proxy 객체를 사용하는 것은 접근 제어 및 부가 기능을 추가하기 위해서이다.
  • 위에서 말한듯 Spring AOP는 Spring Bean에만 적용할 수 있습니다.
  • Spring AOP는 모든 AOP의 기능을 제공하는 것이 목적이 아닌, 중복 코드나 proxy 클래스 작성의 번거로움 등 흔한 문제를 해결하기 위한 솔루션을 제공하는 것이 목적이다.

프록시 패턴

proxy 패턴에서는 interface가 존재하고 Client는 이 interface 타입으로 Proxy 객체를 사용한다. Proxy 객체는 기존의 타겟 객체(Real Subject)를 참조한다. Proxy 객체와 기존의 타겟 객체의 타입은 같고, Proxy는 원래 할 일을 가지고 있는 Real Subject를 감싸서 Client의 요청을 처리한다.

예시

이해하기 쉽게 Spring AOP를 적용하기 전과 적용한 후의 코드 예시를 보여드리겠습니다.

적용하기 전 코드:

public class UserService {
    public void addUser(User user) {
        // 사용자 추가 로직
    }
    
    public void deleteUser(int userId) {
        // 사용자 삭제 로직
    }
}

적용한 후 코드:

public interface UserService {
    void addUser(User user);
    void deleteUser(int userId);
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(User user) {
        // 사용자 추가 로직
    }
    
    @Override
    public void deleteUser(int userId) {
        // 사용자 삭제 로직
    }
}

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.UserService.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("메서드 실행 전 로깅 작업");
    }
}

위의 예시에서 UserService 인터페이스와 그를 구현한 UserServiceImpl 클래스를 정의하였다. 그리고 AOP를 적용하기 위해 LoggingAspect 클래스를 작성하였다. LoggingAspect는 @Aspect 어노테이션을 사용하여 AOP 관련 설정을 하고, @Before 어노테이션을 사용하여 메서드 실행 전에 로깅 작업을 수행한다.

이렇게 Spring AOP를 적용하면 UserService의 메서드를 호출할 때마다 로깅 작업이 수행된다.

profile
백엔드 개발자입니다.

0개의 댓글

Powered by GraphCDN, the GraphQL CDN