Spring/Spring-detail

AOP 란 무엇인가? + 예제

YoonJong 2022. 10. 26. 16:05
728x90

AOP(관점지향 프로그래밍) 를 간단히 설명하면 공통 처리 등의 '횡단적 관심사(부가기능)'를 추출하고 프로그램의 여러 곳에서 호출할 수 있게 설정함으로써 개발자는 실현해야할 기능인 '중심적 관심사'에만 집중하도록 해주는 것이다.

 

AOP 는 내부적으로 아래와 같이 동작한다.

  1. 다이나믹 프록시 객체의 생성 요청

  2. 포인트컷을 통해 부가 기능 대상 여부 확인 -> 어디다가 적용할 건지

  3. 어드바이스로 부가 기능 적용 

  4. 실제 기능 처리


AspectJ 라는 AOP 외의 기술이 있다.

AspectJ 는 프록시를 이용하지 않았으며 CGLib 이라는 바이트 조작 라이브러리를 사용한다.

타깃 객체의 바이트를 고쳐서 부가기능을 직접 넣어주는 방법이다. 따라서 코드를 분리하지 않는다.

프록시를 사용하지 않고 복잡한 바이트를 고치는 방식을 사용하는 이유는 아래와 같다.

  1. 바이트를 조작하면 spring 과 같은 컨테이너의 도움이 필요없다.

  2. 프록시 방식보다 강력하고 유연하다.

 

AspectJ  와 AOP 의 자세한 차이점은 아래를 참고하면 좋을 것 같다.

https://logical-code.tistory.com/118

 

Spring AOP와 AspectJ 비교하기

Thanks to @ㅅㅈㅎ 님 덕분에 3-5 첫번째 문장의 오타를 수정했습니다. 감사합니다! (더 간편합니다다. ⇢ 더 간편합니다.) @김성수 님 덕분에 3-2. Weaving의 오타를 수정했습니다. 감사합니다! (컴파일

logical-code.tistory.com


다시 AOP 로.

관점 지향 프로그래밍
중심적 관심사 횡단적 관심사
실현해야 하는 기능 실현하는 프로그램 별도로 부수적으로 필요한 프로그램
  예외처리 / 로그 정보화면 / 트랜잭션 제어 등

 

핵심기능이 아닌 부가기능의 추가에 사용하는 기능이라고 생각하면 된다.

 

AOP 를 사용하지 않고 맨 처음의 예시처럼 모든 메서드에 시간을  찍어야 한다 라고 했을 때 발생할 수 있는 문제점은 아래와 같다

  • 중복코드 발생가능성 , 유지보수 어려움
  • 비지스니스 로직과 같이있으면 가독성 떨어짐

 

용어 내용
Advice 횡단적 관심사(부가기능) 구현
Aspect Advice를 정리한 것(클래스)
JoinPoint Advice를 중심적 관심사에 적용하는 시점
PointCut Advice를 삽입할수 있는 위치  ex ) 메서드가 get으로 시작하는 곳에만 조건 정의 등
Target Advice 가 도입되는 대상

포인트 컷의 종류

@Before: 대상 메서드의 수행 전

@After: 대상 메서드의 수행 후

@After-returning: 대상 메서드의 정상적인 수행 후

@After-throwing: 예외발생 후

@Around: 대상 메서드의 수행 전/후


테스트 삼아서 기존에 있던 코드에 시작시간을 AOP 를 사용해서 추가해보았다.

 

com.jong.post.domain.member.controller 아래 있는 모든 메서드 들이 실행되기 전! 에 시작시간과 어떤 메서드를 사용했는지 찍어준다.

 

execution 뒤에 정규식 표현이 낯설어서 구글링을 하면서 찾아보았는데, 자료가 많아서 적용시킬 수 있었다.

@Slf4j
@Aspect
@Component
public class SampleAspect {

    @Before("execution(* com.jong.post.domain.member.controller..*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        // 시작시간 + 메서드 이름출력
        long curTime = System.currentTimeMillis();
        SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
        String str = timeFormat.format(new Date(curTime));

        String methodName = joinPoint.getSignature().getName();

        log.info("시작시간 : " + str);
        log.info("메서드 이름 출력 : " + methodName);
    }
}

 

메서드는 2개로 포스트맨에서 테스트했다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberController {

    private final MemberService memberService;

    @PostMapping //회원 가입
    public ResponseEntity<MemberSignFormDto> join(@RequestBody MemberSignFormDto memberSignFormDto){
        memberService.signUp(memberSignFormDto);
        return ResponseEntity.status(HttpStatus.OK).body(memberSignFormDto);
    }
    ...
    ...
        @GetMapping // 전체조회
    public List<MemberResponse> findAll() {
        return memberService.findAll();
    }

차례대로 잘 출력되는 것을 확인 할 수 있었다.

728x90