[Spring]AOP의 개념 및 적용 예제(공통관심사 처리하기)
1. AOP가 필요한 상황?
예를 들어 당신이 프로젝트를 개발하고 있는데
상사가 와서 성능테스트를 하고싶으니 모든 메서드 로직 수행 시간을 로그로 찍어보라고 요청했다고 가정해보자.
일반적으로 이 이야기를 들으면 엄청난 노가다할 생각에 당황할 것이다.
아마도 다음과 같이 코드를 짜기시작할 수도 있을것 같다.
public void joinUser(UserVO vo){
long start = System.currentTimeMillis();
vo.setRole("USER");
userRepository.save(vo);
long end = System.currentTimeMillis();
System.out.println("걸린시간 = " + (end-start));
}
public UserVO loginUser(String id,String pw){
long start = System.currentTimeMillis();
UserVO userVO=userRepository.selectUserInfo(id,pw);
long end = System.currentTimeMillis();
System.out.println("걸린시간 = " + (end-start));
return userVO;
}
이렇게 모든 메서드들에 걸린 시간을 측정하는 로직을 넣는 방식으로 구현하다 보면
시간이 상당히 소요되겠지만 구현은 될것이다.
그런데 구현하고 나니 상사가 다시와서 밀리세컨드 단위 말고 초단위로 측정해달라고 다시 요청했다고 가정해보자.
그러면 일일히 구현해놓았던 로직들을 변경해가면서 불만을 가지게 될 것이다.
이 상황에서 문제점은 다음과 같다.
- 회원가입, 회원 조회에 시간을 측정하는 기능은 핵심관심사항이 아니다.
- 시간 측정로직에 변경사항이 있을 경우 적용하기가 어렵다.
다행히도(?) 스프링을 사용하면 이러한 상황에 AOP를 쓰면 깔끔하게 해결할 수 있다.
AOP란 Aspect Oriented Programming의 약자로 공통 관심사항(cross-cutting concern)과 핵심 관심 사항(core concern)의 분리하는 것이 핵심이다.
즉, 뭔가 공통적인 처리가 필요할 때 사용하는 개념이라고 보면 될 것 같다.
2. AOP 적용하기
이번에는 TimeTraceApp이라는 별도의 클래스를 만들어서 여기에 AOP를 적용해보자.
@Aspect
@Component
public class TimeTraceApp {
@Around("execution(* com.chung.board..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString()+ " " + timeMs +"ms");
}
}
}
이렇게 하면 com.chung.board 하위의 모든 메서드들에게 실행하기 전에 execute 함수에 걸려서 들어오는데
joinPoint.proceed()에서 메소드를 실행(jointPoint.proceed()는 걸린 상태에 있던 메소드를 실행한다.)이고
finally에서 실행이 완료된 메서드의 finish 시간을 측정하게 된다.
눈치를 챘겠지만 @Around 어노테이션에서 AOP를 적용할 범위를 설정할 수 있다.
이러한 AOP를 사용하면 모든 메서드들의 시간 측정과 같이 공통 관심사항에 관한 문제들을 쉽게 해결할 수 있다.
또, 각 클래스들(Controller, service 등)의 로직들과 이러한 공통 관심사항 로직을 분리할 수 있어 코드를 깔끔하게 유지할 수 있다는 장점도 있다.
추가로 이러한 AOP클래스와 같이 특별한 빈은
@Component로 빈 등록을 하는 것 보다 @Configuration 의 설정 클래스에서 따로 빈 등록을 해주는 것을 추천한다.
추후에 유지보수 하는 데 있어 @Configuration클래스에서 따로 등록이 되있으면 알아보기 쉽기 때문이다.
3. AOP의 동작방식
그렇다면 이런 마법같은 AOP는 어떻게 동작하는 것일까?
만약 helloController 라는 컨트롤러와 memberService라는 서비스가 있다고 가정해보자.
그러면 스프링 컨테이너는 다음과 같이 빈을 생성해서 의존관계를 주입할 것이다.
그런데 우리는 memberService에 AOP를 건 상황이라고 가정해보자.
그러면 memberService의 각 메서드가 동작하기 전에 위의 예제코드인 2번에서 짠 execute함수가 실행이 되어야 할 것이다.
이에 스프링 컨테이너는 AOP 가 걸린 클래스에 대해 다음과 같이 동작한다.
우선 memberService와 비슷하게 생긴 memberService의 프록시 객체를 만들어서 helloController에 주입한다.
다만 이 프록시 객체는 스프링이 조작한 객체로 AOP에서 지정해둔 메서드를 실행 한 뒤에 실제 memberService를 호출하는 특징을 가지고 있다.
때문에 helloController가 memberService를 호출하면 이 프록시 객체가 호출이 되고
AOP에서 지정해둔 내용이 실행이 된 다음에 실제 memberService의 로직들을 수행하게 된다.
Reference
이 포스팅은 아래의 강좌를 참고하여 만들어졌습니다.
- 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
'Backend > Spring' 카테고리의 다른 글
[Thymeleaf]HTML Form 태그로 서버에 Multipart 형식의 데이터 전송하기(+로그인 인증문제 해결) (0) | 2021.02.03 |
---|---|
[Spring] 쉽게 익히는 JDBC Template 사용법 및 간단 예제 (0) | 2020.11.30 |
[Spring]Request Scope를 사용해서 깔끔하게 로그남기기 (0) | 2020.11.19 |
[Spring]프로토 타입 빈 사용시 생기는 문제점 해결하기(ObjectProvider와 JSR-330 Provider) (0) | 2020.11.12 |
[Spring]싱글톤 빈 VS 프로토 타입 빈 차이점 (0) | 2020.11.11 |