AOP란?

Aspect Oriented Programming

관점(Aspect) 지향 프로그래밍

→ 관심사(concern): 개발 시 필요한 고민이나 염두에 두어야 하는 일

  • 파라미터가 올바르게 들어왔을까?
  • 이 작업을 하는 사용자가 적절한 권한을 가진 사용자인가?
  • 이 작업에서 발생할 수 있는 모든 예외는 어떻게 처리해야 하는가?

위와 같은 고민들은 '핵심 로직'은 아니지만, 코드를 온전하게 만들기 위해서 필요한 고민들이다.

전통적인 방식(객체 지향 프로그래밍)에서는 개발자가 반복적으로 이러한 고민을 코드에 반영하지만, AOP는 이러한 고민에 대한 문제를 다른 방식으로 접근한다.

 

  1. 핵심 로직: 핵심 비즈니스 로직
    나눗셈에서 두 개의 숫자를 나누는 것
  2. 관심사: 주변 로직
    나눗셈에서 0을 나누는 것이 아닌지 등을 체크하는 것

관심사의 분리(separate concerns): AOP는 개발자가 염두에 두어야 하는 일들은 별도의 '관심사'로 분리하고, 핵심 비즈니스 로직만을 작성할 것을 권장한다.

 


Proxy

  • 핵심 비즈니스 로직에 접근할 때 대상 객체로 바로 접근하는 것이 아니라 Proxy를 통해서 간접적으로 접근하게 된다. 
  • 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장하여 클라이언트의 요청을 받아주어 처리하는 대리자 역할을 한다.
  • Proxy의 단어 자체에 '대리인'이라는 의미를 내포하고 있다. 스프링 AOP에서의 프록시란 말 그대로 대리하여 업무를 처리한다. 함수 호출자는 주요 업무가 아닌 보조 업무를 프록시에게 맡기고, 프록시는 내부적으로 이러한 보조 업무를 처리한다.

순수 자바로 AOP 구현해보기

AOP 사용 전 (전통적인 방식)

[Interface: Exam.java]

1
2
3
4
5
6
7
package spring.aop.entity;
 
public interface Exam {
    
    public int total();
    public float avg();
}
cs

 

[Class: NewlecExam.java]

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
package spring.aop.entity;
 
public class NewlecExam implements Exam{
    private int kor;
    private int eng;
    private int math;
    private int com;
    
    public NewlecExam() {
        
    }
    
    public NewlecExam(int kor, int eng, int math, int com) {
        this.kor = kor;
        this.eng = eng;
        this.math = math;
        this.com = com;
    }
 
    @Override
    public int total() {
        long start = System.currentTimeMillis(); //관심사
        
        int result = kor+eng+math+com; //핵심 로직
        
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        long end = System.currentTimeMillis(); //관심사
        
        String message = (end - start) + "ms 시간이 걸렸습니다.";
        System.out.println(message);
        
        return result;
    }
 
    @Override
    public float avg() {
        float result = total() / 4.0f;
        
        return result;
    }    
    
}
cs

 

total() 메서드를 보면 개발자가 실행 소요시간이 궁금해서 start변수와 end변수를 넣었다. 이와 관련된 코드들은 합계를 구하는 핵심 비즈니스 로직이 아니라 개발자가 별도로 염두에 두고 있는 '관심사'에 해당한다.

 

[Main Class: Program.java]

1
2
3
4
5
6
7
8
9
10
11
12
package spring.aop;
 
public class Program {
 
    public static void main(String[] args) {
        
        Exam exam = new NewlecExam(1111);
        
        System.out.println("total is " + exam.total());
    }
 
}
cs

실행 결과

 


AOP 적용 후

[Class: NewlecExam.java]

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
package spring.aop.entity;
 
public class NewlecExam implements Exam{
    private int kor;
    private int eng;
    private int math;
    private int com;
    
    public NewlecExam() {
        
    }
    
    public NewlecExam(int kor, int eng, int math, int com) {
        this.kor = kor;
        this.eng = eng;
        this.math = math;
        this.com = com;
    }
 
    @Override
    public int total() {
 
        int result = kor+eng+math+com;
        
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        return result;
    }
 
    @Override
    public float avg() {
        float result = total() / 4.0f;
        
        return result;
    }    
    
}
cs

 

[Main Class: Program.java]

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
package spring.aop;
 
public class Program {
 
    public static void main(String[] args) {
        
        Exam exam = new NewlecExam(1111);
        
        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;
                    }
                }
            );
        
        System.out.println("total is " + proxy.total());
        //proxy.total() or exam.total()
        System.out.println("avg is " + proxy.avg());
 
    }
 
}
cs

실행 결과

 

+ Recent posts