AOP 소개 - 핵심 기능과 부가 기능
- 애플리케이션 로직은 크게 핵심 기능과 부가 기능으로 나눌 수 있다.
핵심 기능
- 해당 객체가 제공하는 고유의 기능이다.
- 상품을 주문하거나, 결제를 하는 등의 핵심 비즈니스 로직을 의미한다.
부가 기능
- 핵심 기능을 보조하기 위해 제공되는 기능
- 로그를 출력하거나, 실행 결과를 측정하는 등의 추가 기능을 의미한다.
- 부가 기능 이름 그대로 핵심 기능을 보조하기 위해 존재하기 때문에 단독으로 사용되지 않고, 핵심 기능과 함께 사용된다.
- 보통 부가 기능은 여러 클래스에 걸쳐서 함께 사용되기 때문에 횡단 관심사가 된다.
- 즉, 하나의 부가 기능이 여러 곳에서 동일하게 사용된다는 것을 의미한다.
부가 기능 적용 시 발생하는 문제점
- 부가 기능은 적용해야 할 곳이 많다면 일일이 적용하는게 너무 힘들어진다.
- 별도의 유틸리티로 만들어도 결국 유틸리티를 호출하는 코드가 필요하다.
- 특히 try-catch-finally같은 구조가 필요하다면 더욱 복잡해진다.
- 그러나 자주 발생하는 부가 기능은 그만큼 많이 호출하기 때문에 그만큼 관리하기 힘들어진다.
- 심지어 부가 기능에 수정이 발생한다면 호출하는 부분을 모두 수정해야 한다.
- 정리
- 부가 기능을 적용할 때 아주 많은 반복이 필요하다.
- 부가 기능이 여러 곳에 퍼져서 중복 코드를 만들어낸다.
- 부가 기능을 변경할 때 중복 때문에 많은 수정이 필요하다.
- 부가 기능의 적용 대상을 변경할 때 많은 수정이 필요하다.
- 소프트웨어 개발에서 변경 지점은 하나가 될 수 있도록 잘 모듈화 되어야 한다.
- 그런데 부가 기능처럼 특정 로직을 애플리케이션 전반에 적용하는 문제는 일반적인 OOP 방식으로는 해결이 어렵다.
AOP 소개 - 애스펙트 (Aspect)
- 부가 기능과 부가 기능을 어디에 적용할 지 선택하는 기능을 합쳐서 하나의 모듈로 만든 것이다.
- 부가 기능의 쉬운 관리를 위해 만들어졌다.
- 부가 기능을 핵심 기능에서 분리하고 한 곳에서 관리하도록 한다.
- 부가 기능을 어디에 적용할지 선택할 수 있다.
- 스프링이 제공하는 어드바이저와 @Aspect가 이에 해당한다.
- 애플리케이션을 바라보는 관점을 하나하나의 기능에서 횡단 관심사 관점으로 달리 보는 것을 의미한다.
- 이런 애스팩트를 사용한 프로그래밍 방식을 관점 지향 프로그래밍(AOP, Aspect-Oriented Programming)이라고 한다.
- AOP는 OOP를 대체하기 위한 것이 아니라,
횡단 관심사를 깔끔하게 처리하기 어려운 OOP의 부족한 부분을 보조하는 목적으로 개발되었다. - AOP의 대표적인 구현으로 AspectJ 프레임워크가 있다.
- 스프링도 AOP를 지원하지만 대부분 AspectJ의 문법을 차용하고, AspectJ가 제공하는 기능의 일부만 제공한다.
- AspectJ 프레임워크는 스스로를 다음과 같이 설명한다.
- 자바 프로그래밍 언어에 대한 완벽한 관점 지향 확장
- 횡단 관심사의 깔끔한 모듈화
- 오류 검사 및 처리
- 동기화
- 성능 최적화(캐싱)
- 모니터링 및 로깅
AOP 적용 방식
- AOP를 사용할 때 부가 기능 로직이 실제 로직에 추가되는 방법은 크게 3가지가 있다.
- 컴파일 시점
- 클래스 로딩 시점
- 런타임 시점 (프록시)
위빙 (Weaving)
- 원본 로직에 부가 기능 로직이 추가되는 것을 의미한다.
컴파일 시점 (컴파일 타임 위빙)
.java
파일의 소스 코드를 사용해서.class
파일로 만드는 시점에 부가 기능 로직을 추가한다.- 이 떄는 AspectJ가 제공하는 특별한 컴파일러를 사용해야 한다.
- 부가 기능 코드가 핵심 기능이 있는 컴파일된 코드 주변에 붙는 방식이다.
- AspectJ 컴파일러는 Aspect를 확인해서 해당 클래스가 적용 대상인지 먼저 확인하고, 적용 대상인 경우에 부가 기능 로직을 적용한다.
- 다만, 컴파일 시점에 부가 기능을 적용하려면 특별한 컴파일러도 필요하고 복잡하다.
클래스 로딩 시점 (로드 타임 위빙)
- 자바를 실행하면 자바는
.class
파일을 JVM 내부의 클래스 로더에 보관한다. - 자바는
.class
파일을 JVM에 저장하기 전에 조작할 수 있는 기능을 제공한다.- 로드 타임 위빙은
.class
파일이 JVM에 저장하기 전에 애스펙트를 적용하는 방식이다.
- 로드 타임 위빙은
- 로드 타임 위빙은 자바를 실행할 때 특별한 옵션을 통해 클래스 로더 조작기를 지정해야 한다.
- 이 부분때문에 로드 타임 위빙이 번거롭고 운영하기 어렵다.
- 해당하는 옵션 :
java -javaagent
런타임 시점 (런타임 위빙)
- 런타임 시점은 컴파일도 다 끝나고, 클래스 로더에 클래스도 다 올라가서 이미 자바가 실행되고 난 다음을 말한다.
- 즉, 자바의 메인(main) 메서드가 이미 실행된 다음이다.
- 따라서 자바 언어가 제공하는 범위 안에서 부가 기능을 적용해야 한다.
- 스프링과 같은 컨테이너의 도움을 받고 프록시와 DI, 빈 포스트 프로세서같은 개념들을 총 동원해야 한다.
- 이렇게 하면 최종적으로 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있다.
- 지금까지 공부한 것은 프록시 방식인 런타임 위빙에 해당한다.
- 프록시를 사용하기 때문에 AOP 기능에 일부 제약이 있긴 하다.
- 그래도 특별한 컴파일러나, 자바를 실행할 때 복잡한 옵션과 클래스 로더 조작기를 설정하지 않아도 된다.
- 게다가 스프링만 있으면 얼마든지 AOP를 적용할 수 있다.
차이점 비교
- 컴파일 시점
- 실제 대상 코드에 애스팩트를 통한 부가 기능 호출 코드가 포함된다.
- AspectJ를 직접 사용해야 한다.
- 클래스 로딩 시점
- 실제 대상 코드에 애스팩트를 통한 부가 기능 호출 코드가 포함된다.
- AspectJ를 직접 사용해야 한다.
- 런타임 시점
- 실제 대상 코드는 그대로 유지되는 대신에 프록시를 통해 부가 기능이 적용된다.
- 항상 프록시를 통해야 부가 기능을 사용할 수 있다.
- 스프링 AOP가 사용하는 방식
AOP 적용 위치
- AOP는 생각보다 아주 다양한 곳에 적용할 수 있다.
- 생성자, 필드 값, static 메소드, 메소드 실행 지점 등등…
- 다만 다양한 곳에 적용이 가능한 것은 AspectJ를 직접 사용한 경우에만 해당한다.
- 프록시를 사용하는 스프링 AOP는 메소드 실행 지점에만 AOP를 적용할 수 있다.
- 또한, 스프링 AOP는 스프링 컨테이너가 관리할 수 있는 스프링 빈에만 AOP를 적용할 수 있다.
- 실무에서는 스프링이 제공하는 AOP 기능만 사용해도 대부분의 문제를 해결할 수 있다.
AOP 용어 정리
- 조인 포인트 (Join point)
- 어드바이스가 적용될 수 있는 위치
- 메소드 실행, 생성자 호출, 필드 값 접근, static 메서드 접근 같은 프로그램 실행 중 지점
- AOP를 적용할 수 있는 모든 지점을 의미한다.
- 조인 포인트는 별도의 기능이 아닌 추상적인 개념이다.
- 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메소드 실행 지점으로 제한된다.
- 어드바이스가 적용될 수 있는 위치
- 포인트컷 (Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 주로 AspectJ 표현식을 사용해서 지정한다.
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별할 수 있다.
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 타켓 (Target)
- 어드바이스를 받는 객체
- 포인트컷으로 어드바이스가 적용될 지 정해진다.
- 어드바이스 (Advice)
- 부가 기능
- 특정 조인 포인트에서 Aspect에 의해 취해지는 조치를 의미한다.
- Around(주변), Before(전), After(후)와 같은 다양한 종류의 어드바이스가 있다.
- 애스펙트 (Aspect)
- 어드바이스와 포인트컷을 모듈화 한 것이다.
- @Aspect가 그 예시다.
- 여러 어드바이스와 포인트 컷이 함께 존재한다.
- 어드바이스와 포인트컷을 모듈화 한 것이다.
- 어드바이저 (Advisor)
- 하나의 어드바이스와 하나의 포인트 컷으로 구성된 것
- 스프링 AOP에서만 사용되는 특별한 용어다.
- 위빙 (Weaving)
- 포인트컷으로 결정한 타켓의 조인 포인트에 어드바이스를 적용하는 것
- 위빙을 통해 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가 할 수 있다.
- AOP 적용을 위해 애스펙트를 객체에 연결한 상태를 의미한다.
- 종류
- 컴파일 타임 위빙 (
.java
파일을.class
파일로 변환하는 중에 적용, AspectJ 컴파일러 사용) - 로드 타임 위빙 (
.class
파일을 JVM에 저장하기 전에 적용) - 런타임 위빙 (자바가 실행되고 난 다음에 적용)
- 컴파일 타임 위빙 (
- 스프링 AOP는 런타임 위빙인 프록시 방식이다.
- AOP 프록시
- AOP 기능을 구현하기 위해 만든 프록시 객체
- 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시다.