AOP를 활용해서 공통로직에 적용되는 코드 리팩토링
목차
1.문제 상황
2.AOP?
3.프로젝트에 적용
4.AOP의 주의점
1.문제 상황
블로그 프로젝트에서 모든 페이지에 사이드바가 적용이 되는데 로그인과 회원가입 페이지를 제외하면 7페이지이고 이 페이지마다 컨트롤러에서 쿼리를 모델에 담아야 하는데 이렇게 되면 단순반복이고 코드가 길어지는 상황이어서 AOP를 사용해서 코드의 양을 줄이고 좀 더 간결하게 코드를 리팩토링을 해야 되는 상황입니다.
2.AOP?
AOP는 Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이다. 여기에서 관점지향은 어떤 로직을 기준으로 해서 핵심적인 관점,부가적인 관점으로 나누어서 보고 그 관점을 기준으로 해서 하나의 단위로 묶는 것을 말한다.
대표적으로는 로깅,유효성 검사,파일 입출력 등이 있다.
3.프로젝트에 적용
3-1.Aop 라이브러리를 Gradle과 프로젝트에 적용을 한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
}
@SpringBootApplication
@EnableJpaAuditing
@EnableCaching
@EnableAspectJAutoProxy
public class JpaboardpracticeApplication {
public static void main(String[] args) {
SpringApplication.run(JpaboardpracticeApplication.class, args);
}
}
@EnableAspectJAutoProxy : 스프링에서 자동으로 AOP 프록시를 자동으로 생성하고 AspectJ와의 통합을 통해 런타임에 프록시 객체를 생성하고 조인 포인트에 Advice를 삽입할 수 있습니다
3-2.설정 클래스를 만든 뒤 쿼리와 관련된 클래스를 작성을 한다.
@Aspect
@Log4j2
@Component
@RequiredArgsConstructor
public class CommonQueryAspect {
//Controller에서 공통적으로 적용하는 쿼리를 정리
private final BoardService boardService;
private final HashTagService hashTagService;
private final CategoryService categoryService;
//view 패키지내에 있는 컨트롤러 & ModelAndView 가 있는 모든 부분에 적용
@Around("execution(* co.kr.board.controller.view.*Controller.*(..)) " +
"|| execution(* org.springframework.web.servlet.ModelAndView.*(..))")
public Object commonUiQuery(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("Common Aop !");
//게시글 갯수
Integer boardCount = boardService.articleCount();
//최근에 작성한 글(5개)
List<BoardDto.BoardResponseDto> top5 = boardService.findBoardTop5();
//해시태그 목록
List<String>hashTags = hashTagService.findAllHashTagNames();
//카테고리 목록
List<CategoryDto>categoryDtoList = categoryService.categoryList();
log.info("목록::"+boardCount);
Object result = proceedingJoinPoint.proceed();
if(result instanceof ModelAndView){
ModelAndView mv = (ModelAndView) result;
mv.addObject("count",boardCount);
mv.addObject("top5",top5);
mv.addObject("hashTag",hashTags);
mv.addObject("categoryMenu",categoryDtoList);
log.info("Query::"+proceedingJoinPoint.getSignature().getName());
}
return result;
}
}
코드를 설명하자면 다음과 같다.
@Aspect : Aop를 사용한다는 어노테이션입니다.
@Around : 메소드의 실행 전후에 코드를 삽입하거나, 메소드의 실행을 완전히 대체할 수 있도록 하는 어노테이션입니다.
포인트 컷을 사용을 해서 co.kr.board.controller.view.*Controller 내에 있는 ModelAndView를 사용하고 있는 모든 부분에 적용을 해서 사이드바 페이지에 있는 쿼리를 ModelAndView에 담아서 보내줍니다.
위의 코드를 도식화하면 다음과 같다.
AOP 적용전
AOP 적용후
AOP를 적용전에는 DispatcherServlet에서 Controller로 전달을 하지만
AOP를 적용을 하면 Aop Proxy와 @Around를 거쳐서 컨트롤러에 전달을 하게 된다.
4.AOP의 주의점
하지만 이런 AOP을 사용을 할 때에도 주의할 점이 있습니다.
- 과도한 사용
- AOP는 강력하지만 남용하면 코드의 가독성을 해칠 수 있습니다. 특히 비즈니스 로직과 관련 없는 기능을 AOP로 처리하다 보면 코드의 흐름을 추적하기 어려워질 수 있습니다. AOP는 로깅, 트랜잭션 관리, 보안 체크 등과 같은 횡단 관심사에 주로 사용하고, 비즈니스 로직에는 최소한으로 사용해야 합니다.
- 성능 저하
- AOP는 런타임에 프록시를 생성하고 메서드 호출을 가로채기 때문에, 잘못된 사용은 성능에 영향을 미칠 수 있습니다. 특히, 빈번하게 호출되는 메서드나 성능이 중요한 부분에 AOP를 적용할 때는 신중해야 합니다