AOP란?

관점 지향 프로그래밍입니다. AOP는 OOP( 객체지향 프로그래밍 )에서의 반복되는 공통 기능의 코드를 극복했습니다. AOP는 어떤 로직을 기준으로 핵심적인 로직과 부가적인 로직으로 분리하여 그것들을 관점을 기준으로 모듈화 하여 공통된 코드를 줄이는 것입니다. 

저희가 사용할 부분은 Controller의 메소드가 호출될 때 호출된 메소드, 시작한 시각, 끝나는 시각을 Log로 찍는 것입니다.

 

1. MethodLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {
}
  • @Target: Annotation을 사용할 수 있는 곳을 정의합니다. Class, Parameter, Method, Field 등등 여러 가지 있습니다.
  • @Retention: Annotation의 설정으로 Annotation의 라이프 사이클을 정의합니다. Source, Class, Runtime이 있습니다. 보통 Runtime을 사용하여 프로젝트가 실행 중에도 사용할 수 있도록 Annotation의 라이프 사이클을 정해줍니다.

2. LogAspect

@Aspect
@Slf4j
@Component
public class LogAspect {

    @Around("@annotation(com.example.league.aop.MethodLog)")
    public Object logAop(ProceedingJoinPoint joinPoint)throws Throwable{
        Signature signature = joinPoint.getSignature();
        LocalDateTime startTime = LocalDateTime.now();
        String start = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:SS").format(startTime);
        log.info("[start time]: {} [target]: {} ",start, signature);
        Object result = joinPoint.proceed();
        LocalDateTime endTime = LocalDateTime.now();
        String end = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:SS").format(startTime);
        log.info("[end time]: {} [target]: {}",end,signature);
        return result;
    }
}

 

  • @Aspect: advice + pointcut을 합친 것입니다.
  • pointcout -> 어디에 적용할지에 대한 정보를 담고있다.
  • advice -> 어떤 일을 실행할지 담고 있습니다. 
  • 1 개의 pointcut과 1 개의 advice = advisor가 됩니다. 이 advisor를 1 개 혹은 여러 개 담고 있는 것을 모듈화 한 것을 @Aspect를 붙여 명시해줍니다.
  • 해당 객체는 spring bean으로 등록해야 하므로 ComponentScan을 이용하기 위해 @Component를 붙여줬습니다.
  • pointcut annotation은 여러 가지가 있습니다. 

          1. @PointCut: 타깃을 직접 정의할 수 있습니다.

          2. @Before: 타깃이 실행되기 전에 수행됩니다.

          3. @AfterRunning: 타겟이 실행된 후 수행됩니다. 실질적으로 마지막에 실행됩니다.

          4. @Around: 타깃을 실행 전, 후 모두 총괄합니다. ( 유의할 점으로 ProceedingJoinPoint.proceed()를 꼭 해줘야 합니다. 하지 않으면 메소드가 실행되지 않습니다.

  • 저희는 @Around를 사용했으며, pointcut 표현식으로 @annotation을 사용했으므로 "@annotation(어노테이션 위치)"를 해주시면 됩니다. 구체적인 패키지나 클래스로도 가능합니다. 그럴 때는 execution을 사용합니다.

          1. "execution( * com.example.league.controller..*.*(..))" -> com.example.league.controller 패키지부터 하위 패키지까지 해당되며, *.*은 모든 클래스와 해당 클래스에 모든 메소드를 의미합니다. 

          2. "execution( * com.example.league.controller..*Service.*(..))" -> com.example.league.controller 패키지부터 하위 패키지까지 해당되며, Service로 끝나는 클래스의 모든 메소드에 적용됩니다.

          3. "execution( * com.example.league.controller..*Service.*create(..))" -> com.example.league.controller 패키지부터 하위 패키지까지 해당되며, Service로 끝나는 클래스의 메소드 중에서 create로 끝나는 메소드에 적용됩니다.

          4. "execution( * com.example.league.controller..*Service.*create(*))" ->  com.example.league.controller 패키지부터 하위 패키지까지 해당되며, Service로 끝나는 클래스의 메소드 중에서 create로 끝나는 메소드 중에서 파라미터가 1 개인 것에 적용됩니다.

  • ProceedingJoinPoint: 저희는 Service나 Controller를 bean로 등록하였지만 실제 객체가 아닌 Proxy객체가 등록되며, Controller, Service가 ProceedingJoinPoint에 해당됩니다. 그래서. proceed()를 사용하면 실제 로직이 실행되는 것입니다. 

3. Controller 메소드들에 MethodLog를 붙여줍니다. ( 하나만 예시로 보여드리겠습니다.)

@RestController
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;

    @MethodLog  // pointcut에 해당되므로 bean으로 UserController가 아닌 Proxy 객체가 등록됩니다.
    @PostMapping("/signup")
    public ResponseEntity<String> signup(@RequestBody UserDto userDto){
        userService.signup(userDto);
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }
    @MethodLog
    @PostMapping("/login")
    public ResponseEntity<Long> login(@RequestBody LoginDto loginDto, HttpServletRequest request){
        SessionDto sessionDto = userService.login(loginDto);
        HttpSession session = request.getSession();
        session.setAttribute(SessionConst.LOGIN_MEMBER,sessionDto);
        return new ResponseEntity<>(sessionDto.getId(),HttpStatus.OK);
    }
}

 

4. Postman을 통한 Log 확인 test

1. User 한 명을 가입시킵니다. 

2. Log가 찍히는 것을 확인할 수 있습니다. 

 

지금까지 AOP를 이용하여 Controller가 실행될 때 Log를 찍는 것을 해보았습니다. AOP의 활용은 무궁무진합니다. 다음에 SpringBoot개념 카테고리에서 더 자세히 다뤄보도록 하겠습니다.추가할 로직들은 많지만 다뤄야할 개념 부분은 모두 다뤘다고 생각하여 추가하지 않겠습니다. 다음은 aws에 배포하는 것을 해보겠습니다.

 

모든 코드는 아래 링크에서 확인 가능합니다.

https://github.com/rlaehdals/blogProject

 

GitHub - rlaehdals/blogProject

Contribute to rlaehdals/blogProject development by creating an account on GitHub.

github.com