Baeg-won
좋았다면 추억이고 나빴다면 경험이다.
Baeg-won
전체 방문자
오늘
어제
  • 분류 전체보기
    • 🍃 Spring, Spring Boot
      • 스프링 프레임워크 기초
      • 스프링 핵심 원리 - 기본편
      • 자바 ORM 표준 JPA 프로그래밍 - 기본편
      • 스프링 MVC
      • 실전! 스프링 부트와 JPA 활용1 - 웹 애플리..
    • 🥑 Web Technoloy
    • 🚗 Backend Toy Project
      • 스프링 부트 게시판
      • Photogram
      • Baeg-won Clothing Gallery
    • 🥇 Problem Solving
      • Breadth-First Search
      • Depth-First Search
      • Backtracking
      • Simulation
      • Two-pointer
      • Binary Search
      • Greedy
      • Dynamic Programming
      • Minimum Spanning Tree
      • Dijkstra
      • Floyd warshall
    • ☕ Java
      • 명품 자바 에센셜
      • Applications
    • 🍦 JavaScript
      • JavaScript 기초
    • 🐧 Linux
      • 이것이 리눅스다(CentOS 8)
    • 📟 Database
      • 혼자 공부하는 SQL
    • 🧬 Data Structure
    • 🎬 HTML
    • 🎤 Tech Interview
    • 📌 etc
      • Unity 2D Raising Jelly Game
      • C++
      • 영어 쉐도잉

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Baeg-won

좋았다면 추억이고 나빴다면 경험이다.

🥑 Web Technoloy

Spring AOP(Aspect Oriented Programming)

2023. 6. 3. 18:19

💡 AOP(Aspect Oriented Programming)란?

  • 기존에는 개발자 또는 운영자에게 필요한 코드를 비즈니스 로직 코드와 함께 작성하였습니다.
public int total() {
    long start = System.currentTimeMillis();

    int result = kor + eng + math + com;    // 사용자 코드 부분(주 업무)

    try {
        Thread.sleep(200);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    long end = System.currentTimeMillis();

    String message = (end - start) + "ms 시간이 걸렸습니다.";    // 개발자 및 운영자 코드 부분(부 업무)
    System.out.println(message);

    return result;
}
  • 다만 이처럼 코드를 작성할 경우 코드가 복잡해져서 핵심 비즈니스 로직에 집중하지 못할 수 있고, 변경사항이 있을 시 비즈니스 로직이 작성되어 있는 파일을 직접 열어서 수정해야 하는 불편함이 생기게 됩니다.
  • 따라서 트랜잭션, 로그, 보안 등과 같이 핵심 로직에 공통적으로 포함되는 부가적인 기능들을 따로 분리하여 관리할 수 있도록 하는 기법이 AOP(Aspect Oriented Programming)입니다.
  • 이는 관점 지향 프로그래밍이라고도 불리며, 관점 지향이란 쉽게 말해 어떤 로직을 핵심적인 관점과 부가적인 관점으로 나누어서 보고, 그 관점을 기준으로 각각을 모듈화 하겠다는 것입니다.

  • 여기서 핵심적인 관점은 비즈니스 로직(주 업무), 부가적인 관점은 관리자 코드(부 업무)라고 볼 수 있으며, 부가적인 관점은 흩어진 관심사(Crosscutting Concerns)라고 부르기도 합니다.

💡 AOP 주요 개념

  • Aspect: 부가적인 관점, 즉 흩어진 관심사를 모듈화한 것
  • Target: Aspect를 적용하는 곳
  • Advice: 실질적으로 어떤 일을 해야할 지에 대한 것(실질적인 부가기능을 담은 구현체)
  • JointPoint: Advice가 적용될 위치(메서드 진입 시점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용 가능)
  • PointCut: JointPoint의 상세한 스펙을 정의한 것으로, 'A라는 메소드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음

💡 Spring AOP - Proxy

  • 이를 실현하기 위해서는 관점에 따라 나눈 두 코드를 연결시켜줄 수 있는 도구가 필요한데, 스프링에서는 이를 프록시(Proxy)를 통해 해결합니다.
  • 프록시는 타겟(Target)을 감싸서 타겟의 요청을 대신 받아주는 랩핑(Wrapping) 오브젝트입니다.
  • 호출자(클라이언트)에서 타겟을 호출하게 되면 실제로는 타겟이 아닌, 타겟을 감싸고 있는 프록시 객체가 호출되어, 타겟 메소드 호출 전에 선처리, 타겟 메소드 호출 후에 후처리를 진행합니다.
  • 프록시는 아래와 같은 구문을 통해 생성됩니다.
Exam proxy = Proxy.newProxyInstance(loader, interfaces, h);
  • 여기서 인자로 들어가는 loader는 프록시가 호출할 업무가 있는 클래스 정보이고, interfaces는 loader로 대입된 클래스가 구현하는 인터페이스를 말합니다.
  • 다만 interfaces는 보다시피 복수형이기 때문에 배열의 형태로 전달되어야 합니다.
  • 마지막으로 h는 우리가 실행할 관리자 코드, 즉 부가적인 업무를 말합니다.
  • 따라서 h에는 다음과 같은 익명 클래스가 작성될 수 있습니다.
Exam proxy = (Exam) Proxy.newProxyInstance(NewlecExam.class.getClassLoader(), new Class[] {Exam.class},
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long start = System.currentTimeMillis();

            Object result = method.invoke(exam, args);

            long end = System.currentTimeMillis();

            String message = (end - start) + "ms 시간이 걸렸습니다.";
            System.out.println(message);

            return result;
        }
    }
);

💡 AOP 종류

  • AOP를 사용할 경우 Aspect 실행 시점을 지정하는데에는 다음과 같은 어노테이션을 사용할 수 있습니다.
    • @Before: Target Method가 호출되기 전에 Advice 기능을 수행합니다.
    • @After: Target Method 결과와 상관없이 Target Method가 완료되면 Advice 기능을 수행합니다.
    • @AfterReturning: Target Method가 성공적으로 결과값을 반환한 경우에만 Advice 기능을 수행합니다.
    • @AfterThrowing: Target Method가 수행 중 예외를 던지는 경우에만 Advice 기능을 수행합니다.
    • @Around: Advice가 Target Method를 감싸는 형태로, Target Method 호출 전과 후에 Advice 기능을 수행합니다.

💡 AOP 사용 예제

  • 스프링 AOP를 사용하기 위해선 다음과 같은 의존성 추가 작업을 수행해주어야 합니다.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  • 다음으로 Aspect로 사용될 클래스를 생성하여 @Aspect 어노테이션을 붙여주고, @Component 어노테이션을 통해 스프링 빈으로 등록합니다.
@Component
@Aspect
public class PerfAspect {

    @Around("execution(* com.baegwon..*.EventService.*(..))")
    public Object logPerf(ProceedingJoinPoint pjp) throws Throwable{
        long begin = System.currentTimeMillis();

        Object retVal = pjp.proceed(); // 메서드 호출 자체를 감쌈

        System.out.println(System.currentTimeMillis() - begin);

        return retVal;
    }
}
  • @Around 어노테이션은 타겟 메소드를 감싸서 특정 Advice를 실행하겠다는 의미이며, 위 코드에서 Advice는 타겟 메소드가 실행된 시간을 측정하기 위한 로직으로 구현되어 있습니다.
  • execution(\* com.baegwon..\*.EventService.\*(..))가 의미하는 바는 com.baegwon 아래의 패키지 경로의 EventService 객체의 모든 메서드에 이 Aspect를 적용하겠다는 의미입니다.
    • 이 외에도 특정 어노테이션이 붙은 포인트에 해당 Aspect를 실행할 수 있는 기능과
      • @Around("@annotation(PerLogging)")
    • 스프링 빈의 모든 메서드에 적용할 수 있는 기능을 제공합니다.
      • @Around("bean(simpleEventService)")
  • Around Advice에서 사용할 공통 기능 메소드는 대부분 파라미터로 전달받은 ProceedingJointPoint의 proceed() 메소드만 호출하면 됩니다.
  • ProceedingJoinPoint 인터페이스는 개발도중 호출되는 대상 객체에 대한 정보, 실행되는 메서드에 대한 정보, 메서드를 호출할 때 전달된 인자에 대한 정보에 접근할 수 있도록 해줍니다.
  • 이제 위 Aspect를 적용할 Target을 생성해줍니다.
public interface EventService {

    void createEvent();
    void publishEvent();
    void deleteEvent();
}
@Component
public class SimpleEventService implements EventService {

    @Override
    public void createEvent() {
        try {
            Thread.sleep(1000);
        } catch(InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Created an event");
    }

    @Override
    public void publishEvent() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("Published an event");
    }

    @Override
    public void deleteEvent() {
        System.out.println("Delete an event");
    }
}
@Service
public class AppRunner implements ApplicationRunner {

    @Autowired
    EventService eventService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        eventService.createEvent();
        eventService.publishEvent();
        eventService.deleteEvent();
    }
}
  • 이후 프로그램을 실행해보면 아래와 같은 실행 결과를 확인할 수 있습니다.
Created an event
1003
Published an event
1000
Delete an event
0

📌 References

  • https://engkimbs.tistory.com/746
저작자표시

'🥑 Web Technoloy' 카테고리의 다른 글

JPA와 Hibernate 그리고 Spring Data JPA  (0) 2023.06.04
Lombok이란?  (1) 2023.06.04
SpringBoot에서 SMTP를 활용한 메일 전송 구현하기  (0) 2022.11.16
Spring Interceptor 개념 정리, 적용법  (0) 2022.11.15
OAuth 2.0 개념 정리  (0) 2022.11.11
    '🥑 Web Technoloy' 카테고리의 다른 글
    • JPA와 Hibernate 그리고 Spring Data JPA
    • Lombok이란?
    • SpringBoot에서 SMTP를 활용한 메일 전송 구현하기
    • Spring Interceptor 개념 정리, 적용법
    Baeg-won
    Baeg-won

    티스토리툴바