본문은 인프런의 [스프링 핵심 원리 - 고급편]를 수강하고 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
스프링 핵심 원리 - 고급편
1. 쓰레드 로컬
2. 템플릿 메서드 패턴과 콜백 패턴
3. 프록시 패턴과 데코레이터 패턴
4. 동적 프록시 기술
5. 스프링이 지원하는 프록시
6. 빈 후처리기
7. @Aspect AOP
8. 스프링 AOP 개념
9. 스프링 AOP 구현
10. 포인트컷
11. 실무 주의사항
1. AOP 소개
1) 핵심 기능과 부가 기능
애플리케이션 로직은 크게 핵심 기능과 부가 기능으로 나눌 수 있다.
- 핵심 기능
- 해당 객체가 제공하는 고유의 기능
- ex) 주문로직, 결제로직
- 부가 기능
- 핵심 기능을 보조하기 위해 제공되는 기능
- ex) 로그추적, 트랜잭션
부가 기능은 대체적으로 여러 클래스에 걸쳐서 함께 사용된다. → 횡단 관심사(cross-cutting concerns)
그러나 부가 기능을 여러 곳에 적용하는 것은 쉽지 않다. 다음과 같은 문제가 발생할 수 있다. (부가 기능 적용의 문제점)
- 부가 기능을 적용할 때 아주 많은 반복이 발생한다
- 부가 기능이 여러 곳에 퍼져서 중복 코드가 발생한다
- 부가 기능을 변경할 때 중복 때문에 많은 수정이 필요하다
- 부가 기능의 적용 대상을 변경할 때 대상이 많으므로 많은 수정이 필요하다
2) 애스펙트
이러한 부가 기능 적용의 문제점을 해결하기 위해 애스펙트라는 개념이 도입됐다. 애스펙트는 부가 기능 자체와, 해당 부가 기능을 어디에 적용할지를 정의한 것이다. 바로 전 챕터에서 알아본 @Aspect와 스프링이 제공하는 어드바이저(부가 기능 + 적용 대상)이 이에 해당한다.
- 애스펙트 = 부가 기능 + 적용대상
- ex) @Aspect, Advisor(Advice+Pointcut)
애스펙트는 관점이라는 의미로서, 애플리케이션을 바라보는 관점을 횡단 관심사 관점으로 보는 것이다. 이렇게 애스펙트를 사용한 프로그래밍 방식을 관점 지향 프로그래밍, 즉 AOP(Aspect-Oriented Programming)이라고 한다.
AOP의 대표적인 구현엔 AspectJ 프레임워크가 있다. 스프링 또한 AOP를 지원하지만, AspectJ가 제공하는 기능의 일부만 제공한다는 한계가 있으며 대부분 AspectJ의 문법을 차용한다는 특징이 있다.
- 앞서 배웠던 @Aspect, Advisor가 바로 스프링에서 제공하는 AOP 관련 기능이다
2. AOP 적용 방식
1) AOP 적용 방식
AOP를 사용할 때 부가 기능 로직은 크게 3가지 방법으로 실제 로직에 추가된다.
- 컴파일 시점
- 클래스 로딩 시점
- 런타임 시점(프록시)
컴파일 시점
AspectJ 컴파일러를 사용해서 소스 코드(.java)로 바이트 코드(.class)를 만드는 시점에 부가 기능 로직을 추가할 수 있다. AspectJ 컴파일러는 Aspect를 확인해서 해당 클래스가 적용 대상인지 확인하고 만약 적용 대상이라면 부가 기능 로직을 적용한다. 다만, 별도의 컴파일러가 필요하고 컴파일 시점에 부가 기능을 적용하는 것이 다소 복잡하다는 점 때문에 실제론 잘 사용하지 않는다.
- 부가 기능 코드가 핵심 기능이 있는 코드 주변에 실제로 붙어 버린다
- 이렇게 원본 로직에 부가 기능 로직이 추가되는 것을 위빙(Weaving)이라고 한다
- 위빙(Weaving): 옷감을 짜다, 직조하다 -> 애스펙트와 실제 코드를 연결해서 붙이는 것
클래스 로딩 시점
AspectJ 클래스 로더 조작기를 이용해서 생성된 바이트 코드를 조작한 후에 JVM에 올릴 수 있다. 이 시점에 애스펙트를 적용하는 것을 로드 타임 위빙이라고 한다. 실제로 클래스 로더 조작기를 지정하는 부분이 번거롭고 운영하기 어려워서 이 또한 잘 사용하지 않는다.
- 자바 언어는 바이트코드를 JVM에 저장하기 전에 조작할 수 있는 기능을 제공하는데, 이를 Instrumentation API라고 한다
- 많은 모니터링 툴들이 이 방식을 사용한다 (참고 링크)
런타임 시점
런타임 시점에 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있다. 이것이 지금까지 학습해온 프록시 방식의 AOP이다. 위 두 가지 방법에 비해 기능에 일부 제약이 있다는 단점이 있지만, 별도 컴파일러/클래스 로더 조작기 및 옵션을 설정하지 않아도 된다는 점에서 편리하다는 장점이 있다.
- 런타임은 컴파일 후 클래스 로더에 클래스가 다 올라가서 자바가 실행된 시점이기 때문에 자바 언어가 제공하는 범위 안에서 부가 기능을 제공해야 한다
- 이러한 이유로 기능에 제한이 있다. 자세한 내용은 아래에서 다룰 것이다.
정리
1) 컴파일 시점
- AspectJ 컴파일러 사용
- 실제 대상 코드에 애스펙트를 통한 부가 기능 호출 코드가 포함됨
- Aspect를 직접 사용해야 함
2) 클래스 로딩 시점
- AspectJ 클래스 로더 조작기 사용
- 실제 대상 코드에 애스펙트를 통한 부가 기능 호출 코드가 포함됨
- Aspect를 직접 사용해야 함
3) 런타임 시점
- 프록시 사용 (스프링 AOP 방식)
- 실제 대상 코드 그대로 유지
2) AOP 적용 위치
AspectJ를 사용해서 컴파일 시점과 클래스 로딩 시점에 적용하는 AOP는 실제 바이트코드를 조작하므로 모든 지점에서 기능을 사용할 수 있다. 모든 지점이라 함은 생성자, 필드 값 접근, static 메서드 접근, 메서드 실행을 모두 일컫는다.
- 이렇게 AOP를 적용할 수 있는 지점을 조인 포인트(Joint Point)라고 한다.
그러나 프록시 방식을 사용하는 스프링 AOP는 메서드 실행 지점에만 AOP를 적용할 수 있다. 이는 프록시가 메서드 오버라이딩을 통해서 동작하기 때문이다. 따라서 생성자, 필드 값 접근, static 메서드 접근 시엔 프록시 개념이 적용될 수 없다. 또한, 스프링 AOP는 스프링 컨테이너가 관리할 수 있는 스프링 빈에만 AOP를 적용할 수 있다.
- 프록시를 사용하는 스프링 AOP의 조인 포인트는 메서드 실행으로 제한된다
- 프록시는 자신의 메서드가 호출되는 시점에 실제 호출할 대상, 메서드, 인수 정보 등을 알게 되기 때문이다
AspectJ를 직접 사용하는 편의 기능은 더 다양하지만, 러닝커브가 높고 설정방법이 상당히 복잡하므로 스프링 AOP 제공하는 기능을 사용하면 된다. 실무에서는 스프링이 제공하는 AOP 기능만 사용해도 충분하다.
3. AOP 용어 정리
1) 조인 포인트(Join Point)
- 어드바이스가 적용될 수 있는 위치
- 메서드 실행, 생성자 호출, 필드 값 접근, static 메서드 접근
- AOP를 적용할 수 있는 모든 지점 (추상적인 개념)
- 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메서드 실행 지점으로 제한됨
2) 포인트컷(Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 주로 Aspect 표현식을 사용해서 지정
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능
3) 타겟(Target)
- 어드바이스를 받는 객체, 포인트컷으로 결정
4) 어드바이스(Advice)
- 부가 기능
- 특정 조인 포인트에서 Aspect에 의해 취해지는 조치 (= 부가 기능이란 뜻)
- Around(주변), Before(전), After(후)와 같은 다양한 종류의 어드바이스가 있음
5) 애스펙트(Aspect)
- 어드바이스 + 포인트컷을 모듈화 한 것
- @Aspect를 생각하면 됨
- 여러 어드바이스와 포인트 컷이 함께 존재
6) 어드바이저(Advisor)
- 하나의 어드바이스와 하나의 포인트 컷으로 구성된 특별한 애스펙트
- 스프링 AOP에서만 사용되는 특별한 용어
7) 위빙(Weaving)
- 포인트컷으로 결정한 타겟의 조인 포인트에 어드바이스를 적용하는 것
- 위빙을 통해 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가할 수 있음
- AOP 적용을 위해 애스펙트를 객체에 연결한 상태
- 컴파일 타임
- 로드 타임
- 런타임 (=프록시 방식, 스프링 AOP가 사용하는 방식)
8) AOP 프록시
- AOP 기능을 구현하기 위해 만든 프록시 객체, 스프링에서 AOP 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다
'Spring > 스프링 핵심 원리 - 고급편' 카테고리의 다른 글
[스프링 핵심 원리 - 고급편] 포인트컷 (0) | 2023.03.16 |
---|---|
[스프링 핵심 원리 - 고급편] 스프링 AOP 구현 (0) | 2023.03.08 |
[스프링 핵심 원리 - 고급편] @Aspect AOP (0) | 2023.03.07 |
[스프링 핵심 원리 - 고급편] 빈 후처리기 (0) | 2023.03.06 |
[스프링 핵심 원리 - 고급편] 스프링이 지원하는 프록시 (0) | 2023.03.06 |