AOP 적용 방법 3가지

coding S's avatar
Apr 19, 2024
AOP 적용 방법 3가지

[ 첫번째 방법 ]

1. 깃발(어노테이션)을 만들고 2. 그 깃발을 PointCut(별칭)으로 등록 한다. 3. Advice를 만든다. (메서드 행위) 4. PointCut을 Advice에 적용한다

예제

package shop.mtcoding.aopstudy.config.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 메소드에 적용할 어노테이션을 정의하기 위한 어노테이션입니다. @Target(ElementType.METHOD) //런타임 시에도 어노테이션 정보를 유지하기 위한 어노테이션 @Retention(RetentionPolicy.RUNTIME) public @interface Hello { }
package shop.mtcoding.aopstudy.config.advice; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class HelloAdvice { // 깃발에 별칭주기 @Pointcut("@annotation(shop.mtcoding.aopstudy.config.annotation.Hello)") public void hello(){} // 매개변수에 접근해서 값을 찾는 것을 가능 - 값을 주입하려면 @Around 사용해야함 @Before("hello()") public void helloAdvice(JoinPoint jp) throws Throwable { Object[] args = jp.getArgs(); for (Object arg : args) { if(arg instanceof String){ String username = (String) arg; System.out.println(username+"님 안녕"); } } } }
 

[ HelloController ]

package shop.mtcoding.aopstudy.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import shop.mtcoding.aopstudy.config.annotation.Hello; @RestController public class HelloController { @GetMapping("/hello/v1") public String v1(){ return "v1"; } // http://localhost:8080/hello/v2?username=ssar @Hello @GetMapping("/hello/v2") public String v2(String username){ System.out.println("username : 값 변경? : "+username); return "v2"; } }
notion image
💡
포스트맨 돌리는 실습 직접 해보기!
 

[ 두번째 방법 ]

1. 이미 만들어져 있는 깃발을 PointCut(별칭)으로 등록 한다. 2. Advice를 만든다. (메서드 행위) 3. PointCut을 Advice에 적용한다. 4. postMapping과 putMapping에 관한 매개변수에 대한 값을 파싱 → 맵에 던져주고 가공해서 MyValidationException의 위임

예제

package shop.mtcoding.aopstudy.config.advice; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import shop.mtcoding.aopstudy.config.exception.MyValidationException; import java.util.HashMap; import java.util.Map; // Aspect로 동작하기 위한 클래스임을 선언하는 어노테이션 @Aspect // Spring의 컴포넌트로 등록되도록 하는 어노테이션 @Component public class ValidAdvice { // Pointcut을 선언 @PostMapping 어노테이션이 지정된 메소드를 대상으로 함 @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)") public void postMapping() { } // @PutMapping 어노테이션이 지정된 메소드를 대상으로 함 @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)") public void putMapping() { } // 메소드의 매개변수 중에 BindingResult가 있는 경우, 해당 객체에 에러가 있는지 검사 // 에러가 있으면 에러 정보를 Map에 담아 오류를 던짐 @Before("postMapping() || putMapping()") public void validationAdvice(JoinPoint jp) throws Throwable { Object[] args = jp.getArgs(); for (Object arg : args) { if (arg instanceof BindingResult) { BindingResult bindingResult = (BindingResult) arg; if (bindingResult.hasErrors()) { Map<String, String> errorMap = new HashMap<>(); // BindingResult에 포함된 모든 FieldError를 순회하면서 에러 정보를 Map에 담기 for (FieldError error : bindingResult.getFieldErrors()) { errorMap.put(error.getField(), error.getDefaultMessage()); } // 발생한 에러 정보를 담은 Map과 함께 MyValidationException을 throw throw new MyValidationException(errorMap); } } } }
 

[ 컨트롤러 ]

@PostMapping("/valid") public String join(@Valid JoinInDto joinInDto, BindingResult bindingResult){ return "ok"; }
 

[ JoinDTO ]

package shop.mtcoding.aopstudy.dto; import lombok.Getter; import lombok.Setter; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @Getter @Setter public class JoinInDto { @NotNull //NULL 안됨 private String username; @NotEmpty // 값이 비어 있으면 안됨 private String password; @NotEmpty // 값이 비어 있으면 안됨. @Size(min = 4, max = 10) // 4자 이상 10자 이하 private String email; }
 

[ MyValidationException ]

package shop.mtcoding.aopstudy.config.exception; import lombok.Getter; import java.util.Map; @Getter public class MyValidationException extends RuntimeException { private Map<String, String> erroMap; public MyValidationException(Map<String, String> erroMap) { this.erroMap = erroMap; } }
notion image
 

[ 세번째 방법 ]

깃발을 생성하지 않고, 특정한 패턴이 수행될 때 정의된 advice를 실행하게 할 수도 있다. Spring Framework에서는 XML을 이용하여 Aspect-Oriented Programming (AOP)을 설정하는 방법이 있다. 그러나 Spring Boot에서는 주로 어노테이션 기반의 AOP를 사용하며, execution expression을 이용하여 pointcut을 등록할 수 있습니다. 하지만, 직접적으로 execution expression을 이용하여 pointcut을 등록하려면 여러가지 설정을 해주어야 한다. 대신, Spring Boot에서는 AspectJ를 사용하여 간편하게 pointcut을 등록할 수 있는 기능을 제공한다. AspectJ는 Java와 비슷한 문법을 가지며, execution expression을 이용하여 pointcut을 등록하는 방법이 있다.
 
Share article

codingb