[내배캠/TIL(6/22)]Spring의 AOP

손홍서·2022년 6월 22일
1

Spring

목록 보기
13/24

day42 TIL

AOP가 왜 필요할까?

객체지향 프로그래밍은 클래스를 책임과 관심에 따라 분리한다. 클래스가 단일 책임을 가지게 되면서 모듈의 응집도는 높아지고 결합도는 낮아진다. 이러한 규칙 덕분에 클래스를 변경하는 이유는 하나이고, 한 부분에서 변경이 발생해도 파급효과가 적다.

하지만 이러한 객체지향 설계 방식을 따르면 공통된 기능이 흩어져 존재한다는 단점이 생긴다. 이러한 중복되는 부가기능을 어플리케이션의 핵심 비지니스 로직 코드로 분리하는 방법이 있을까?

그것이 바로 AOP!!!

먼저, Spring AOP를 사용하지 않는다는 가정하에 코드를 보자

회원이 서비스를 이용한 총시간을 구하려고한다. 이 경우 모든 controller에 총시간을 구하는 부가기능을 추가해 줘야한다.
부가기능을 추가한 코드는 다음과 같다.

@RestController
public class SearchRequestController {
	...
    
    @GetMapping("/api/search")
    public List<ItemDto> getItems(@RequestParam String query, @AuthenticationPrincipal UserDetailsImpl userDetails) {
    	// ** 부가 기능
        // 측정 시작 시간
        long startTime = System.currentTimeMillis();

        try {//** 핵심 기능
            String resultString = naverShopSearch.search(query);
            return naverShopSearch.fromJSONtoItems(resultString);
        } finally {//**부가 기능
            // 측정 종료 시간
            long endTime = System.currentTimeMillis();
            // 수행시간 = 종료 시간 - 시작 시간
            long runTime = endTime - startTime;
            // 로그인 회원 정보
            User loginUser = userDetails.getUser();

            // 수행시간 및 DB 에 기록
            UserTime userTime = userTimeRepository.findByUser(loginUser);
            if (userTime != null) {
                // 로그인 회원의 기록이 있으면
                long totalTime = userTime.getTotalTime();
                totalTime = totalTime + runTime;
                userTime.updateTotalTime(totalTime);
            } else {
                // 로그인 회원의 기록이 없으면
                userTime = new UserTime(loginUser, runTime);
            }

            System.out.println("[User Time] User: " + userTime.getUser().getUsername() + ", Total Time: " + userTime.getTotalTime() + " ms");
            userTimeRepository.save(userTime);
        }
    }
}

try 블럭에 있는 코드 외에는 부가 기능 코드이다. 이 부가 기능 코드를 controller에 있는 모든 메서드에 추가해야한다. 이 복잡한 코드를 반복해서 작성해야한다. 어떻게 작성했다고 하더라도 이후 이 코드를 수정하려면 이 작업을 반복해야한다. 이는 응집도가 떨어지고, 가독성도 떨어지며 유지보수 측면에서도 별로다.

AOP(Aspect-Oriented Programming)

AOP는 부가 기능을 모듈화 하여 쉽게 추가 가능하다.

  • 개념적 이해

    부가기능을 모듈로 만들어 핵심기능과 합쳐 실행되도록한다.

  • 스프링 실제 동작

@Component // 스프링 IoC 에 빈으로 등록
@Aspect
public class UserTimeAop {
    private final UserTimeRepository userTimeRepository;

    public UserTimeAop(UserTimeRepository userTimeRepository) {
        this.userTimeRepository = userTimeRepository;
    }

    @Around("execution(public * com.sparta.springcore.controller..*(..))") //controller에서 public인 부분만! -> 포인트컷 규칙
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        // 측정 시작 시간
        long startTime = System.currentTimeMillis();

        try {
            // 핵심기능 수행
            Object output = joinPoint.proceed();
            return output;
        } finally {
            // 측정 종료 시간
            long endTime = System.currentTimeMillis();
            // 수행시간 = 종료 시간 - 시작 시간
            long runTime = endTime - startTime;
            // 로그인 회원이 없는 경우, 수행시간 기록하지 않음
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth != null && auth.getPrincipal().getClass() == UserDetailsImpl.class) {
                // 로그인 회원 -> loginUser 변수
                UserDetailsImpl userDetails = (UserDetailsImpl) auth.getPrincipal();
                User loginUser = userDetails.getUser();

                // 수행시간 및 DB 에 기록
                UserTime userTime = userTimeRepository.findByUser(loginUser);
                if (userTime != null) {
                    // 로그인 회원의 기록이 있으면
                    long totalTime = userTime.getTotalTime();
                    totalTime = totalTime + runTime;
                    userTime.updateTotalTime(totalTime);
                } else {
                    // 로그인 회원의 기록이 없으면
                    userTime = new UserTime(loginUser, runTime);
                }

                System.out.println("[User Time] User: " + userTime.getUser().getUsername() + ", Total Time: " + userTime.getTotalTime() + " ms");
                userTimeRepository.save(userTime);
            }
        }
    }
}

AOP를 적용하면 controller들은 핵심 비지니스 로직만 남게 된다.


참고자료
https://tecoble.techcourse.co.kr/post/2021-06-25-aop-transaction/

profile
Hello World!!

0개의 댓글