๐Ÿ“š [Spring] - Spring AOP

CodeByHanยท2025๋…„ 11์›” 30์ผ

์Šคํ”„๋ง

๋ชฉ๋ก ๋ณด๊ธฐ
32/33

์˜ค๋Š˜์€ Spring AOP์— ๋Œ€ํ•ด ์ •๋ฆฌํ•ด๋ณผ๋ ค๊ณ  ํ•œ๋‹ค.
ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‹ค ๋ณด๋ฉด ํ•ต์‹ฌ ๋กœ์ง ๋ง๊ณ ๋„ ๋กœ๊น…, ๊ถŒํ•œ ์ฒดํฌ, ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ฐ™์€ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋„ฃ์–ด์•ผ ํ•  ๋•Œ๊ฐ€ ๋งŽ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด๋Ÿฐ ์ฝ”๋“œ๋“ค์ด ์—ฌ๊ธฐ์ €๊ธฐ ์„ž์—ฌ๋ฒ„๋ฆฌ๋ฉด ๋‚˜์ค‘์— ๊ด€๋ฆฌํ•˜๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ํž˜๋“ค์–ด์ง„๋‹ค.
๊ทธ๋ž˜์„œ โ€œ์ด๋Ÿฐ ๊ณตํ†ต์ ์ธ ์ž‘์—…๋“ค์„ ๊น”๋”ํ•˜๊ฒŒ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์—†์„๊นŒ?โ€ ๋ผ๋Š” ๊ณ ๋ฏผ์—์„œ Spring AOP๋ฅผ ๊ณต๋ถ€ํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ์ด๋ฒˆ์— ์ •๋ฆฌํ•œ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ ๋‚ด ํ”„๋กœ์ ํŠธ์—๋„ ์ ์šฉํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

๐Ÿค” ๊ด€์ ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Aspect Oriented Programming )

AOP ๋Š” ํ”„๋กœ๊ทธ๋žจ ์ „๋ฐ˜์— ๊ฑธ์ณ ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์„ ๋ชจ๋“ˆํ™”ํ•˜์—ฌ ์ฝ”๋“œ์˜ ๋ณต์žก๋„๋ฅผ ๊ฐ์†Œ ์‹œํ‚ค๊ณ , ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•œ๋‹ค.

AOP ์˜ ํ•„์š”์„ฑ์„ ์ดํ•ดํ•˜๋Š”๋ฐ ๊ฐ€์žฅ ๊ธฐ์ดˆ๊ฐ€ ๋˜๋Š” ๊ฐœ๋…์€ ๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ(Separation of Concerns) ์ด๋‹ค.

์„œ๋กœ ๋‹ค๋ฅธ ๊ด€์‹ฌ์‚ฌ(Concerns)๊ฐ€ ํ•œ ์ฝ”๋“œ์— ์„ž์—ฌ ์žˆ๋‹ค๋ฉด, ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์„ ์ˆ˜์ •ํ•  ๊ฒฝ์šฐ, ์˜๋„์น˜ ์•Š์€ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ AOP๋Š” ํ•ต์‹ฌ ๊ด€์‹ฌ์‚ฌ(Core Concern) ์™€ ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ(Cross-cutting Concern) ๋ฅผ ๋‚˜๋ˆ„์–ด์„œ ๊ด€๋ฆฌ๋ฅผ ํ•œ๋‹ค.

๐Ÿ“Œ Spring AOP

AOP ๊ฐœ๋…๋„

ํ•ต์‹ฌ ๊ด€์‹ฌ์‚ฌ(Core Concern), ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ(Cross-cutting Concern)... ์†”์งํžˆ ์ด๋ ‡๊ฒŒ ๋งํ•˜๋ฉด ์šฐ๋ฆฌ ๊ฐœ๋ฐœ์ž๋“ค์€ "๋ญ” ์†Œ๋ฆฌ์•ผ?" ๋ผ๋Š” ๋ง์ด ๋‚˜์˜ฌ ์ˆ˜๋„ ์žˆ๋‹ค.

์ฝ”๋“œ๋กœ ํ•œ๋ฒˆ ์ดํ•ดํ•ด๋ณด์ž! ๐Ÿ‘‡

๐Ÿ“ ์‹ค์ œ ์ƒํ™ฉ์œผ๋กœ ์ดํ•ดํ•˜๊ธฐ

๐Ÿ˜‹ ์ฒ˜์Œ์—” ๊ดœ์ฐฎ์•˜์–ด

ํšŒ์›๊ฐ€์ž…์„ ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์™€๋ดค๋‹ค. ์—ฌ๊ธฐ๊นŒ์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.

@Transactional
public Member signUp(String name, String email) {
    // ํ•ต์‹ฌ ๋กœ์ง: ํšŒ์› ์ •๋ณด ์ €์žฅ
    Member member = new Member(name, email);
    return memberRepository.save(member);
}

ํ•ต์‹ฌ ๋กœ์ง๋งŒ ๊น”๋”ํ•˜๊ฒŒ ์žˆ์–ด์„œ ๋ณด๊ธฐ๋„ ์ข‹๊ณ , ์ดํ•ดํ•˜๊ธฐ๋„ ์‰ฝ๋‹ค. โœจ


์•…๋• ์‚ฌ์žฅ๋‹˜ ๋“ฑ์žฅ ๐Ÿ‘ฟ

ํ•˜์ง€๋งŒ ์•…๋• ์‚ฌ์žฅ๋‹˜์ด "๊ฑฐ๊ธฐ ์‹ ์ž…! ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์‹œ๊ฐ„ ์ธก์ •ํ•ด๋ด!" ์ด๋ ‡๊ฒŒ ์š”๊ตฌ๋ฅผ ํ•˜๋ฉด ์šฐ๋ฆฌ๋Š” ์ด๋ ‡๊ฒŒ ์ธก์ •์„ ํ•ด์•ผ ํ•œ๋‹ค.

@Transactional
public Member signUp(String name, String email) {
    long start = System.currentTimeMillis(); // ์‹œ์ž‘ ์‹œ๊ฐ„ ์ธก์ •
    
    // ํ•ต์‹ฌ ๋กœ์ง: ํšŒ์› ์ •๋ณด ์ €์žฅ
    Member member = new Member(name, email);
    Member savedMember = memberRepository.save(member);
    
    long end = System.currentTimeMillis(); // ๋ ์‹œ๊ฐ„ ์ธก์ •
    System.out.println("signUp ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
    
    return savedMember;
}

์Œ... ์†”์งํžˆ ์—ฌ๊ธฐ๊นŒ์ง€๋Š” ๊ดœ์ฐฎ๋‹ค. ์ฝ”๋“œ๊ฐ€ ์ข€ ์ง€์ €๋ถ„ํ•ด์กŒ์ง€๋งŒ ํ•  ๋งŒํ•˜๋‹ค.


์ง€์˜ฅ์˜ ์‹œ์ž‘ ๐Ÿ”ฅ

ํ•˜์ง€๋งŒ ์•…๋• ์‚ฌ์žฅ๋‹˜์ด "์•„๋‹ˆ ํšŒ์›๊ฐ€์ž… ๋ง๊ณ  ํƒˆํ‡ด, ์ˆ˜์ •, ํšŒ์› ์กฐํšŒ๋„ ๋‹ค ์ธก์ •ํ•ด๋ด! ์•„ ๊ทธ๋ฆฌ๊ณ  ์ฃผ๋ฌธ, ์ฃผ๋ฌธ ์ทจ์†Œ, ๋ฐฐ์†ก, ๊ฒฐ์ œ๋„!" ๋ผ๊ณ  ํ•˜๋ฉด...

@Transactional
public void withdrawMember(Long memberId) {
    long start = System.currentTimeMillis();
    // ํ•ต์‹ฌ ๋กœ์ง
    memberRepository.deleteById(memberId);
    long end = System.currentTimeMillis();
    System.out.println("withdrawMember ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
}

@Transactional
public void updateMember(Long memberId, String name) {
    long start = System.currentTimeMillis();
    // ํ•ต์‹ฌ ๋กœ์ง
    Member member = memberRepository.findById(memberId);
    member.updateName(name);
    long end = System.currentTimeMillis();
    System.out.println("updateMember ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
}

@Transactional
public Order createOrder(Long memberId, Long productId) {
    long start = System.currentTimeMillis();
    // ํ•ต์‹ฌ ๋กœ์ง
    Order order = new Order(memberId, productId);
    orderRepository.save(order);
    long end = System.currentTimeMillis();
    System.out.println("createOrder ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
}

@Transactional
public void cancelOrder(Long orderId) {
    long start = System.currentTimeMillis();
    // ํ•ต์‹ฌ ๋กœ์ง
    Order order = orderRepository.findById(orderId);
    order.cancel();
    long end = System.currentTimeMillis();
    System.out.println("cancelOrder ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
}

์†์ด ์•„ํ”„๋‹ค... ๐Ÿ˜ญ ๊ทธ๋ฆฌ๊ณ  ์ด๊ฑด ์‹œ๊ฐ„ ์ธก์ •๋งŒ ์ถ”๊ฐ€ํ•œ ๊ฑด๋ฐ, ๋งŒ์•ฝ ์—ฌ๊ธฐ์— ๋กœ๊น…, ๋ณด์•ˆ ์ฒดํฌ, ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๊นŒ์ง€ ์ถ”๊ฐ€ํ•˜๋ผ๊ณ  ํ•˜๋ฉด?


๐Ÿ˜ฑ ๋ฌธ์ œ์  ์ •๋ฆฌ

์ด๋ ‡๊ฒŒ ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ๋˜‘๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌ-๋ถ™์—ฌ๋„ฃ๊ธฐ ํ•˜๋‹ค ๋ณด๋ฉด:

  • ์ฝ”๋“œ ์ค‘๋ณต โ†’ 100๊ฐœ ๋ฉ”์„œ๋“œ๋ฉด 100๋ฒˆ ๋ณต๋ถ™
  • ์œ ์ง€๋ณด์ˆ˜ ์ง€์˜ฅ โ†’ ์‹œ๊ฐ„ ์ธก์ • ๋ฐฉ์‹ ๋ฐ”๊พธ๋ ค๋ฉด 100๊ฐœ ๋‹ค ์ˆ˜์ •
  • ํ•ต์‹ฌ ๋กœ์ง ํŒŒ์•… ์–ด๋ ค์›€ โ†’ ์ •์ž‘ ์ค‘์š”ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์— ๋ฌปํ˜€๋ฒ„๋ฆผ
  • ์‹ค์ˆ˜ํ•˜๊ธฐ ์‰ฌ์›€ โ†’ ํ•œ๋‘ ๊ตฐ๋ฐ ๋นผ๋จน์œผ๋ฉด ๋

๐Ÿ’ก AOP๊ฐ€ ํ•ด๊ฒฐ์ฑ…์ด๋‹ค!

๋ฐ”๋กœ ์ด๋Ÿด ๋•Œ AOP(Aspect-Oriented Programming, ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ)๊ฐ€ ํ•„์š”ํ•˜๋‹ค!

AOP๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๋ฐ”๋€๋‹ค:

// ํ•ต์‹ฌ ๋กœ์ง๋งŒ ๊น”๋”ํ•˜๊ฒŒ!
@Transactional
public Member signUp(String name, String email) {
    Member member = new Member(name, email);
    return memberRepository.save(member);
}

@Transactional
public void withdrawMember(Long memberId) {
    memberRepository.deleteById(memberId);
}

@Transactional
public Order createOrder(Long memberId, Long productId) {
    Order order = new Order(memberId, productId);
    return orderRepository.save(order);
}

์‹œ๊ฐ„ ์ธก์ • ๋กœ์ง์€ ๋”ฑ ํ•œ ๊ณณ์—๋งŒ!

@Aspect
@Component
public class ExecutionTimeAspect {
    
    @Around("execution(* com.example.service..*(..))")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object result = joinPoint.proceed(); // ์‹ค์ œ ๋ฉ”์„œ๋“œ ์‹คํ–‰
        
        long end = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName + " ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
        
        return result;
    }
}

๐ŸŽฏ ํ•ต์‹ฌ ์ •๋ฆฌ

  • ํ•ต์‹ฌ ๊ด€์‹ฌ์‚ฌ(Core Concern): ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง (ํšŒ์›๊ฐ€์ž…, ์ฃผ๋ฌธ ๋“ฑ)
  • ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ(Cross-cutting Concern): ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ์— ๊ณตํ†ต์œผ๋กœ ์ ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ (์‹œ๊ฐ„ ์ธก์ •, ๋กœ๊น…, ๋ณด์•ˆ ๋“ฑ)

AOP๋Š” ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ๋ฅผ ํ•ต์‹ฌ ๋กœ์ง์—์„œ ๋ถ„๋ฆฌํ•ด์„œ ํ•œ ๊ณณ์—์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค!

์ด์ œ ์•…๋• ์‚ฌ์žฅ๋‹˜์ด "๋กœ๊น…๋„ ์ถ”๊ฐ€ํ•ด!" ๋ผ๊ณ  ํ•ด๋„ ๋‹นํ™ฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ๐Ÿ˜Ž


๐Ÿ“š ๊ฐ„๋‹จ AOP ์šฉ์–ด ์ •๋ฆฌ

1. Target (ํƒ€๊ฒŸ)

๋ง ๊ทธ๋Œ€๋กœ Target, ์ฆ‰, ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•  ๋Œ€์ƒ์„ ์˜๋ฏธํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์‹œ๊ฐ„ ์ธก์ • ๊ธฐ๋Šฅ์„ ์ ์šฉํ•  MemberService, OrderService ๊ฐ™์€ ํด๋ž˜์Šค๋“ค์ด ๋ฐ”๋กœ Target์ด๋‹ค.

// ์ด ํด๋ž˜์Šค๊ฐ€ ๋ฐ”๋กœ Target!
@Service
public class MemberService {
    public Member signUp(String name, String email) {
        // ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
    }
}

2. Advice (์–ด๋“œ๋ฐ”์ด์Šค)

ํƒ€๊นƒ์—๊ฒŒ ์ œ๊ณตํ•  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ๋ชจ๋“ˆ์„ ์˜๋ฏธํ•œ๋‹ค.

์‰ฝ๊ฒŒ ๋งํ•ด์„œ "๋ฌด์Šจ ๊ธฐ๋Šฅ? ์–ด๋А ์‹œ์ ์—?"๋ฅผ ์ •์˜ํ•œ๋‹ค. ์‹œ๊ฐ„ ์ธก์ • ์ฝ”๋“œ, ๋กœ๊น… ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋กœ Advice๋‹ค.

Advice ์ข…๋ฅ˜:

  • @Before: ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „
  • @After: ๋ฉ”์„œ๋“œ ์‹คํ–‰ ํ›„ (์˜ˆ์™ธ ๋ฐœ์ƒ ์—ฌ๋ถ€ ์ƒ๊ด€์—†์ด)
  • @AfterReturning: ๋ฉ”์„œ๋“œ ์ •์ƒ ์ข…๋ฃŒ ํ›„
  • @AfterThrowing: ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ
  • @Around: ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „ํ›„ (๊ฐ€์žฅ ๊ฐ•๋ ฅํ•จ!)
@Around("execution(* com.example.service..*(..))")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    // ์ด ๋ฉ”์„œ๋“œ ์ „์ฒด๊ฐ€ Advice!
    long start = System.currentTimeMillis();
    Object result = joinPoint.proceed();
    long end = System.currentTimeMillis();
    return result;
}

3. Join Point (์กฐ์ธ ํฌ์ธํŠธ)

Advice๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ์ , ์ƒ์„ฑ์ž ํ˜ธ์ถœ ์‹œ์ , ํ•„๋“œ ๊ฐ’ ์ ‘๊ทผ ์‹œ์  ๋“ฑ์ด ๋  ์ˆ˜ ์žˆ๋‹ค. Spring AOP์—์„œ๋Š” ์ฃผ๋กœ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ์ ์„ ์‚ฌ์šฉํ•œ๋‹ค.

public Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
    // joinPoint๋ฅผ ํ†ตํ•ด ์‹คํ–‰๋˜๋Š” ๋ฉ”์„œ๋“œ ์ •๋ณด๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Œ
    String methodName = joinPoint.getSignature().getName(); // ๋ฉ”์„œ๋“œ ์ด๋ฆ„
    Object[] args = joinPoint.getArgs(); // ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ
    
    return joinPoint.proceed(); // ์‹ค์ œ ๋ฉ”์„œ๋“œ ์‹คํ–‰
}

4. Pointcut (ํฌ์ธํŠธ์ปท)

Advice๋ฅผ ์ ์šฉํ•  Join Point๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์‰ฝ๊ฒŒ ๋งํ•ด์„œ "์–ด๋–ค ๋ฉ”์„œ๋“œ์— ์ ์šฉํ•  ๊ฒƒ์ธ๊ฐ€"๋ฅผ ์ •์˜ํ•˜๋Š” ํ•„ํ„ฐ ์—ญํ• ์„ ํ•œ๋‹ค.

// service ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ํด๋ž˜์Šค, ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ์ ์šฉ
@Around("execution(* com.example.service..*(..))")

// MemberService์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ์ ์šฉ
@Around("execution(* com.example.service.MemberService.*(..))")

// signUp์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ์ ์šฉ
@Around("execution(* com.example.service.*Service.signUp*(..))")

Pointcut ํ‘œํ˜„์‹ ์˜ˆ์‹œ:

  • execution(* com.example.service..*(..)): service ํŒจํ‚ค์ง€์™€ ํ•˜์œ„ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
  • execution(public * *(..)): ๋ชจ๋“  public ๋ฉ”์„œ๋“œ
  • execution(* save*(..)): save๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ

5. Aspect (์• ์ŠคํŽ™ํŠธ)

Advice + Pointcut์„ ํ•ฉ์นœ ๊ฒƒ์ด๋‹ค.

"์–ด๋””์—(Pointcut), ๋ฌด์—‡์„(Advice) ์ ์šฉํ•  ๊ฒƒ์ธ๊ฐ€"๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ์€ ๋ชจ๋“ˆ์ด๋‹ค.

@Aspect  // ์ด ํด๋ž˜์Šค๊ฐ€ Aspect!
@Component
public class ExecutionTimeAspect {
    
    // Pointcut + Advice = Aspect
    @Around("execution(* com.example.service..*(..))")  // Pointcut
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {  // Advice
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        System.out.println(joinPoint.getSignature().getName() + " ์‹คํ–‰ ์‹œ๊ฐ„: " + (end - start) + "ms");
        return result;
    }
}

6. Weaving (์œ„๋น™)

Pointcut์œผ๋กœ ์ง€์ •ํ•œ Target์— Advice๋ฅผ ์ ์šฉํ•˜๋Š” ๊ณผ์ •์„ ์˜๋ฏธํ•œ๋‹ค.

์‰ฝ๊ฒŒ ๋งํ•ด์„œ ํ•ต์‹ฌ ๋กœ์ง์— ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ "์งœ ๋„ฃ๋Š”" ์ž‘์—…์ด๋‹ค.

Weaving ์‹œ์ :

  • ์ปดํŒŒ์ผ ์‹œ์ : .java โ†’ .class ํŒŒ์ผ ์ƒ์„ฑ ์‹œ (AspectJ)
  • ํด๋ž˜์Šค ๋กœ๋”ฉ ์‹œ์ : ํด๋ž˜์Šค๋ฅผ JVM์— ๋กœ๋“œํ•  ๋•Œ (AspectJ)
  • ๋Ÿฐํƒ€์ž„ ์‹œ์ : ์‹คํ–‰ ์ค‘์— ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ์ ์šฉ (Spring AOP ๋ฐฉ์‹!)
// Spring AOP๋Š” ๋Ÿฐํƒ€์ž„ ์œ„๋น™ ์‚ฌ์šฉ
// ์‹ค์ œ๋กœ๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๋งŒ๋“ค์–ด์ ธ์„œ ์‹คํ–‰๋จ

MemberService memberService = new MemberService(); // ์›๋ณธ ๊ฐ์ฒด
// ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ์ฃผ์ž…๋˜๋Š” ๊ฒƒ์€...
MemberService$$EnhancerBySpringCGLIB$$... // ํ”„๋ก์‹œ ๊ฐ์ฒด!

๐ŸŽฏ ์šฉ์–ด ๊ด€๊ณ„๋„

Aspect = Pointcut + Advice

Target โ† (Pointcut์œผ๋กœ ์„ ๋ณ„) โ† Advice ์ ์šฉ (Weaving)
     โ†“
 Join Point (์‹ค์ œ ์‹คํ–‰ ์ง€์ )

ํ•œ ์ค„ ์š”์•ฝ:

  • Target: ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ์ ์šฉํ•  ๋Œ€์ƒ
  • Advice: ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ ์ฝ”๋“œ (์–ธ์ œ, ๋ฌด์—‡์„)
  • Join Point: ์ ์šฉ ๊ฐ€๋Šฅํ•œ ์ง€์ 
  • Pointcut: Join Point ์ค‘์—์„œ ์‹ค์ œ ์ ์šฉํ•  ์ง€์ ์„ ์„ ๋ณ„ (์–ด๋””์—)
  • Aspect: Advice + Pointcut์„ ๋ชจ๋“ˆํ™”ํ•œ ๊ฒƒ
  • Weaving: ์‹ค์ œ๋กœ ์ ์šฉํ•˜๋Š” ๊ณผ์ •

Spring AOP ๋™์ž‘ ์›๋ฆฌ

Spring์—์„œ AOP๋Š” ํ”„๋ก์‹œ ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
ํ”„๋ก์‹œ ํŒจํ„ด์ด๋ž€, ์–ด๋–ค ๊ฐ์ฒด(ํƒ€๊ฒŸ)๋ฅผ ์ง์ ‘ ์ฐธ์กฐํ•˜์ง€ ์•Š๊ณ , ๋Œ€์‹  ๊ทธ ๊ฐ์ฒด๋ฅผ ๋Œ€์‹  ์ˆ˜ํ–‰ํ•˜๋Š” ํ”„๋ก์‹œ(proxy) ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ์‹์„ ๋งํ•œ๋‹ค.

๋จผ์ € BeanPostProcessor์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž

BeanPostProcessor(๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ)

์ƒ์„ฑ๋œ ๋นˆ ๊ฐ์ฒด๋ฅผ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•˜๊ธฐ ์ „์— ์กฐ์ž‘ํ•˜๋Š” ๊ฐ์ฒด

์‰ฝ๊ฒŒ ๋งํ•ด์„œ, ๋นˆ์ด ์ƒ์„ฑ๋˜๊ณ  ๋‚˜์„œ "์ž ๊น! ์ด ๋นˆ์„ ๊ทธ๋Œ€๋กœ ๋“ฑ๋กํ•˜์ง€ ๋ง๊ณ  ๋‚ด๊ฐ€ ์† ์ข€ ๋ด์•ผ๊ฒ ์–ด!" ํ•˜๊ณ  ์ค‘๊ฐ„์— ๋ผ์–ด๋“œ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

public interface BeanPostProcessor {
    
    // Bean ์ดˆ๊ธฐํ™” ์ „์— ์‹คํ–‰
    default Object postProcessBeforeInitialization(Object bean, String beanName) 
            throws BeansException {
        return bean;
    }
    
    // Bean ์ดˆ๊ธฐํ™” ํ›„์— ์‹คํ–‰ โญ (AOP๋Š” ์—ฌ๊ธฐ์„œ ํ”„๋ก์‹œ๋กœ ๋ฐ”๊ฟˆ!)
    default Object postProcessAfterInitialization(Object bean, String beanName) 
            throws BeansException {
        return bean;
    }
}

ํ•˜๋Š”์ผ
1. ์ƒ์„ฑ๋œ ๋นˆ ์ „๋‹ฌ
2. ๋นˆ ๊ต์ฒด ๋Œ€์ƒ ํ™•์ธ
3. ๋นˆ ๊ต์ฒด ๋Œ€์ƒ์ด ๋งž๋‹ค๋ฉด ์ด๋ฅผ ์กฐ์ž‘ ๋˜๋Š” ๊ต์ฒด
4. ๊ต์ฒดํ•˜๊ฑฐ๋‚˜ ์ „๋‹ฌ๋ฐ›์€ ๋นˆ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
5. ๋ฐ˜ํ™˜๋œ ๋นˆ ๊ฐ์ฒด๋ฅผ ์ปจํ…์ŠคํŠธ์— ๋“ฑ๋ก

์Šคํ”„๋ง AOP์—์„œ ์˜๋ฏธํ•˜๋Š” ํ”„๋ก์‹œ(proxy)๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ด๋‹ค.

  1. ๋นˆ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ๋’ค ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.

  2. ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ๋Š” ์–ด๋“œ๋ฐ”์ด์ €๋ฅผ ์‹น ํ›‘์–ด๋ณธ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์–ด๋“œ๋ฐ”์ด์ € ๋‚ด์˜ ํฌ์ธํŠธ ์ปท(Pointcut)๋ฅผ ์ด์šฉํ•ด ์ „๋‹ฌ๋ฐ›์€ ๋นˆ์ด ํ”„๋ก์‹œ ์ ์šฉ ๋Œ€์ƒ์ธ์ง€ ํ™•์ธํ•œ๋‹ค.

  3. ์ ์šฉ ๋Œ€์ƒ์ธ์ง€ ํ™•์ธํ•˜๋ฉด '์•„ ์–˜๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ• ๋งŒํ•œ ๋Œ€์ƒ์ด์•ผ' ๋ผ๊ณ  ํŒ๋‹จ๋˜๋ฉด ํ”„๋ก์‹œ ์ƒ์„ฑ๊ธฐ์— ์ „๋‹ฌํ•ด์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ”„๋ก์‹œ ์ƒ์„ฑ ๋Œ€์ƒ ๋นˆ๋“ค์„ ๋Œ€์ƒ์œผ๋กœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

  4. ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•œ ๋นˆ์ด๋ผ๋ฉด ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ, ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š์€ ๋นˆ์ด๋ผ๋ฉด ๊ทธ๋ƒฅ ๋นˆ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

  5. ๋นˆ ํ›„์ฒ˜๋ฆฌ๊ธฐ์— ์ „๋‹ฌ๋ฐ›์€ ๊ฐ์ฒด๋ฅผ ์ปจํ…Œ์ด๋„ˆ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค.

์Šคํ”„๋ง AOP ๊ตฌํ˜„ ์˜ˆ์‹œ

์Šคํ”„๋ง AOP๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹์€ ์ข€ ๋‹ค์–‘ํ•˜์ง€๋งŒ ๋‚˜๋Š” ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ณ  ์ฑ…์—์„œ๋„ ๊ถŒ์žฅํ•˜๋Š” ๋ฐฉ์‹์ธ @AspectJ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ฒ ๋‹ค.

1. ์˜์กด์„ฑ ์ถ”๊ฐ€

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-aop'
}

2. ํฌ์ธํŠธ์ปท๊ณผ ํ‘œํ˜„์‹ & ์ง€์‹œ์ž

AspectJ๋Š” ํฌ์ธํŠธ์ปท์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํŠน๋ณ„ํ•œ ํ‘œํ˜„์‹์„ ์ œ๊ณตํ•œ๋‹ค.

Spring AOP ์ฃผ์š” ํฌ์ธํŠธ์ปท ์ข…๋ฅ˜

  • execution

    • ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ง€์ ์„ ๋งค์นญ
    • ์Šคํ”„๋ง AOP์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋ฉฐ, ๋งค์šฐ ์œ ์—ฐํ•˜๊ณ  ๋ณต์žกํ•œ ํ‘œํ˜„ ๊ฐ€๋Šฅ
  • within

    • ํŠน์ • ํด๋ž˜์Šค๋‚˜ ํƒ€์ž… ์•ˆ์˜ ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ๋ฅผ ๋งค์นญ
  • args

    • ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๊ฐ€ ์ง€์ •ํ•œ ํƒ€์ž…์ผ ๋•Œ ๋งค์นญ
  • this

    • Spring AOP ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๋งค์นญ
  • target

    • ํ”„๋ก์‹œ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์‹ค์ œ ๊ฐ์ฒด(Target)๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๋งค์นญ
  • @target

    • ์‹คํ–‰ ๊ฐ์ฒด ํด๋ž˜์Šค์— ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ์„ ๋•Œ ๋งค์นญ
  • @within

    • ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํด๋ž˜์Šค ๋‚ด๋ถ€์˜ ์กฐ์ธ ํฌ์ธํŠธ๋ฅผ ๋งค์นญ
  • @annotation

    • ๋ฉ”์„œ๋“œ์— ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๊ฒฝ์šฐ ๋งค์นญ
  • @args

    • ์ „๋‹ฌ๋œ ์‹ค์ œ ์ธ์ˆ˜์˜ ๋Ÿฐํƒ€์ž„ ํƒ€์ž…์— ํŠน์ • ์–ด๋…ธํ…Œ์ด์…˜์ด ์žˆ์„ ๋•Œ ๋งค์นญ
  • bean

    • Spring ์ „์šฉ ํฌ์ธํŠธ์ปท
    • ๋นˆ ์ด๋ฆ„์œผ๋กœ ํฌ์ธํŠธ์ปท์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Œ

ํฌ์ธํŠธ์ปท์€ โ€œAdvice๋ฅผ ์–ด๋””์— ์ ์šฉํ• ์ง€ ์ง€์ •ํ•˜๋Š” ๊ธฐ์ค€โ€์ด๊ณ ,
execution, within, args, annotation ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋Œ€์ƒ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” execution ์„ ์‚ดํŽด๋ณด์ž

execution(์ ‘๊ทผ์ œ์–ด์ž? ๋ฐ˜ํ™˜ํƒ€์ž… ํŒจํ‚ค์ง€.ํด๋ž˜์Šค.๋ฉ”์„œ๋“œ(ํŒŒ๋ผ๋ฏธํ„ฐ) ์˜ˆ์™ธ?)
  • ? ํ‘œ์‹œ๋Š” ์ƒ๋žต ๊ฐ€๋Šฅ
  • * ๋Š” ๋ชจ๋“  ๊ฐ’์„ ์˜๋ฏธ
  • .. ๋Š” 0๊ฐœ ์ด์ƒ์„ ์˜๋ฏธ
// 1. ๋ชจ๋“  public ๋ฉ”์„œ๋“œ
execution(public * *(..))

// 2. ๋ฐ˜ํ™˜ ํƒ€์ž…์ด void์ธ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(void *(..))

// 3. ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด set์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(* set*(..))

// 4. service ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(* com.example.service.*.*(..))

// 5. service ํŒจํ‚ค์ง€์™€ ํ•˜์œ„ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(* com.example.service..*.*(..))

// 6. MemberService ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(* com.example.service.MemberService.*(..))

// 7. ์ •ํ™•ํžˆ int minus(int, int) ๋ฉ”์„œ๋“œ๋งŒ
// ๋ฐ˜ํ™˜ ํƒ€์ž…์€ int ์ด๊ณ , ๋‘ ๊ฐœ์˜ int ํƒ€์ž… ํŒŒ๋ฆฌ๋ฏธํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š” minus๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฉ”์„œ๋“œ
execution(int minus(int, int))

// ๋ฐ˜ํ™˜ ํƒ€์ž…๊ณผ๋Š” ์ƒ๊ด€ ์—†๊ณ , ๋‘ ๊ฐœ์˜ int ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›๋Š” minus๋ผ๋Š” ์ด๋ฆ„์˜ ๋ฉ”์„œ๋“œ
execution(* minus(int, int)

// 8. ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ String ํ•˜๋‚˜์ธ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(* *(String))

// 9. ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ String์ธ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(* *(String, ..))

// 10. Member ํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ
execution(com.example.domain.Member *(..))

AspectJ ์–ด๋“œ๋ฐ”์ด์Šค

(1) After: ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ฑฐ๋‚˜ ์˜ˆ์™ธ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•œ ์ดํ›„์— ํ˜ธ์ถœ

(2) AfterReturning: ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋œ ์ดํ›„์— ํ˜ธ์ถœ

(3) AfterThrowing: ๋ฉ”์†Œ๋“œ๊ฐ€ ์˜ˆ์™ธ ์ƒํ™ฉ์„ ๋ฐœ์ƒ์‹œํ‚จ ์ดํ›„์— ํ˜ธ์ถœ

(4) Before: ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ด์ „์— ํ˜ธ์ถœ

(5) Around: ๋ฉ”์†Œ๋“œ์˜ ํ˜ธ์ถœ ์ „๊ณผ ๋ฐ˜ํ™˜๋˜๊ฑฐ๋‚˜ ์˜ˆ์™ธ ์ƒํ™ฉ ์ดํ›„์— ํ˜ธ์ถœ

3. ์–ด๋“œ๋ฐ”์ด์ €๋ฅผ ์ƒ์„ฑ , ์ •์˜

  • @AspectJ ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด ์–ด๋“œ๋ฐ”์ด์ €๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์ด๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผํ•œ๋‹ค.

  • ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ •์˜ํ•œ ํด๋ž˜์Šค๋ฅผ ์ธ์ž ํƒ€์ž…์œผ๋กœ ๋ฐ›์„ ๋•Œ๋Š” ํŒจํ‚ค์ง€๋ช…๋„ ์ถ”๊ฐ€ํ•ด์•ผํ•œ๋‹ค.

@Aspect // ์–ด๋“œ๋ฐ”์ด์ € ์ƒ์„ฑ
@Component // ๋นˆ ๋“ฑ๋ก
public class CartExecutionTimeAspect {

    // ํฌ์ธํŠธ์ปท ์„ค์ •
    @Pointcut("execution(void putItemIntoCart(Long, cart.dto.AuthorizationInformation))")
    public void pointcut() {}

    // ์–ด๋“œ๋ฐ”์ด์Šค ์ •์˜ + ํฌ์ธํŠธ์ปท ์„ค์ •
    @Around("pointcut()")
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long start = System.currentTimeMillis(); // ์‹œ์ž‘ ์‹œ๊ฐ„ ์ฒดํฌ

        Object result = proceedingJoinPoint.proceed(); // ํƒ€๊นƒ ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ

        long end = System.currentTimeMillis(); // ๋๋‚˜๋Š” ์‹œ๊ฐ„ ์ฒดํฌ
        System.out.printf("%s ๋ฉ”์†Œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„: %d ๋ฐ€๋ฆฌ ์ดˆ%n",
                proceedingJoinPoint.getSignature().getName(), (end - start)); // ์‹คํ–‰์‹œ๊ฐ„ ์ถœ๋ ฅ

        return result; // ํƒ€๊นƒ ๋ฉ”์†Œ๋“œ์˜ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜
    }
}

4. Configuration์— @EnableAspectJAutoProxy๋ฅผ ์ถ”๊ฐ€

@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
}
putItemIntoCart ๋ฉ”์†Œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„: 153 ๋ฐ€๋ฆฌ ์ดˆ

์ฐธ๊ณ  :

profile
๋…ธ๋ ฅ์€ ๋ฐฐ์‹ ํ•˜์ง€ ์•Š์•„ ๐Ÿ”ฅ

0๊ฐœ์˜ ๋Œ“๊ธ€