이것저것 해보기🌼

[AOP] 메소드 로그 찍기, Decode/Encode 하기 본문

BE/Spring Boot

[AOP] 메소드 로그 찍기, Decode/Encode 하기

realtree 2021. 7. 5. 16:50

AOP를 활용하여 메소드 실행 전,후, 타입, 값, 수행시간 등 다양한 로그를 남길수 있다.

 

먼저 Dependencies에 aop를 추가해주어야한다.

 

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

 

 

 

예를 들어 아래와 같은 메소드 Controller가 정의되어있다고 하자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.example.aop.controller;
 
import com.example.aop.annotation.Decode;
import com.example.aop.annotation.Timer;
import com.example.aop.dto.User;
import org.springframework.web.bind.annotation.*;
 
@RestController
@RequestMapping("/api")
public class RestApiController {
 
    @GetMapping("/get/{id}")
    public String get(@PathVariable Long id, @RequestParam String name){
        return id+" "+name;
    }
    @PostMapping("/post")
    public User post(@RequestBody User user){
        return user;
    }
 
    @Timer
    @DeleteMapping("/delete")
    public void delete() throws InterruptedException {
 
        //db logic
        Thread.sleep(1000*2);
    }
 
 
    @Decode
    @PutMapping("/put")
    public User put(@RequestBody User user){
        System.out.println("put");
        System.out.println(user);
        return user;
    }
}
cs

 

 

 

1. 메소드 이름, 파라미터 타입, 값, 리턴값 확인

아래와 같이 AOP Annotation을 통해 메소드의 이름과 파라미터 타입, 값, 리턴값 등을 확인할 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.aop.aop;
 
 
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
 
import java.lang.reflect.Method;
 
@Aspect  //aop 사용
@Component  //spring 으로 관리하겠다
public class ParameterAop {
 
 
    @Pointcut("execution(* com.example.aop.controller..*.*(..))")
    private void cut(){}
 
    @Before("cut()")  //cut() 메소드 실행 전에
    public void before(JoinPoint joinPoint){
        //method 이름 출력하기
        MethodSignature methodSignature= (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        System.out.println(method.getName());
 
        Object[] args = joinPoint.getArgs();
 
        for(Object obj:args){
            System.out.println("type:"+obj.getClass().getSimpleName());
            System.out.println("value:"+obj);
        }
    }
 
    //정상 실행 후 return 오브젝트 값 확인하고싶을때
    @AfterReturning(value = "cut()", returning = "returnObj")
    public  void afterReturn(JoinPoint joinPoint, Object returnObj){
 
        System.out.println("return obj:");
        System.out.println(returnObj);
    }
}
cs

 

수행결과:  localhost:8080/get/100?name=seohee GET 수행시,

get
type:Long
value:100
type:String
value:seohee
return obj:
100 seohee

 

 

2. 메소드 수행시간 확인

메소드의 수행시간을 재려면 아래와 같이 @Around를  사용한다.

 

- Timer Annotation 만들기

java class 만들기에서 Annotation 을 선택하여 Timer를 하나 생성한다.

1
2
3
4
5
6
7
8
9
10
11
package com.example.aop.annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Timer {
}
cs

그리고 아래와 같은 TimerAop 클래스를 하나 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.aop.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.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
 
@Aspect
@Component
public class TimerAop {
 
 
    @Pointcut("execution(* com.example.aop.controller..*.*(..))")
    private void cut(){}
 
    @Pointcut("@annotation(com.example.aop.annotation.Timer)")
    private void enableTimer(){}
 
    @Around("cut() && enableTimer()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
 
        StopWatch stopWatch = new StopWatch();
 
        stopWatch.start();
        Object result = joinPoint.proceed(); //method 실행
        stopWatch.stop();
 
        System.out.println("total time:"+stopWatch.getTotalTimeSeconds());
    }
}
cs

 

수행결과: delete 수행시,

delete
total time:2.0118792

 

 

3. Decode / Encode 수행 확인

다음은 decode / encode 방법이다.

예제는 이메일주소를 Base64 로 보낸 뒤, UTF-8로 디코드하여 내용을 출력하는 것이다.

 

 

마찬가지로 Decode 라는 annotation 을 생성한 후,

DecodeAop 클래스를 하나 작성한다.

이 클래스의 기능은 내가 원하는 arg인 User에 해당할경우 이메일주소를 디코드해서 객체에 저장하고,

값을 리터닝할때 다시 base64로 인코딩해서 리턴하는 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.example.aop.aop;
 
import com.example.aop.dto.User;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
 
@Aspect
@Component
public class DecodeAop {
 
    @Pointcut("execution(* com.example.aop.controller..*.*(..))")
    private void cut(){}
 
    @Pointcut("@annotation(com.example.aop.annotation.Decode)")
    private void enableDecode(){}
 
    @Before("cut() && enableDecode()")
    public void before(JoinPoint joinPoint) throws UnsupportedEncodingException {
        Object[] args = joinPoint.getArgs();
 
        for(Object arg : args){
            if(arg instanceof User){
                User user = User.class.cast(arg);
 
                String base64email = user.getEmail();
                String  email =  new String(Base64.getDecoder().decode(base64email),"UTF-8");
 
                user.setEmail(email);  //user 객체 디코드
            }
        }
    }
 
    @AfterReturning(value = "cut() && enableDecode()", returning = "returnObj")
    public void afterReturn(JoinPoint joinPoint, Object returnObj){
        if(returnObj instanceof User ){
            User user = User.class.cast(returnObj);
 
            String email = user.getEmail();
            String  base64email =  Base64.getEncoder().encodeToString(email.getBytes());
 
            user.setEmail(base64email);  //user 객체 디코드
 
        }
    }
}
cs

 

그래서 main method를 이용해 먼저 원하는 이메일주소의 base64 인코딩 값을 확인해서, 이것을 PUT 메소드에 넣어 보냈다.

 

seohee@email.com 인코딩 : 

c2VvaGVlQGVtYWlsLmNvbQ==

 

put 수행결과:

리턴값은 그대로 BASE64 코드로 돌아오는 것을 알수 있고, 

이것을 decode & encode 하는 과정을 출력해보면 아래와 같았다.

 

 

 

 

'BE > Spring Boot' 카테고리의 다른 글

Validation, Exception 처리하기  (0) 2021.07.05
Annotation 정리  (0) 2021.07.05
Spring 핵심  (0) 2021.07.05
[Object Mapper] Object 를 text로 바꾸기  (0) 2021.07.05
Response 내려주기  (0) 2021.07.03