λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
Spring

Spring AOP - (2) AOP κ°œλ… 및 μ‹€μŠ΅

by 주발2 2021. 9. 24.
λ°˜μ‘ν˜•

πŸ“Ž  κΈ€λ˜ 6κΈ° ν¬μŠ€νŒ…

1. λ―ΈμΉ˜λ„λ‘ λ”μ› λ˜ 7μ›”μ˜ 회고

 

2. μ‚¬μš©μžκ°€ κ²Œμ‹œλ¬Όμ„ μž‘μ„±ν•  λ•Œμ˜ νŠΈλžœμž­μ…˜ 처리

 

3. Spring AOP - (1) ν”„λ‘μ‹œ νŒ¨ν„΄, λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄

 

4. [MySQL] - νŠΈλžœμž­μ…˜μ˜ κ²©λ¦¬ μˆ˜μ€€(Isolation level)

 

5. Spring AOP - (2) AOP κ°œλ… 및 μ‹€μŠ΅

 

6. μΈν…”λ¦¬μ œμ΄(IntelliJ) - 디버깅(Debugging) ν•˜κΈ°

 

7. [Java, λ””μžμΈνŒ¨ν„΄] - μ‹±κΈ€ν„΄ νŒ¨ν„΄(Singleton Pattern)

 

8. μ›”κ°„ μ½”λ“œλ¦¬λ·° Ver_0.1: 컀리어 μ„±μž₯ CODE μ„Έλ―Έλ‚˜ 정리

 

9. 포슀트맨(API ν…ŒμŠ€νŠΈ) ν™œμš©ν•˜κΈ°

 

10. λœ»κΉŠμ€ 2021λ…„ 회고


πŸ“Ž  Spring AOP(Aspect Oriented Programming)

ν¬μŠ€νŒ…μ— μž‘μ„±ν•œ 예제 μ½”λ“œλŠ” κΉƒν—ˆλΈŒμ—μ„œ ν™•μΈν•˜μ‹€ 수 μžˆμŠ΅λ‹ˆλ‹€ :)

 

μ§€λ‚œλ²ˆ μ •λ¦¬ν•œ Spring AOP - (1) ν”„λ‘μ‹œ νŒ¨ν„΄, λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ ν¬μŠ€νŒ…μ— 이어 λ‚˜λ¨Έμ§€ λͺ©μ°¨λ“€μ— λŒ€ν•΄ 정리λ₯Ό ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

ν¬μŠ€νŒ…μ˜ λͺ©μ°¨ μž…λ‹ˆλ‹€.

  • λΆ€κ°€κΈ°λŠ₯을 μœ„μž„ν•˜λŠ” 전톡적인 방법 1 - ν”„λ‘μ‹œ νŒ¨ν„΄
  • λΆ€κ°€κΈ°λŠ₯을 μœ„μž„ν•˜λŠ” 전톡적인 방법 2 - λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄
  • Spring AOP κ°œλ…
  • Spring AOP νŠΉμ§•
  • Spring AOP μš©μ–΄
  • Spring AOP μ‹€μŠ΅ 1
  • Spring AOP μ–΄λ…Έν…Œμ΄μ…˜
  • Spring AOP μ‹€μŠ΅ 2
  • 마무리

μ§€λ‚œ ν¬μŠ€νŒ…μ—μ„œλŠ” λΆ€κ°€κΈ°λŠ₯을 μœ„μž„ν•˜λŠ” 전톡적인 λ°©λ²•μœΌλ‘œ ν”„λ‘μ‹œ νŒ¨ν„΄ 및 λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ— λŒ€ν•΄ 정리λ₯Ό ν–ˆμ—ˆμŠ΅λ‹ˆλ‹€.

이번 μ‹œκ°„μ—λŠ” λΆ€κ°€κΈ°λŠ₯을 μ²˜λ¦¬ν•˜λŠ” λ°©λ²•μœΌλ‘œ Spring AOP의 κ°œλ… 및 μš©μ–΄, μ‹€μŠ΅ 등에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

 

 

🎯  Spring AOP κ°œλ…


AOPλŠ” Aspect Oriented Programming의 μ•½μžλ‘œ κ΄€μ  지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ΄λΌκ³ λ„ ν•©λ‹ˆλ‹€.

관점은 μ½”λ“œλ₯Ό λ°”λΌλ³΄λŠ” μ‹œκ°μ •λ„λ‘œ μƒκ°ν•΄μ£Όμ‹œλ©΄ 될 것 κ°™μŠ΅λ‹ˆλ‹€.

 

관점 지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ€ κΈ°λŠ₯을 κ΅¬ν˜„ν•  λ•Œ ν•΅μ‹¬μ μΈ κΈ°λŠ₯κ³Ό 뢀가적인 κΈ°λŠ₯을 λΆ„λ¦¬ν•˜μ—¬ κ° 관점(Aspect)을 κΈ°μ€€μœΌλ‘œ λͺ¨λ“ˆν™”λ₯Ό ν•˜λŠ” 방식을 μ˜λ―Έν•©λ‹ˆλ‹€.

  • 핡심적인 κΈ°λŠ₯: μ μš©ν•˜κ³ μž ν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
  • 뢀가적인 κΈ°λŠ₯: λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 및 νŠΈλžœμž­μ…˜ 관리, λ‘œκΉ…, 파일 μž…μΆœλ ₯ λ“±

Spring AOP

AOPμ—μ„œ 각 관점을 κΈ°μ€€μœΌλ‘œ λͺ¨λ“ˆν™”λ₯Ό ν•œλ‹€λŠ” 것은 μ½”λ“œλ“€μ„ λΆ€λΆ„μ μœΌλ‘œ λ‚˜λˆ„μ–΄μ„œ λͺ¨λ“ˆν™”λ₯Ό ν•œλ‹€λŠ” μ˜λ―Έμž…λ‹ˆλ‹€.

μœ„ 사진과 같이 μ½”λ“œμ—μ„œ λ‹€λ₯Έ 뢀뢄에 반볡적으둜 μ‚¬μš©λ˜λŠ” μ½”λ“œλ“€μ„ λ°œκ²¬ν•  수 μžˆλŠ”λ°

이λ₯Ό νš‘단 관심사(Crosscutting Concerns)라고 ν•©λ‹ˆλ‹€.

 

μœ„ κ·Έλ¦Όμ—μ„œ λͺ¨λ“  ν΄λž˜μŠ€μ— κ³΅ν†΅μ μœΌλ‘œ μ‚¬μš©λ˜κ³  μžˆλŠ” 주황색 κΈ°λŠ₯을 μˆ˜μ •ν•΄μ•Ό ν•˜λŠ” 일이 λ°œμƒν•˜λ©΄ A, B, C λͺ¨λ“  ν΄λž˜μŠ€μ—μ„œ κΈ°λŠ₯을 μˆ˜μ •ν•΄μ£Όμ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— μœ μ§€λ³΄μˆ˜λ©΄μ—μ„œλ„ νš¨μœ¨μ μ΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

 

μ΄λŸ¬ν•œ λ¬Έμ œμ μ„ ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ‚˜μ˜¨ 기술이 AOP둜, μ •λ¦¬ν•΄λ³΄μžλ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

'νš‘λ‹¨ 관심사(Crosscutting Concerns)의 뢄리λ₯Ό ν—ˆμš©ν•¨μœΌλ‘œμ¨ λͺ¨λ“ˆμ„±μ„ μ¦κ°€μ‹œν‚€λŠ” 것이 λͺ©μ μΈ ν”„λ‘œκ·Έλž˜λ° νŒ¨λŸ¬λ‹€μž„'

AOPλŠ” κ²°κ΅­ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ‹€μ–‘ν•œ μΈ‘λ©΄μ—μ„œ λ…λ¦½μ μœΌλ‘œ λͺ¨λΈλ§ν•˜κ³ , μ„€κ³„ν•˜κ³ , κ°œλ°œν•  수 μžˆλ„λ‘ λ§Œλ“€μ–΄ μ£Όκ³ , κ·Έλ‘œμΈν•΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ λ‹€μ–‘ν•œ κ΄€μ μ—μ„œ 바라보며 κ°œλ°œν•  수 있게 λ„μ™€μ€λ‹ˆλ‹€.

 

 

μ•„λž˜λŠ” 은행 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΄€μ‹¬μ‚¬λΌλŠ” κ΄€μ μœΌλ‘œ ν‘œν˜„ν•œ κ΅¬μ‘°μž…λ‹ˆλ‹€.

μœ„ 은행 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ κ΅¬μ‘°λŠ” 각 ν•΅μ‹¬ κΈ°λŠ₯λ“€(κ³„μ’Œμ΄μ²΄, μž…μΆœκΈˆ, μ΄μžκ³„μ‚°)이 μˆ˜ν–‰λ  λ•Œ λ§ˆλ‹€ λΆ€κ°€μ μΈ κΈ°λŠ₯λ“€(λ‘œκΉ…, νŠΈλžœμž­μ…˜, λ³΄μ•ˆ)이 이루어져야 ν•©λ‹ˆλ‹€. 즉, κ³„μ’Œμ΄μ²΄μ˜ κ΄€μ μ—μ„œλ„ λ‘œκΉ…, νŠΈλžœμž­μ…˜, λ³΄μ•ˆμ„ μˆ˜ν–‰ν•΄μ•Ό ν•˜κ³  μž…μΆœκΈˆ, μ΄μžκ³„μ‚°μ˜ κ΄€μ μ—μ„œλ„ λ™μΌν•˜κ²Œ λ‘œκΉ…, νŠΈλžœμž­μ…˜, λ³΄μ•ˆ κΈ°λŠ₯을 μˆ˜ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€.

 

μœ„ κ΅¬μ‘°μ—μ„œ 핡심 κΈ°λŠ₯듀을 μˆ˜ν–‰ν•˜λŠ”λ° λ°˜λ“œμ‹œ μ‹€ν–‰ν•˜λŠ” 곡톡적인(뢀가적인) κΈ°λŠ₯듀이 λ‘œκΉ…, νŠΈλžœμž­μ…˜, λ³΄μ•ˆ μž…λ‹ˆλ‹€. μ΄λŸ¬ν•œ 것듀을 νš‘λ‹¨κ΄€μ‹¬μ΄λΌκ³  ν•˜λŠ”λ°, μ™œ μ΄λŸ¬ν•œ λΆ€κ°€ κΈ°λŠ₯을 뢄리해야 ν•˜λŠ”κ±ΈκΉŒμš”? λ‹Ήμ—°ν•˜κ²Œλ„, 쀑볡이 λ°œμƒν•œλ‹€λŠ” 문제점과 μœ μ§€λ³΄μˆ˜μ˜ 어렀움이 μžˆμŠ΅λ‹ˆλ‹€.

 

κ°„λ‹¨ν•œ μŠˆλ„μ½”λ“œ(pseudocode)λ₯Ό 톡해 λ¬Έμ œμ μ— λŒ€ν•΄ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

class 둜그 {
  static printLog();
}

class κ³„μ’Œμ΄μ²΄ {
  이체() {
    둜그.printLog();
    ...
  }
}

class μž…μΆœκΈˆ {
  μž…κΈˆ() {
    둜그.printLog();
    ...
  }
}

class μ΄μžκ³„μ‚° {
  μ΄μžκ³„μ‚°() {
    둜그.printLog();
    ...
  }
}

μœ„ μ˜ˆμ œλŠ” 정말 λ‹¨μˆœν•˜κ²Œ μž‘μ„±ν–ˆκΈ°λ•Œλ¬Έμ— λΆˆνŽΈν•¨μ„ λŠλΌμ§€ λͺ»ν• μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ μœ„ μ˜ˆμ œμ—μ„œ 둜그λ₯Ό 좜λ ₯ν•˜λŠ” printLog()의 λ©”μ†Œλ“œλͺ…이 λ³€κ²½λ˜μ—ˆλ‹€λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒμš”?

 

λ‹€μŒκ³Όκ°™μ΄ 둜그λ₯Ό μ‚¬μš©ν•˜κ³  μžˆλŠ” λͺ¨λ“  클래슀 및 λ©”μ†Œλ“œλ₯Ό μ°Ύμ•„κ°€ λ³€κ²½ν•΄μ•Ό ν•˜λŠ” 일이 λ°œμƒν•©λ‹ˆλ‹€.

class 둜그 {
  static printLog4j();
}

class κ³„μ’Œμ΄μ²΄ {
  이체() {
    둜그.printLog4j();
    ...
  }
}

class μž…μΆœκΈˆ {
  μž…κΈˆ() {
    둜그.printLog4j();
    ...
  }
}

class μ΄μžκ³„μ‚° {
  μ΄μžκ³„μ‚°() {
    둜그.printLog4j();
    ...
  }
}

ν˜„μž¬λŠ” 둜그 κΈ°λŠ₯을 μ‚¬μš©ν•˜κ³  μžˆλŠ” ν΄λž˜μŠ€κ°€ 3κ°œλ°–μ— μ‘΄μž¬ν•˜μ§€ μ•Šμ§€λ§Œ, λ§Œμ•½ 둜그 κΈ°λŠ₯을 μ‚¬μš©ν•˜κ³  μžˆλŠ” ν΄λž˜μŠ€κ°€ 점차 λŠ˜μ–΄λ‚˜ 3κ°œκ°€ μ•„λ‹Œ 30개, 100κ°œκ°€ λ˜μ—ˆμ„λ•Œ μœ„μ™€ 같이 μˆ˜μ •μ‚¬ν•­μ΄ λ°œμƒν•˜λ©΄ λͺ¨λ“  ν΄λž˜μŠ€μ— 직접 μˆ˜μ •μ„ ν•΄μ•Όν•˜λŠ” λ²ˆκ±°λ‘œμ›€μ΄ λ°œμƒν•©λ‹ˆλ‹€.

λ”°λΌμ„œ μŠ€ν”„λ§ AOPλ₯Ό 톡해 관심사λ₯Ό λΆ„λ¦¬ν•˜κ³  μ΄λŸ¬ν•œ λ¬Έμ œμ μ„ 해결해보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

 

🎯 Spring AOP νŠΉμ§•


  • AOP(Aspect Oriented Programming)λŠ” ν”„λ‘œκ·Έλž¨ ꡬ쑰에 λŒ€ν•œ λ˜ λ‹€λ₯Έ 사고방식을 μ œκ³΅ν•˜μ—¬ OOPλ₯Ό λ³΄μ™„ν•©λ‹ˆλ‹€.
  • OOPμ—μ„œ λͺ¨λ“ˆν™”μ˜ 핡심 λ‹¨μœ„λŠ” ν΄λž˜μŠ€(μ—­ν• )이고, AOPμ—μ„œλŠ” λͺ¨λ“ˆν™” λ‹¨μœ„κ°€ Aspect(관점)μž…λ‹ˆλ‹€.
  • AspectλŠ” μ—¬λŸ¬ μœ ν˜•κ³Ό 객체에 κ±Έμ³μžˆλŠ” λ™μΌν•œ 관심사(ex. νŠΈλžœμž­μ…˜)의 λͺ¨λ“ˆμ„ κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.
  • μŠ€ν”„λ§ λΉˆμ—λ§Œ AOPλ₯Ό μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

🎯 Spring AOP μš©μ–΄


AOPμ—μ„œ μ‚¬μš©λ˜κ³  μžˆλŠ” μš©μ–΄λ“€μ— λŒ€ν•΄ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. μ•„λž˜ μš©μ–΄λ“€μ€ Springμ—λ§Œ κ΅­ν•œλ˜λŠ” 것이 μ•„λ‹Œ AOP ν”„λ ˆμž„μ›Œν¬ μ „μ²΄μ—μ„œ μ‚¬μš©λ˜λŠ” κ³΅μš©μ–΄μž…λ‹ˆλ‹€.

AOP의 μš©μ–΄λŠ” 단어 μžμ²΄λ‘œλ„ ꡉμž₯히 μ–΄λ ΅κ³  머리가 μ•„ν”„κΈ° λ•Œλ¬Έμ— ..πŸ˜‚.. μ „λΆ€ μ™Έμš°κ³  μ΄ν•΄ν•˜λ €κΈ° λ³΄λ‹€λŠ” κ°„λ‹¨ν•˜κ²Œ μš©μ–΄μ •λ„λ§Œ 읽어본 ν›„ μ•„λž˜ μ‹€μŠ΅μ„ 톡해 AOP에 λŒ€ν•΄ μ‚΄νŽ΄λ³΄κ³  λ‹€μ‹œ μ½μ–΄λ³΄λŠ” 것을 μΆ”μ²œλ“œλ¦½λ‹ˆλ‹€.

 

νƒ€κ²Ÿ(Target)

  • λΆ€κ°€κΈ°λŠ₯을 λΆ€μ—¬ν•  λŒ€μƒμ„ μ˜λ―Έν•©λ‹ˆλ‹€. 
  • μœ„ κ΅¬μ‘°μ—μ„œλŠ” 핡심기λŠ₯(κ³„μ’Œμ΄μ²΄, μž…μΆœκΈˆ, μ΄μžκ³„μ‚°)을 담은 클래슀λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

μ• μŠ€νŽ™νŠΈ(Aspect)

  • OOP의 ν΄λž˜μŠ€μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ μ• μŠ€νŽ™νŠΈλŠ” AOP의 κΈ°λ³Έ λͺ¨λ“ˆμž…λ‹ˆλ‹€.
  • μ• μŠ€νŽ™νŠΈλŠ” λΆ€κ°€κΈ°λŠ₯을 μ •μ˜ν•œ μ–΄λ“œλ°”μ΄μŠ€μ™€ μ–΄λ“œλ°”μ΄μŠ€λ₯Ό 어디에 μ μš©ν• μ§€ κ²°μ •ν•˜λŠ” ν¬μΈνŠΈμ»·μ˜ μ‘°ν•©μœΌλ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€.
  • 보톡 싱글톀 ν˜•νƒœμ˜ 였브젝트둜 μ‘΄μž¬ν•©λ‹ˆλ‹€.

μ–΄λ“œλ°”μ΄μŠ€(Advice)

  • μ‹€μ§ˆμ μœΌλ‘œ λΆ€κ°€κΈ°λŠ₯을 담은 κ΅¬ν˜„μ²΄λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.
  • μ–΄λ“œλ°”μ΄μŠ€μ˜ 경우 νƒ€κ²Ÿ μ˜€λΈŒμ νŠΈμ— μ’…μ†λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μˆœμˆ˜ν•˜κ²Œ λΆ€κ°€κΈ°λŠ₯μ—λ§Œ 집쀑할 수 μžˆμŠ΅λ‹ˆλ‹€.

쑰인 포인트(Join Point)

  • μ–΄λ“œλ°”μ΄μŠ€κ°€ 적용될 수 μžˆλŠ” μœ„μΉ˜λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.
  • μŠ€ν”„λ§ AOPμ—μ„œλŠ” λ©”μ†Œλ“œ 쑰인포인트만 μ œκ³΅ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • λ”°λΌμ„œ, μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬ λ‚΄μ—μ„œμ˜ μ‘°μΈν¬μΈνŠΈλŠ” λ©”μ†Œλ“œλ₯Ό 가리킨닀고 μƒκ°ν•˜μ…”λ„ λ¬΄λ°©ν•©λ‹ˆλ‹€.

포인트컷(PointCut)

  • λΆ€κ°€κΈ°λŠ₯이 적용될 λŒ€μƒ(λ©”μ†Œλ“œ)λ₯Ό μ„ μ •ν•˜λŠ” 방법을 μ˜λ―Έν•©λ‹ˆλ‹€.
  • μŠ€ν”„λ§ AOP의 쑰인 ν¬μΈνŠΈλŠ” λ©”μ†Œλ“œμ˜ μ‹€ν–‰μ΄λ―€λ‘œ μŠ€ν”„λ§μ˜ ν¬μΈνŠΈμ»·μ€ λ©”μ†Œλ“œλ₯Ό μ„ μ •ν•˜λŠ” κΈ°λŠ₯을 κ°–κ³  μžˆμŠ΅λ‹ˆλ‹€.
  • λ”°λΌμ„œ 포인트컷의 ν‘œν˜„μ‹μ€ λ©”μ†Œλ“œμ˜ μ‹€ν–‰μ΄λΌλŠ” 의미인 execution으둜 μ‹œμž‘ν•˜κ³ , λ©”μ†Œλ“œμ˜ μ‹œκ·Έλ‹ˆμ²˜λ₯Ό λΉ„κ΅ν•˜λŠ” 방법을 주둜 μ΄μš©ν•©λ‹ˆλ‹€.

ν”„λ‘μ‹œ(Proxy)

  • ν΄λΌμ΄μ–ΈνŠΈμ™€ νƒ€κ²Ÿ 사이에 투λͺ…ν•˜κ²Œ μ‘΄μž¬ν•˜μ—¬ λΆ€κ°€κΈ°λŠ₯을 μ œκ³΅ν•˜λŠ” μ˜€λΈŒμ νŠΈμž…λ‹ˆλ‹€.
  • DIλ₯Ό 톡해 νƒ€κ²Ÿ λŒ€μ‹  ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ£Όμž…λ˜λ©°, ν΄λΌμ΄μ–ΈνŠΈμ˜ λ©”μ†Œλ“œ ν˜ΈμΆœμ„ λŒ€μ‹  λ°›μ•„ νƒ€κ²Ÿμ— μœ„μž„ν•΄μ£Όλ©° λΆ€κ°€κΈ°λŠ₯을 λΆ€μ—¬ν•©λ‹ˆλ‹€.

μΈνŠΈλ‘œλ•μ…˜(Introduction)

  • νƒ€κ²Ÿ ν΄λž˜μŠ€μ— 좔가적인 λ©”μ†Œλ“œλ‚˜ ν•„λ“œλ₯Ό μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯을 μ˜λ―Έν•©λ‹ˆλ‹€.

μœ„λΉ™(Weaving)

  • μ–΄λ“œλ°”μ΄μŠ€λ₯Ό 핡심 둜직 μ½”λ“œμ— μ μš©ν•˜λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.

 

 

 

🎯 Spring AOP μ‹€μŠ΅ 1


μŠ€ν”„λ§ AOPλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•©λ‹ˆλ‹€.

    // Maven
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    
    // Gradle
    implementation 'org.springframework.boot:spring-boot-starter-aop'

 

μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•œ ν›„ μ•„λž˜ μ‹€μŠ΅ μ½”λ“œλ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

 

Controller & Dto

package com.juhyun.springaop.aop.controller;

import ...

@RestController
@RequestMapping("/api")
public class RestApiController {

    @GetMapping("/{id}")
    public String get(@PathVariable Long id, @RequestParam String name) {
        System.out.println("--------------- @GetMapping ---------------");
//        System.out.println("get method");
//        System.out.println("id: " + id);
//        System.out.println("name: " + name);
        return id + " " + name;
    }

    @PostMapping
    public UserDto post(@RequestBody UserDto userDto) {
        System.out.println("--------------- @PostMapping ---------------");
//        System.out.println("post method");
//        System.out.println("user: " + user);
        return userDto;
    }
}


package com.juhyun.springaop.aop.dto;

import ...

@Getter
@Setter
@ToString
public class UserDto {

    private String id;
    private String password;
    private String email;

}

 

AOP

package com.juhyun.springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class ParameterAop {

    /*
    com.juhyun.springaop.aop.controller νŒ¨ν‚€μ§€ ν•˜μœ„μ— μžˆλŠ” λ©”μ†Œλ“œκ°€ μ‹€ν–‰λ˜κΈ° μ „
     -> @Before μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ„ μ–Έλœ before() λ©”μ†Œλ“œκ°€ μ‹€ν–‰
     -> 정상 μ‹€ν–‰λ˜κ³  λ¦¬ν„΄λ˜λ©΄ @AfterReturning μ–΄λ…Έν…Œμ΄μ…˜μ΄ μ„ μ–Έλœ afterReturn() λ©”μ†Œλ“œμ—μ„œ κ²°κ³Όλ₯Ό 확인
    */

    // controller νŒ¨ν‚€μ§€μ˜ ν•˜μœ„ λ©”μ†Œλ“œλ₯Ό λͺ¨λ‘ Aspect둜 μ„€μ •
    @Pointcut("execution(* com.juhyun.springaop.aop.controller..*.*(..))")
    private void cut() {

    }

    // cut() λ©”μ†Œλ“œκ°€ μ‹€ν–‰λ˜λŠ” μ§€μ μ˜ 이전에 before() λ©”μ†Œλ“œ μˆ˜ν–‰
    @Before("cut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("--------------- @Before ---------------");
        System.out.println(joinPoint.toString());
        System.out.println(joinPoint.toShortString());
        System.out.println(joinPoint.toLongString());

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        System.out.println("method: " + method.getName());

        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println("type: " + arg.getClass().getSimpleName());
            System.out.println("value: " + arg);
        }
    }

    // λ°˜ν™˜κ°’ 확인
    @AfterReturning(value = "cut()", returning = "object")
    public void afterReturn(JoinPoint joinPoint, Object object) {
        System.out.println("--------------- @After ---------------");
        System.out.println(object);
    }

}

 

ν”„λ‘œμ νŠΈμ˜ κ΅¬μ‘°λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

 

컨트둀러의 Get, Post λ©”μ†Œλ“œμ— λŒ€ν•΄ ν…ŒμŠ€νŠΈλ₯Ό 해보면 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

GET

 

POST

Controller μ½”λ“œμ— λΆ€κ°€κΈ°λŠ₯으둜 μ£Όμ„μ²˜λ¦¬λœ System.out.println() 좜λ ₯ν•˜λŠ” 뢀뢄을 AOP둜 λΆ„λ¦¬ν•¨μœΌλ‘œμ¨ μ»¨νŠΈλ‘€λŸ¬λŠ” λΆ€κ°€κΈ°λŠ₯(좜λ ₯)을 ν•  ν•„μš”κ°€ 없이 본인의 κΈ°λŠ₯만 μˆ˜ν–‰ν•˜λ©΄ λ©λ‹ˆλ‹€.

 

 

 

🎯 Spring AOP μ–΄λ…Έν…Œμ΄μ…˜


μœ„ μ˜ˆμ œμ—μ„œ μ‚¬μš©λœ μ—¬λŸ¬ μ–΄λ…Έν…Œμ΄μ…˜(@Aspect, @Component, @Pointcut, @Before, @After) 등등에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

@Aspect, @Component

ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ Aspectλ₯Ό λ‚˜νƒ€λ‚΄λŠ” 클래슀λ₯Ό λͺ…μ‹œν•˜κ³ , μŠ€ν”„λ§ 빈으둜 λ“±λ‘ν•˜κΈ° μœ„ν•΄ @Component μ–΄λ…Έν…Œμ΄μ…˜μ„ λͺ…μ‹œν•©λ‹ˆλ‹€.

 

@Pointcut(포인트컷 ν‘œν˜„μ‹)

ν¬μΈνŠΈμ»·μ€ μœ„μ—μ„œ λΆ€κ°€κΈ°λŠ₯이 적용될 λŒ€μƒ(λ©”μ†Œλ“œ)λ₯Ό μ„ μ •ν•˜λŠ” 방법이라고 μ„€λͺ…을 λ“œλ Έμ—ˆλŠ”λ°μš”, 포인트컷 ν‘œν˜„μ‹μ˜ λ¬Έλ²•κ΅¬μ‘°λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

[] κ΄„ν˜ΈλŠ” μ˜΅μ…˜μ΄κΈ° λ•Œλ¬Έμ— μƒλž΅μ΄ κ°€λŠ₯ν•˜λ©°, | λŠ” OR μ‘°κ±΄μž…λ‹ˆλ‹€.

포인트컷 ν‘œν˜„μ‹ 문법ꡬ쑰

 

μœ„ μ˜ˆμ œμ—μ„œ μž‘μ„±ν•œ ν¬μΈνŠΈμ»·μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

// controller νŒ¨ν‚€μ§€μ˜ ν•˜μœ„ λ©”μ†Œλ“œλ₯Ό λͺ¨λ‘ Aspect둜 μ„€μ •
@Pointcut("execution(* com.juhyun.springaop.aop.controller..*.*(..))")

μœ„ 문법을 ν•΄μ„ν•΄λ³΄μžλ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • execution: 포인트컷 μ§€μ •μž
  • *: λͺ¨λ“  νƒ€μž…μ˜ 리턴이 κ°€λŠ₯
  • com.~.controller..*.*: (νƒ€κ²Ÿμ΄ λ˜λŠ” λ©”μ†Œλ“œ) ν•΄λ‹Ή νŒ¨ν‚€μ§€ ν•˜μœ„μ— μ‘΄μž¬ν•˜λŠ” λͺ¨λ“  λ©”μ†Œλ“œλ“€
  • (..): λͺ¨λ“  νƒ€μž…μ˜ νŒŒλΌλ―Έν„°μ— λŒ€ν•΄ ν—ˆμš©ν•˜λŠ” 쑰건

 

λ§Œμ•½ μœ„ μ˜ˆμ œμ—μ„œ RestApiController 클래슀 ν•˜μœ„μ— μ‘΄μž¬ν•˜λŠ” get() λ©”μ†Œλ“œλ§Œ μ„€μ •ν•˜λ €κ³  ν•˜λ©΄ λ‹€μŒκ³Ό 같이 μ§€μ •ν•˜λ©΄ λ©λ‹ˆλ‹€.

// RestApiController 클래슀 ν•˜μœ„μ˜ get λ©”μ†Œλ“œλ₯Ό Aspect둜 지정
@Pointcut("execution(* com.juhyun.springaop.aop.controller.RestApiController.get(..))")

ν…ŒμŠ€νŠΈλ₯Ό 해보면 get λ©”μ†Œλ“œμ™€λŠ” 달리 post λ©”μ†Œλ“œμ—λŠ” AOP 적용이 λ˜μ§€μ•Šμ€ κ±Έ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

포인트컷 ν‘œν˜„μ‹ μ˜ˆμ‹œ

  • execution(int minus(int, int)): int νƒ€μž…μ˜ 리턴 κ°’, minusλΌλŠ” λ©”μ†Œλ“œ, 두 개의 int νŒŒλΌλ―Έν„°λ₯Ό κ°€μ§€λŠ” λ©”μ†Œλ“œ
  • execution(* minus(int, int)): λͺ¨λ“  νƒ€μž…μ˜ 리턴, minusλΌλŠ” λ©”μ†Œλ“œ, 두 개의 int νŒŒλΌλ―Έν„°λ₯Ό κ°€μ§€λŠ” λ©”μ†Œλ“œ
  • execution(* minus(..)): 리턴 νƒ€μž…, νŒŒλΌλ―Έν„° μ’…λ₯˜, κ°œμˆ˜μ— 상관없이 minusλΌλŠ” λ©”μ†Œλ“œ 이름을 가진 λͺ¨λ“  λ©”μ†Œλ“œ
  • execution(* minus()): 리턴 νƒ€μž…μ€ 상관없이 νŒŒλΌλ―Έν„°κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” minus λ©”μ†Œλ“œ
  • execution(* *(..)): 리턴 νƒ€μž…, νŒŒλΌλ―Έν„°, λ©”μ†Œλ“œ 이름에 상관없이 λͺ¨λ“  쑰건을 ν—ˆμš©ν•˜λŠ” 포인트컷 ν‘œν˜„μ‹
  • execution(* com.juhyun.aop.Target.*(..)): com.juhyun.aop νŒ¨ν‚€μ§€μ˜ Target ν΄λž˜μŠ€μ— μ‘΄μž¬ν•˜λŠ” λ©”μ†Œλ“œ
  • execution(* com.juhyun.aop.*.*(..)): com.juhyun.aop νŒ¨ν‚€μ§€μ— μ‘΄μž¬ν•˜λŠ” λͺ¨λ“  클래슀

 

@Before

νƒ€κ²Ÿ λ©”μ†Œλ“œκ°€ μ‹€ν–‰ν•˜κΈ° 이전 μ–΄λ“œλ°”μ΄μŠ€ κΈ°λŠ₯을 μˆ˜ν–‰

 

@After

νƒ€κ²Ÿ λ©”λ„μŠ€μ˜ 결과에 상관없이 μ‹€ν–‰ ν›„ μ–΄λ“œλ°”μ΄μŠ€ κΈ°λŠ₯을 μˆ˜ν–‰

 

@AfterReturning

νƒ€κ²Ÿ λ©”μ†Œλ“œκ°€ μ •μƒμ μœΌλ‘œ 결과값을 λ°˜ν™˜ ν›„ μ–΄λ“œλ°”μ΄μŠ€ κΈ°λŠ₯을 μˆ˜ν–‰

 

@AfterThrowing

νƒ€κ²Ÿ λ©”μ†Œλ“œκ°€ μˆ˜ν–‰ 쀑 μ˜ˆμ™Έλ₯Ό λ°œμƒν•˜λ©΄ μ–΄λ“œλ°”μ΄μŠ€ κΈ°λŠ₯을 μˆ˜ν–‰

 

@Around

μ–΄λ“œλ°”μ΄μŠ€κ°€ νƒ€κ²Ÿ λ©”μ†Œλ“œλ₯Ό κ°μ‹Έμ„œ νƒ€κ²Ÿ λ©”μ†Œλ“œ ν˜ΈμΆœμ „, ν›„ μ–΄λ“œλ°”μ΄μŠ€ κΈ°λŠ₯을 μˆ˜ν–‰

AroundλŠ” νƒ€κ²Ÿμ„ μ‹€ν–‰ν•  지 ν˜Ήμ€ λ°”λ‘œ λ°˜ν™˜ν• μ§€λ„ μ •ν•  수 있음

 

 

 

🎯 Spring AOP μ‹€μŠ΅ 2


이번 μ‹€μŠ΅μ—μ„œλŠ” νŠΉμ • λ©”μ†Œλ“œμ˜ μ‹œκ°„μ„ κΈ°λ‘ν•˜λŠ” λΆ€κ°€κΈ°λŠ₯을 AOPλ₯Ό 톡해 μ μš©ν•΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈ ꡬ쑰 및 μ „λ°˜μ μΈ μ½”λ“œλŠ” μœ„ μ‹€μŠ΅ 1κ³Ό λ™μΌν•©λ‹ˆλ‹€.

// μ»€μŠ€ν…€ μ–΄λ…Έν…Œμ΄μ…˜
package com.juhyun.springaop.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Timer {
}


// RestApiController

... μƒλž΅

    @Timer
    @DeleteMapping("/delete")
    public String delete() throws InterruptedException {
        // DB logic 2초 μˆ˜ν–‰
        Thread.sleep(1000 * 2);
        return "delete";
    }
    

// AOP
package com.juhyun.springaop.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Aspect
@Component
public class TimerAop {

    @Pointcut("execution(* com.juhyun.springaop.aop.controller..*.*(..))")
    private void cut() { }

    @Pointcut("@annotation(com.juhyun.springaop.aop.annotation.Timer)")
    private void enableTimer() { }

    @Around("cut() && enableTimer()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("--------------- @Around ---------------");
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // νƒ€κ²Ÿ 였브젝트의 λ©”μ„œλ“œ μ‹€ν–‰(controller νŒ¨ν‚€μ§€ ν•˜μœ„ ν΄λž˜μŠ€λ“€μ˜ λ©”μ†Œλ“œ)
        Object result = joinPoint.proceed();
        System.out.println("result: " + result);

        stopWatch.stop();

        System.out.println("Total Time: " + stopWatch.getTotalTimeSeconds());
    }
}

μœ„ μ˜ˆμ œμ—μ„œλŠ” ProceddingJoinPoint λΌλŠ” ν΄λž˜μŠ€κ°€ μΆ”κ°€λ˜μ—ˆλŠ”λ°μš”, ν•΄λ‹Ή 클래슀의 proceed()λ©”μ†Œλ“œλ₯Ό μ‹€ν–‰ν•¨μœΌλ‘œμ„œ νƒ€κ²Ÿ 였브젝트의 λ©”μ†Œλ“œκ°€ 싀행이 λ©λ‹ˆλ‹€. λ”°λΌμ„œ ν•΄λ‹Ή μ½”λ“œ μ „ν›„λ‘œ λΆ€κ°€κΈ°λŠ₯을 μœ„ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ©΄ λ©λ‹ˆλ‹€.

β€» μ£Όμ˜ν•˜μ‹€ 점은 @Around μ–΄λ“œλ°”μ΄μŠ€μ—μ„œλ§Œ ProceedingJoinPoint λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ μ‚¬μš©ν•˜κ³ , λ‚˜λ¨Έμ§€ @Before, @After, @AfterReturning, @AfterThrowing μ–΄λ“œλ°”μ΄μŠ€μ—μ„œλŠ” JoinPointλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ μ‚¬μš©ν•©λ‹ˆλ‹€. μ΄λŠ” Around μ–΄λ“œλ°”μ΄μŠ€μ—μ„œλ§Œ proceed() λ©”μ†Œλ“œκ°€ ν•„μš”ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

 

λ˜ν•œ TimerλΌλŠ” μ–΄λ…Έν…Œμ΄μ…˜ 기반으둜 예제λ₯Ό μž‘μ„±ν–ˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•  λ©”μ†Œλ“œ(delete)에 μ μš©ν•˜κ³ , AOPμ—μ„œλŠ” 이λ₯Ό μ μš©ν•˜κΈ° μœ„ν•΄ 적용 λ²”μœ„λ₯Ό μ„€μ •ν•˜λŠ” cut() λ©”μ†Œλ“œμ™€, Timer μ–΄λ…Έν…Œμ΄μ…˜μ„ μ„€μ •ν•˜λŠ” enableTimer() λ©”μ†Œλ“œλ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

μ‹€ν–‰ μ‹œκ°„μ€ νƒ€κ²Ÿ λ©”μ†Œλ“œ(delete)의 μ „, ν›„ 에 이루어져야 ν•˜κΈ° λ•Œλ¬Έμ— @Around μ–΄λ…Έν…Œμ΄μ…˜μ„ 톡해 μ„€μ •ν•©λ‹ˆλ‹€.

around() λ©”μ†Œλ“œμ—μ„œλŠ” joinPoint.proceed() μ½”λ“œλ₯Ό 기반으둜 μœ„μ—λŠ” μ‹€ν–‰ μ „, μ•„λž˜λŠ” μ‹€ν–‰ ν›„ μˆ˜ν–‰ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

 

 

🎯  마무리


μ΄μƒμœΌλ‘œ μŠ€ν”„λ§ AOP와 ν”„λ‘μ‹œ νŒ¨ν„΄, λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ— λŒ€ν•΄ μ •λ¦¬ν•˜κ³  ν•™μŠ΅ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

개인적으둜 AOP의 경우 Target, Aspect, Advice, PointCut, Join Point λ“±λ“± μš©μ–΄λ§Œ λ΄€μ„λ•ŒλŠ” 이해가 μ „~ν˜€ λ˜μ§€μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ 이번 ν¬μŠ€νŒ…μ„ κ³„κΈ°λ‘œ μ—¬λŸ¬ μžλ£Œμ™€ λ¬Έμ„œ, μ„œμ  등을 μ°Έκ³ ν•˜λ©° AOP에 λŒ€ν•΄ 정리λ₯Ό ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€. ν™•μ‹€νžˆ μ΄λ‘ λ³΄λ‹€λŠ” μ‹€μŠ΅μ„ 톡해 ν•™μŠ΅ν•˜λŠ” 것이 μ΄ν•΄ν•˜κΈ°μ—λŠ” 훨씬 더 μˆ˜μ›”ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€.πŸ˜ƒ

λ¬Όλ‘  μ œκ°€ ν•™μŠ΅ν•œ λ‚΄μš©μ€ μ§€κ·Ήνžˆ μΌλΆ€μ΄μ§€λ§Œ, λ‹€μŒμ— AOPλ₯Ό μ‹€λ¬΄μ—μ„œ ν˜Ήμ€ 개인적으둜 μ‚¬μš©ν•΄ λ³Ό κΈ°νšŒκ°€ 생긴닀면 κ·Έλ•Œ μ’€ 더 μΆ”κ°€μ μœΌλ‘œ 정리해보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

 

 

 

References

 

λ°˜μ‘ν˜•

λŒ“κΈ€