특정 기능에만 로그를 찍고 싶어! 그럼 어떻게 하지? 답은 AOP!

Shinny·2022년 8월 16일
0

애플리케이션 로직은 2가지로 나눌 수 있다.
1. 핵심 기능(ex. BoardSerivce의 핵심 기능은 게시판 로직)
2. 부가 기능(ex. 로그 추적)

부가 기능을 핵심 기능이 있는 Controller, Service, Repository Layer에 넣어도 좋을까? 답은 No이다. 생각해봐도 반복 코드가 넘칠 것 같고, 수정할 일이 생기면 너무 복잡할 것 같았다. 결국 핵심은 1. 부가 기능과 핵심 기능을 분리하는 것, 2. 부가 기능을 어디에 적용할지 결정하는 것이었다.
사실 AOP라는 단어는 종종 들어보기는 했지만 이번 프로젝트에서 실제 적용을 해보기 전까지는 굉장히 추상적이고 막연한 개념이었다.

AOP 적용 방식

공부를 해보니, AOP 적용 방식은 크게 3가지가 있었다. 컴파일 시점, 클래스 로딩 시점, 런타임 시점에 적용하는 것이다. 하지만 프록시 방식을 사용하는 스프링 AOP는 메서드 실행 지점에만 AOP를 적용할 수 있다.

-- 추가 설명
1. 컴파일 시점(.java 소스 코드를 컴파일러를 사용해 .class 파일로 만드는 시점에 부가기능 로직을 추가)
2. 클래스 로딩 시점(.class 파일은 JVM의 클래스 로더에 보관한다. 이 때 조작한 파일을 JVM에 올린다.)
3. 런타임 시점(프록시) (자바의 main 메서드가 이미 실행된 다음, 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있다.)

🔎 이번 프로젝트에 AOP를 적용한 코드

프록시 방식을 사용하는 스프링 AOP는 스프링 컨테이너가 관리할 수 있는 스프링 빈에만 AOP를 적용할 수 있다. Controller 는 스프링 컨테이너가 관리하는 Component이기 때문에 AOP를 적용할 수 있었고, 나는 Controller 의 코드가 실행되고 끝나는 지점에 AOP를 적용하였다.

@Slf4j
@Aspect
public class RequestLoggingAspect {

    @Pointcut("execution(* com.example.letsgongbu.controller..*(..))")
    private void allController() {}

    @Around("allController()")
    public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        long start = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            long end = System.currentTimeMillis();
            log.debug("Request : {}, {}, total : {}", request.getMethod(), request.getRequestURI(), end-start);
        }
        return joinPoint.proceed();
    }
}

AOP 용어 정리

  1. 조인 포인트: AOP를 적용할 수 있는 모든 지점이자 어드바이스가 적용될 수 있는 모든 위치
  2. 포인트컷 : 조인 포인트 중 어드바이스(부가기능)가 적용될 위치를 선별하는 기능
profile
비즈니스 성장을 함께 고민하는 개발자가 되고 싶습니다.

0개의 댓글