dev/백엔드

gdg on campus ewha - Spring WIL 4주차

wosrn 2024. 10. 6. 11:19

관점지향 프로그래밍과  커스텀어노테이션

 

 

< 관점지향 프로그래밍 >

- AOP : 핵심기능과 부가기능 분리하자

- 애플리케이션  = 부가기능(중복되어 코드 전반에 흩어져있다) + 핵심기능 / 핵심기능 : 핵심 비즈니스 로직

- 부가기능과 적용할 위치를 합쳐 ASPECT로 모듈화

- 핵심 비즈니스 로직과 Aspect가 합쳐져서 애플리케이션이 실행됨

- AOP : 횡단 관심사를 핵심 비즈니스 로직에서 분리하여 모듈성 향상시키는 프로그래밍 패러다임 / OOP 보완 / Aspect라는 특별한 객체로 모듈화

- Aspect : 부가기능을 모듈화한 것 - Advice(what,when) + Pointcut(where)

- Tartet : aspect가 적용될 객체(클래스,메소드..)

- Advice : aspect가 특정 조인포인트에서 해야하는 부가기능

- Joinpoint : advice가 적용될 수 있는 시점 (ex.메소드 진입 시점)

- Pointcut  : 어떤 조인포인트에 어드바이스를 적용할 지 결정하는 기준 - 어드바이스가 적용되지 않는 조인포인트도 있다 

- Weaving : 포인트컷에 의해 결정된 타겟의 조인포인트에 부가기능(Advice)를 삽입하는 과정 - 스프링 aop에선 런타임위빙을 사용

 

- Spring AOP : 간단한 AOP기능만을 제공 - 메소드레벨만 지원(조인포인트), 위빙은 런타임시에만 가능, 스프링 컨테이너가 관리하는 빈만이 대상

 

- 프록시 : 스프링 AOP는 프록시 기반으로 설계됨 : 스프링은 기존 코드를 수정하지 않고 추가적인 동작을 더하기 위해 프록시 사용

- 프록시란? 대리자 - 원래 객체를 감싸고 있는 객체 - 기존 객체의 인터페이스를 유지하고 추가적인 동작을 수행 , 접근을 제어하고 싶거나 부가기능을 추가하고 싶을 때 사용(오브젝트보다 프록시에 먼저 접근하게되는 구조, 나갈때도 프록시를 거쳐 나가게됨)

- 스프링에선 클라이언트가 객체 사용할 때, 대상 객체 대신 이 객체를 감싼 프록시객체를 제공

- 프록시는 타겟객체에 대한 호출을 가로채서 Advice의 부가기능 수행 후 Target의 핵심기능 호출 (before Advice의 경우)

 

- Aspect 우선순위 : 동일한 조인포인트에 여러개의 Advide가 적용된다면 원하는 순서대로 되지 않을 수 있음 -> 따라서 Aspect들 간의 우선순위를 지정해줘야 -> @Order 어노테이션 방식, Ordered 인터페이스 방식

우선순위 높을수록 더 바깥의 프록시 객체가 되어 더 먼저 실행됨

 

 

<  커스텀어노테이션 >

어노테이션? 코드 사이에 주석처럼 쓰이면서 특별한 기능,의미를 더해주는 기술 / 프로그램에게 추가적인 정보를 제공해주는 메타데이터

스프링에선 원하는 어노테이션 만들어서 쓸 수 있음

용도 : 컴파일러에게 코드작성문법 에러를 체크하도록 정보제공, 런타임 시 특정 기능 실행하도록 정보제공 등 (스프링에서 제공되는 대부분 어노테이션은 후자를 위해 사용되고 있음)

: @interface 타입으로 정의 -> 메타 어노테이션 추가(다른 어노테이션에 적용되기 위한 어노테이션) -

@Target 지정 : 어노테이션 적용 대상

@Retention 지정 : 어노테이션 보존 범위 (ex. 컴파일 전까지만 유효, 런타임 시에도 참조 가능 등)

@Documented : javadoc 포함여부

@Inherted : 상속여부

 

커스텀 어노테이션 기반 AOP : aop dependency 추가  어노테이션 만들기 - aspect 생성(어노테이션 사용 등) - 포인트컷, 어드바이스 생성

 

<과제> - 커스텀 어노테이션과 AOP를 활용한 성능 모니터링 관점 구현 : 성능 모니터링을 위한 AOP클래스 작성

 

 

우선 이렇게 패키지와 파일을 추가했다 : AOP 관련코드는 aspect패키지 안에, 어노테이션은 annotations패키지에

 

 

그 후 커스텀 어노테이션을 정의한다. 위의 경우 어노테이션 적용 대상은 메서드, 보존범위는 런타임시기까지이다

 

다음으로는 AOP를 활용하여 성능 모니터링 클래스를 구현한다. aspect를 생성하고, 포인트컷과 어드바이스를 생성한다

- 포인트컷 : AOP에서 어떤 메소드에 어드바이스를 적용할지를 결정하는 표현식 - 이 코드는 메소드에 적용된 @PerformanceMonitor 어노테이션을 기준으로 성능 모니터링을 적용 ( @Around("@annotation(PerformanceMonitor)")는 @PerformanceMonitor 어노테이션이 적용된 메소드에만 AOP 로직을 적용하겠다는 것을 의미 )

- 어드바이스 : 포인트컷이 가리키는 메소드에 실행될 추가 로직 -> @Around 어노테이션을 사용하여 메소드의 실행 전후로 성능 모니터링 로직을 적용 -> joinPoint.proceed()가대상 메소드를 실제로 실행하는 부분, 메소드 호출 전에 startTime을 기록하고, 메소드가 끝난 후 endTime을 기록하여 실행 시간을 계산

=> proceed() 메소드? proceed() 메소드는 AOP에서 사용되는 ProceedingJoinPoint 인터페이스의 메소드 ( AOP 어드바이스가 적용된 메소드를 실제로 실행하기 위한 메소드 )

 

 

다음으론 성능측정의 대상이 될 대상 메소드를 만든다

 

그리고 이를 테스트할 실행클래스를 만든다

 

**이 때 HelloApplication이 ExampleService 보다 상위 혹은 같은 경로상에 있어야한다

나의 경우 위처럼 했다가 에러가 나서(어플리케이션보다 sevice가 상위에 위치)

이렇게 수정해주었더니 정상적으로 작동되었다 ( @SpringBootApplication이 있는 패키지가 루트 패키지로 설정되어야 하며, 하위 패키지에서 빈이 자동으로 등록되기 때문 )

 

그러나 이후에도 문제가 있었는데, 테스트는 돌아가지만 aspect에서 코딩한 

logger.info("[PERFORMANCE MONITOR] Method " + joinPoint.getSignature() + " executed in " + executionTime + " ms");

이 부분이 테스트를 실행해서 성능측정 대상인 메소드가 실행되어도 출력되지가 않았다

여러 해결법을 써보다가 결국 성공한 것은 

 

이렇게 aspect의 @Around 안의 매개변수를 annotations.PerformanceMoniter로 바꿔서 해결되었다 (원래는 그냥 PerformanceMoniter였다) 

나의 경우 퍼포먼스모니터가 annotations 패키지 안에 있는데, 이 경우 AOP 표현식에서 해당 패키지를 포함해야 한다고 한다

 

이 사진에서 볼 수 있듯 Method의 실행시간이 측정되는 것을 볼 수 있다

'dev > 백엔드' 카테고리의 다른 글

스프링 스터디 4주차  (1) 2024.11.10
스프링 스터디 3주차  (0) 2024.11.03
스프링 스터디 2주차  (0) 2024.10.05
스프링 스터디 1주차  (0) 2024.10.04
gdg on campus ewha - Spring WIL 2주차  (0) 2024.09.24