오티스의개발일기

[SPRING BOOT] aop 를 활용하여 request요청 log찍기 본문

개발/spring boot

[SPRING BOOT] aop 를 활용하여 request요청 log찍기

안되면 될때까지.. 2022. 12. 29. 11:00
728x90

 

 

 

# 0 . AOP란? 

aop란 애스팩트라고 부르는 별도의 클래스에 캡슐화하는 접근 방식 이다

 

더쉽게 풀어서 이야기하자면

각 비지니스 코드파일 혹은 컨트롤러 등등 여러가지 파일에대한 어떠한 이벤트를 받고

로그같은 것들을 수행하고싶은데

각자 하나하나 만들기에는 중복되는 코드와 관리가 힘들기때문에

하나 혹은 정리해놓은 클래스 내부에서 자신이 관심이있는 클래스를 등록해두고

그 클래스가 동작할때

aop가 가지고있는 생명주기를 가지고

동작 전 그리고 동작시 그리고 후 에대한 어떠한 로직을 처리할때 사용한다.

 

오늘은 aop를 사용해볼것이고 너무 깊게 사용하지않을것이다.

그이유는 이 프로잭트에서는 불필요하기때문에

이것을 사용하여 매 request에 대한 요청 로그를 찍어볼것이다.

 

사용할 어노테이션에대해 간략하게 설명하겠다.

 

@Aspect = 관심사를 등록한다는이야기다 한마디로 이파일 aop에 사용하겠음~ 하고 말하는것과 같다.

곧 LogAdvice 라는 파일을 만들건데 그곳 상단에 적어줄것이다.

 

@JoinPoint = 어드바이스가 실행하는 동작을 끼워 넣을 수 있는 때를 말한다 우리는 이걸 사용하지 않을것이다.

 

@Advice = @JoinPoint 에서 실행되는 코드를 말한다.

 

@PointCut = 어드바이스가 실행되는 하나 이상의 조인 포인트를 선택하는 표현식 이건 한마디로 이벤트를 등록하는것과 마찬가지이다.

저곳에 파라미터로 어떤 위치를 적는데 그 위치에서 발생하는 모든 이벤트를 받겠다~ 이런말이다.

 

@Around = @JoinPoint 이전과 이후에 호출되는 어드바이스이다. 우리는 이것을 메인으로 사용해 로그처리를 할것이다.

 

 

 

자 시작해보겠다.

 

 

우리가 만들것은

 

1. logback-spring.xml

2. LogAdvice.java

2. UserController

 

이다 한번 만들어보자

 

일단 파일 구조는 이러하다

 

 

#1. aop dependencies 추가

implementation 'org.springframework.boot:spring-boot-starter-aop'

 

 

# 2. logback-spring.xml 생성

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 콘솔(STDOUT)에 log 기록 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative] %-5level ${PID:-} --- [%15.15thread] %-40.40logger{36} : %msg%n</Pattern>
        </layout>
    </appender>

    <!-- log root 레벨 설정 (logging.level.root=info)-->
    <root level="info">
        <!--     참조할 appender 설정 - STDOUT -->
        <appender-ref ref="STDOUT" />
    </root>

</configuration>

 

 

# 3. LogAdvice.java 생성

 

 

package com.example.backend.common.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Component
@Aspect
public class LogAdvice {

    private static final Logger logger = LoggerFactory.getLogger(LogAdvice.class);

    @Bean
    public RequestContextListener requestContextListener(){    return new RequestContextListener();}
    
    
    // 어디에 관심이 있는지 등록
    @Pointcut("within(com.example.backend.controller..*)") 
    public void onRequest() {
    }
    
    
    // 위에 만든 onRequest가 동작할시 requestLogging 동작함
    @Around("com.example.backend.common.aop.LogAdvice.onRequest()")
    public Object requestLogging(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        HttpServletRequest request = null;
        try {
            request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        } catch (IllegalStateException e) {
            return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        }
        long start = System.currentTimeMillis();
        try {
            return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        } finally {
            long end = System.currentTimeMillis();
            logger.info("Request: {} {}: ({}ms)", request.getMethod(), request.getRequestURL(), end - start);
        }
    }

}

 

 

이제 컨트롤러를 만들고 postman으로 확인해보자

 

 

package com.example.backend.controller;

import com.example.backend.common.exception.CustomApiException;
import com.example.backend.dto.CMRespDto;
import com.example.backend.dto.user.request.RequestUserRegisterDto;
import com.example.backend.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RestController
@RequiredArgsConstructor
@CrossOrigin(origins = {"*"})
@RequestMapping("/api/user")
public class UserController {

    private final UserService userService;
    @PostMapping("/register")
    public CMRespDto<?> register (@Valid @RequestBody RequestUserRegisterDto requestUserRegisterDto) {
        System.out.println("123");
        return new CMRespDto<>(200, "정상처리", null);
    }

    @GetMapping("/hi")
    public CMRespDto<?> hi (){
        userService.register();
        return new CMRespDto<>(200, "정상처리", null);
    }
}

 

 

 

 

 

정상 post man 으로 요청했을때 정상처리가 되는걸 볼수있다

 

 

 

로그도 정상 출력되는걸 확인할수있다.

 

 

이것으로 spring boot aop를 활용한 로그출력을 마치도록하겠다.

 

728x90
Comments