Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Archives
Today
Total
관리 메뉴

JAN's History

Spring AOP를 이용한 전처리와 후처리 구현방법 2탄!!(+무한참조 에러해결) 본문

스프링

Spring AOP를 이용한 전처리와 후처리 구현방법 2탄!!(+무한참조 에러해결)

JANNNNNN 2024. 5. 28. 15:17

이제 advice 메서드가 @Around를 통해 낚아채는 것을 확인했으니, 내부에 유효성검사 로직을 넣어주면

전처리 후처리 로직을 자동으로 실행할 수 있습니다.

@Around("execution(* com.cos.photogramstart.web.api.*Controller.*(..))")
    public Object apiAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println(" web api 컨트롤러 ================");
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg:args){
                System.out.println("유효성검사하는 함수입니다.");
                BindingResult bindingResult = (BindingResult) arg
                }
}

앞전 1탄에서 proceedingJoinPoint를 통해 메서드가 가지고 있는 모든 정보에 접근할 수 있음을 알았는데요.

우선 메서드가 가지고 있는 매개변수에 접근해서 오브젝트 배열에 담아보겠습니다!

 

무한참조 에러 발생

그.러.나 이렇게 배열에 바로 넣게되면, 무한참조 에러가 발생할 수 있습니다.

저의 경우에도 User 데이터를 불러오면서 images 객체도 같이 담아오다보니 무한참조가 발생했거든요 ..ㅜㅜ

➡️sysout 에서 toString하다가 getter가 다 되다보니 무한참조가 발생했다고 보시면 됩니다!

그래서 저는 User 오브젝트에 images 객체를 제외한 toString()을 직접 만들어 해결했습니다.

 

에러 해결

@Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", name='" + name + '\'' +
                ", website='" + website + '\'' +
                ", bio='" + bio + '\'' +
                ", email='" + email + '\'' +
                ", phone='" + phone + '\'' +
                ", gender='" + gender + '\'' +
                ", profileImageUrl='" + profileImageUrl + '\'' +
                ", role='" + role + '\'' +
                ", createDate=" + createDate +
                '}';
    }

➡️보면 User 데이터를 가져올때 toString을 오버라이딩하여 image는 빼고 getter를 사용하도록 하여 에러를 해결했습니다.

 

이렇게 되면 정상적으로 세션정보를 다 가지고 오게 됩니다


이제 우리는 AOP를 사용해 @Around로 지정한 위치에서 전처리 후처리를 나누는 방법을 알았습니다.

그리고 proceedingJoinPoint로 지정한 위치에서 실행되는 메서드에 내부 data에 접근할 수 있는 방법까지 확인했습니다.


그러면 if를 걸어서 BindingResult값이 있으면 유효성검사를 실행할 수 있도록 하겠습니다!

📍 BindingResult : @Valid로 유효성 검사를 하는 로직에만 있는 변수입니다.

그리고, 각자 ApiController와 Controller에서 적어두었던 유효성 검사 로직을 가져와 붙여주면 끝입니다.

@Component //RestController, Service든 모든 것들이 Component를 상속해서 만들어져 있음.
@Aspect
public class ValidationAdvice {

    @Around("execution(* com.cos.photogramstart.web.api.*Controller.*(..))")
    public Object apiAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//        System.out.println(" web api 컨트롤러 ================");
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg:args){
            if(arg instanceof BindingResult){
                //BindingResult : 유효성검사를 처리할 때 사용되는 객체
                //arg가 BindingResult타입의 인자라면, 유효성 검사가 필요한 메서드라는 뜻!
                System.out.println("유효성검사하는 함수입니다.");
                BindingResult bindingResult = (BindingResult) arg;

                if (bindingResult.hasErrors()) {
                    Map<String, String> errorMap = new HashMap<>();
                    for (FieldError error : bindingResult.getFieldErrors()) {
                        errorMap.put(error.getField(), error.getDefaultMessage());
                        System.out.println("error.getDefaultMessage() = " + error.getDefaultMessage());
                    }
                    throw new CustomValidationApiException("유효성검사 실패함", errorMap);
                }
            }
        }

        //proceedingJoinPoint => profile 함수의 모든 곳에 접근할 수 있는 변수
        //profile함수보다는 먼저 실행

        return proceedingJoinPoint.proceed(); //이때 profile함수가 실행됨.
    }

    @Around("execution(* com.cos.photogramstart.web.*Controller.*(..))")
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//        System.out.println(" web컨트롤러 ================");
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg:args){
            System.out.println("유효성검사하는 함수입니다.");
            BindingResult bindingResult = (BindingResult) arg;

            if (bindingResult.hasErrors()) {
                Map<String, String> errorMap = new HashMap<>();
                for (FieldError error : bindingResult.getFieldErrors()) {
                    errorMap.put(error.getField(), error.getDefaultMessage());
                    System.out.println("error.getDefaultMessage() = " + error.getDefaultMessage());
                }
                throw new CustomValidationException("유효성검사 실패함",errorMap);
            }

        }

        return proceedingJoinPoint.proceed(); //이때 profile함수가 실행됨.
    }
}

그리고 원래 Controller에 있던 유효성검사 로직은 삭제해주면 됩니다

➡️유효성검사 로직을 AOP관점으로 분리하여 사용하니 댓글달기 로직도 깔끔해보입니다!

 

결과

 

➡️예시로 댓글내용 없이 댓글을 작성했을 경우에도 성공적으로 CustomValidationApiException이 발동하면서 유효성검사가 진행되는 것을 알 수 있습니다

 

 

이렇게 하면 하나하나 ApiController와 Controller에서 적어둬야했던 유효성검사 로직을 자동으로 발동시킬 수 있습니다!