일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Collection
- cloud
- post
- DevOps
- Set
- container
- sonarQube
- jdk
- LAN어댑터
- 액세스회선
- Linux
- STREAM
- 라우터
- 캐시서버
- 방화벽
- JPA
- AOP
- map
- Spring
- gradle
- 소켓
- ansible
- Java
- mybatis
- tomcat
- Pipeline
- IntelliJ
- Jenkins
- docker
- 허브
- Today
- Total
거북이-https://velog.io/@violet_evgadn 이전완료
AOP 본문
AOP란?
◎ 스프링 삼각형
위 그림에 나오는 삼각형을 "스프링 삼각형"이라고 한다.
이는 스프링의 3대 요소를 나타낸 것으로써 DI, AOP, PSA 3가지로 이루어지고 그 기반에는 POJO기반으로 프로젝트가 구성되어야 함을 말해 준다.
DI는 이전까지 IoC와 함께 지겨울 정도로 설명했다.
PSA(Portable Service Abstraction)는 환경이나 세부 기술 변화에 관계없이 일관된 방식으로 기술에 접근할 수 있게 만들어 준다는 것이다.
Spring은 어댑터 패턴을 활용해 같은 작업을 수행하는 다수의 기술을 공통된 인터페이스를 통해 제어할 수 있게 해준다. 어댑터 패턴은 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 인터페이스로 변환하는 패턴으로써 인터페이스 호환성 문제 때문에 활용할 수 없는 클래스들을 연결해서 사용할 수 있다. Spring도 어댑터 패턴을 활용하여 PSA를 가지므로 원래 클래스와는 동작 방식 등에 조금씩 차이가 존재할 수 있지만 환경에 관계없이 기능을 수행할 수 있게 해주는 것이다.
PSA는 Spring POJO와 관계가 깊은데 결국 POJO로 개발된 코드는 특정 환경이나 구현 방식에 종속적이지 않아야 한다는 의미이다. 이를 위해 Spring에서는 Spring 이외의 패키지들을 POJO화 시키기 위해 껍데기를 씌워 개발에 활용한다.
남은 것은 AOP이다. 개인적으로는 이 AOP가 Spring의 꽃이 아닐까 생각한다. 그렇다면 Spring 기초 이론 설명을 화려해 장식할 AOP에 대해서 알아보자.
◎ AOP 정의
AOP는 Aspect Oriented Programming의 약자로써 관점 지향 프로그래밍이라고 부른다.
AOP를 알기 위해선 "관점 지향"이라는 단어의 의미를 이해하는 것이 우선이다.
관점 지향이라는 것은 특정 로직을 핵심적인 관점과 부가적인 관점으로 나누어 보고 각각을 모듈화 하는 것을 말한다. 즉, AOP는 프로그램을 핵심기능과 부가 기능으로 나누어서 설계 및 구현하는 것을 일컫는다.
여기서 OOP와 AOP를 헷갈릴 수 있는데, 간단히 말하자면 OOP는 객체의 관점에서 프로그래밍을 진행한다는 것으로써 객체를 유기적으로 연결하여 프로그래밍한다는, 어떻게 보면 "핵심 로직 구현"에 신경을 쓴 개념이라면 AOP는 "부가 로직"과 "핵심 로직"을 구분하여 "부가 로직"을 Aspect를 통해 모듈화 하는, 즉 "부가 로직 구현"에 신경을 쓴 개념인 것이다.
AOP에서는 코드를 부분적으로 쪼개서 모듈화 하는데, 모듈화 한 것 중에서는 다른 부분(메서드)에서도 반복해서 활용되는 코드가 있을 것이다. 이렇게 반복적으로 활용되는 코드를 흩어진 관심사(Crossing Concerns)라고 부른다. AOP는 이런 흩어진 관심사를 Aspect로 모듈화 하여 핵심적인 기능과 분리한 이후 모듈화하고 재사용함으로써 개발을 할 때 핵심 로직에만 집중할 수 있게 만들어준다.
결국 AOP에서는 핵심 기능과 공통 기능을 분리시켜 개발하고 공통 기능을 핵심 기능에 (블록처럼) 끼워 넣는 개발 형태를 가진다. 이렇게 개발함으로써 중복되는 코드를 제거할 수 있어 개발 효율성이 크게 증대되고 개발의 가독성도 크게 증가할 것이다. 공통 기능을 한 곳에 보관하기 때문에 공통 기능의 수정이 필요할 경우 한꺼번에 수정이 가능하여 유지보수가 편해지며 재활용성이 극대화된다는 장점을 가진다.
먼저 위 사진을 통해 이해해보자. 우리는 Class A, B, C를 만들 것이다. 원래라면 우리는 Class A, B, C를 각각 코딩하여 3개의 클래스를 만들어야 했을 것이다.
그런데 Class A, B, C를 보니 공통되는 부분이 보이는 것이다. 개발자는 생각했다. 이렇게 공통된 부분을 한 번만 만들어서 처리하면 더욱 편하지 않을까?
그래서 노란색을 Aspect X로, 파란색을 Aspect Y로, 빨간색을 Aspect Z로 모듈화 시킨 뒤 각각의 Class에 모듈화 된 Aspect를 전달해주는 것이다. 그렇다면 클래스 측에서 설정값에 따라 Aspect를 제 위치에 갖다 놓기만 하면 Class A, B, C가 구현되는 것이다.
또 다른 예시를 들어보자.
현실에서 활용하는 애플리케이션은 로그를 남겨 놓는 것이 핵심이다. 특히 에러 로그와 소요 시간에 대한 로그는 필수적일 것이다. 모든 메서드에서 이 기능을 필요로 하기 때문에 AOP를 활용하지 않는다면 우리는 해당 로직에 대한 코드를 복사 + 붙여 넣기 해야 할 것이다.
이렇게 개발할 경우 메서드를 추가할 때마다 공통 로직을 복사 + 붙여 넣기 해야 할 텐데, 너무나 많은 시간이 소요되지 않을까? 원래 존재하는 메서드들에 새로운 공통 로직을 추가해야 할 경우 존재하는 모든 메서드에 복사+붙여 넣기를 통해 새로운 공통 로직을 집어넣을 수 있을까? 그리고 중간에 규정이 바뀌어서 시간을 ms 단위로 쟀었는데 s 단위로 재는 것으로 바꿔야 한다면 해당 로직을 일일이 찾아 로직을 바꿔줘야 할까?
사실상 불가능하다.
그리고 이런 문제를 깡그리 해결해 줄 수 있는 게 AOP인 것이다.
에러 로그와 소요 시간에 대한 로그를 Aspect로써 모듈화 시키고 메서드에 모듈화 시킨 Aspect를 적용만 시키면 되는 것이다. 만약 새로운 공통 로직이 생긴다면 Aspect를 추가하여 적용시키기만 하면 되는 것이다. 또한 Aspect 로직이 수정되어야 할 경우 해당 Aspect 로직만 수정해주면 해당 Aspect를 활용한 모든 메서드에 알아서 변경된 로직이 적용될 것이다.
즉 AOP는 공통된 로직과 핵심 로직을 분리하여 개발하고, 공통된 로직은 모듈화 시켜 적용함으로써 핵심 로직에만 주의를 기울여 개발할 수 있게 도와주는 것이다.
AOP 특징
◎ AOP 관련 용어
Target
- 부가 기능(공통 로직)을 부여할 대상
- 핵심 기능을 담당하고 있음
Advice
- 실질적으로 어떻게 부가 기능을 수행해야 하는지 담은 구현체
- Advice는 어떤 부가 기능을 어떤 방식으로 구현할 것인지 코드를 통해 담아 놓은 부가 기능 코드라고 보면 됨
JoinPoint
- Advice가 적용될 위치
- AOP 프레임워크에서는 "생성자 호출 시점", "필드에서 꺼내올 시점", "메서드 실행 시점" 등 다양한 시점에서 Advice를 적용할 수 있도록 설정해 놓았다
- Spring에서는 "메서드 실행 시점"에서만 Advice가 적용되게 설정되어 있다
PointCut
- JoinPoint의 상세한 스펙을 정의한 것
- JoinPoint 중 실제로 부가 기능을 삽입할 위치를 정의한 모듈
- "어떤 메서드" 실행 시점에 Advice를 호출할 것인지 정함
- Advice 코드를 끼워 넣을 수만 있다면 JoinPoint가 되며, 여기에서 추가하고 싶은 Advice를 "실제로 넣을 장소"가 Pointcut이 된다. 즉 JoinPoints는 Pointcut의 후보가 되는 것이다
Aspect
- 흩어진 관심사를 모듈화 한 것
- Advice + Pointcut
- Aspect의 동작 방식 : Pointcut을 통해 부가 기능을 추가시킬 장소를 찾아가 Advice에 구현된 부가 기능 로직을 수행시킴
- "어떤 부가 로직을" "어떤 위치에" 배치시킬지 정해 놓은 모듈이다
Proxy
- Target을 감싸 Target으로 들어오는 요청을 대신 받아주는 Wrapping 객체
- Client가 Target을 호출하면 Target을 감싸는 Proxy가 호출되어 Target 메서드를 실행하기 전 Advice를 처리하고 Target 메서드를 실행한 이후 후처리를 하도록 구성되어 있음
◎ AOP 적용 방법
총 3가지 적용법이 존재한다.
- 컴파일 시점에 Byte Code를 조작하여 처음부터 AOP가 적용된 바이트 코드를 생성하는 Compile Time 적용법
- 컴파일 시점에는 AOP를 적용하지 않고 클래스가 로딩되는 시점에서 클래스 자체 정보를 변경하여 활용하는 Load Time 적용법
- Spring Bean을 생성할 때 Bean을 감싸는 Proxy Bean을 만든 이후 Proxy Bean에 Aspect를 추가하여 Proxy에 요청이 도착할 경우 Aspect 로직을 수행한 이후 핵심 로직을 수행하는 Runtime 적용법
Spring AOP는 주로 Runtiem 적용법을 활용한다
◎ Spring AOP
스프링 AOP는 Proxy 패턴을 활용한다.
스프링 AOP 동작 방식에 대해 배우기 전 프록시 패턴에 대해 알아보자
Proxy를 번역하면 대리자, 대변인의 의미를 가진다. 이를 프로그램에 적용해도 같다. 프록시는 결국 "특정 일을 대신해주는 것"이라고 이해하면 된다.
원래 객체를 Proxy라는 특수한 객체로 감싸서 만들기 때문에 빈번한 객체 생성이 필요할 경우 매우 비효율적이지만 Spring Bean은 모든 Bean들이 싱글톤 패턴을 활용하기 때문에 1번의 객체 생성만 발생한다. 즉, 이런 단점에 영향을 받지 않기 때문에 로컬과 떨어져 있는 객체를 활용할 수 있다는 프록시 객체의 특수성을 잘 활용할 수 있게 되는 것이다.
(여기서 로컬은 핵심 로직, 떨어져 있는 객체는 부가 로직, 즉 Aspect가 될 것이다)
Spring AOP는 Runtime 적용법에서 설명했듯 Spring Bean을 감싸는 Proxy Bean을 생성하고 Proxy에 Aspect를 추가함으로써 Proxy에 요청이 도착할 경우 Aspect 로직을 먼저 처리한 이후 핵심 로직이 처리되는 과정을 거친다고 하였다.
즉, Spring AOP는 Spring Bean에 등록되는 것이 우선시되어야 하며, 이에 따라 Spring AOP는 Spring Bean에만 적용할 수 있다.
이렇게 Proxy 패턴을 활용해 AOP가 구현되어 있으므로 핵심 로직을 순수 자바(POJO) 기반으로 구성할 수 있으며, Compile 과정에 특별한 추가 로직이 필요하지 않게 되는 것이다.
그렇다면 AOP가 적용된 이후 프로그램의 흐름에 대해서 알아보자.
먼저 서블릿을 간단히 설명하자면 자바 웹 페이지를 제공할 때 동적인 데이터 제공을 것을 도와주는 것으로써, 일단 지금은 HTTP 요청과 응답을 받는 객체 정도로만 이해하자(나중에 서블릿에 대해 설명할 기회가 있을 것이다)
먼저 Servlet이 Client 측에서 온 요청을 받을 것이다(위 시퀀스 다이어그램에선 POST 요청을 받게 되었다)
만약 AOP를 적용하지 않았다면 이 요청을 바로 Controller에 전달하여 핵심 로직을 실행시켰을 것이다.
하지만 AOP가 적용된 지금은 핵심 로직이 담긴 Controller는 Proxy에 감싸져 있다. 즉, Proxy가 이 요청을 먼저 받게 된다.
이후에 Proxy에 저장된 Aspect를 보면서 "어떤 부가 로직이" "언제" 수행되는지를 판별하게 된다.
@Around 어노테이션은 메서드 실행 전후 Advice를 실행시키라는 어노테이션인데 이는 Aspect 실행 시점을 정해주는 어노테이션이므로 일단은 깊게 생각하지 말고 일단은 @Around Advice는 그냥 "Advice", 즉 부가 기능에 대한 코드라고만 생각하자.
Proxy는 서블릿으로부터 온 요청을 받아 Advice에 요청이 왔다는 것을 전달해준다. 그렇다면 Advice는 자신(부가 로직)이 실행될 차례라는 것을 이해하고 부가 로직을 실행시킨다.
이렇게 실행시킨 부가 로직이 완료되었다면 Controller(핵심 로직)에 "부가 로직의 수행이 완료되었다!"라고 알린다.
그렇다면 그제야 Controller(핵심 로직)은 자신에게 요청이 왔다는 것을 인지하고 핵심 로직을 실행하게 되는 것이다.
이후 Respond 값(Request에 대한 핵심 로직 결괏값. 이후로는 Result라고 쓰겠다)이 생성되었다면 Controller는 이를 바로 서블릿으로 보내지 않는다.
요청이 왔던 정확히 역순으로 Result를 보내게 되는데, 먼저 Advice에게 Result를 보낸다.
부가 기능 중 Result를 활용하는 로직이 있을 수도 있고 핵심 로직이 끝난 이후 실행되어야 하는 로직(ex. 실행 시간 로그 남기기)이 포함되어 있을 수도 있기 때문이다.
이렇게 Advice(부가 기능)에 대한 로직이 모두 수행되었다면 Proxy에게 부가 로직 및 핵심 로직이 끝났음을 알리고 Result 값 또한 전달해준다. Proxy는 이때 "핵심 로직과 부가 로직 모두가 종료" 되었음을 알게 되며, Spring Bean을 저장소에 반환하며 동시에 결괏값을 서블릿에 전달하게 될 것이다. 마지막으로 Result 값을 받은 서블릿은 Client에게 HTTP Respond를 통해 전달해 줄 것이다.
◎ Aspect 실행 시점을 지정하는 어노테이션
Aspect는 Advice와 Pointcut을 통해 "어떤 메서드"에 "어떤 부가 로직"을 구현할지 담고 있는데, Advice를 담을 때 이 부가 로직이 Target의 "어느 시점에서" 실행되어야 하는지도 담고 있다.
Advice는 어노테이션을 통해 부가 로직의 실행 시점을 지정해주는데, 이와 관련된 어노테이션은 아래와 같다.
- @Before : "핵심 기능" 호출 전에 Advice 기능을 수행
- @After : "핵심 기능" 호출 이후에 Advice 기능을 수행하는데, 수행의 성공 여부는 영향을 끼치지 않는다.
- @AfterReturning : "핵심 기능"이 성공적으로 완료되었을 때, 완료 이후에 Advice 기능이 수행된다.
- @AfterThrowing : "핵심 기능"이 실패하였을 때(즉, 예외가 발생했을 때) Advice 기능이 수행된다.
- @Around : @Before + @After. "핵심 기능" 호출 전과 후 모든 상황에 Advice 기능을 수행하는데 Advice가 Target을 감싸서 Target 로직 실행 이전과 이후에 Advice 기능이 수행되도록 하는 것이다.
요약
Spring의 3대 요소를 스프링 삼각형으로 표현할 수 있는데, POJO를 기반으로 DI, AOP, PSA 3가지로 프로젝트가 구성되어야 함을 말한다.
이 중 AOP는 "관점 지향 프로그래밍"의 약자로써 부가 기능과 핵심 기능을 나눠 설계 및 구현하는 것을 말한다.
부가 기능은 여러 모듈에서 반복되는 기능들이 많은데, 이렇게 반복되는 기능을 "흩어진 관심사"라고 하며 AOP는 흩어진 관심사를 Aspect로 모듈화 하여 재사용함으로써 핵심 로직 구현에만 집중할 수 있게 하는 프로그래밍을 말한다.
AOP를 활용하면 반복되는 코드를 줄여 개발 효율성 증대 및 가독성이 커지고, 공통 기능을 한 번에 관리하므로 관리가 쉬워지고 수정이 편리해져 유지보수성이 커짐과 동시에 재활용성도 커진다
AOP는 핵심 로직을 담고 있는 동시에 부가 기능을 적용시킬 Target, 부가 기능에 대한 코드가 들어 있는 Advice, Advice를 적용할 수 있는 JoinPoint와 실제로 Advice를 적용한 Pointcut, 그리고 Advice와 Pointcut을 합쳐 "어떤 부가 로직을 어떤 위치에 배치시킬 것인가"를 모듈화 시킨 Aspect로 이루어진다.
AOP는 컴파일 시점에 AOP를 적용하는 Compile Time, 클래스가 로드되는 시점에서 클래스 자체를 변경하여 AOP를 적용하는 Load Time, 그리고 Spring AOP에서 주로 활용하는 Proxy를 사용해 Proxy에 요청이 들어오면 Aspect 로직을 먼저 실행시키고 핵심 로직을 순차적으로 수행시키는 Runtime 적용법이 존재한다.
Spring은 Bean을 Proxy에 담아 Proxy에 요청이 들어오면 선행적으로 Advice를 실행시키고, 이후에 핵심 로직을 수행시키면서 Proxy에서 부가 기능과 핵심 로직에 대한 흐름을 관리하므로 꼭 Spring Bean에 등록되어야지만 AOP로 활용할 수 있다.
Advice를 실행시키는 시점은 어노테이션을 통해 적용할 수 있는데, 메서드 실행 이전인 @Before, 메서드 실행 이후인 @After, 어드바이스가 Target 메서드를 모두 감싸 메소드 호출 전과 이후 Advice 기능을 수행하는 @Around, 정상적 반환 이후인 @AfterReturning 및 예외 발생 이후 @AfterThrowing에 Advice를 위치시킬 수 있다.
'웹 개발 > Spring(이론)' 카테고리의 다른 글
DTO 클래스 (0) | 2022.09.19 |
---|---|
Spring의 데이터 처리 방법 (0) | 2022.09.19 |
IoC와 DI (0) | 2022.08.03 |
스프링 컨테이너와 스프링 빈 (0) | 2022.08.03 |
Spring (0) | 2022.08.02 |