๐ŸŒˆ [Section2] 20. AOP (Aspect-Oriented Programming)

ํ˜„์ฃผยท2022๋…„ 10์›” 18์ผ
0

bootcamp

๋ชฉ๋ก ๋ณด๊ธฐ
39/71

๐Ÿ“• ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ!

  • AOP (Aspect-Oriented Programming)
  • AOP ์šฉ์–ด
  • AOP ํ”„๋ก์‹œ

โœ๏ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง

1. ํ•ต์‹ฌ ๊ด€์‹ฌ์‚ฌ (Core Concerns)
( ํ•ต์‹ฌ ๊ธฐ๋Šฅ )

  • ๊ฐ์ฒด๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ณ ์œ ์˜ ๊ธฐ๋Šฅ

Ex. ์—…๋ฌด ๋กœ์ง

2. ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ (CROSS-CUTTING CONCERNS)

  • ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๋ณด์กฐํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ๊ฑธ์ณ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ
    โžœ ์ค‘๋ณต ์ฝ”๋“œ ์ƒ๊น€ ( AOP ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐ )

  • ํ•ต์‹ฌ๊ธฐ๋Šฅ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ ( ๋‹จ๋… X )

Ex. ๋กœ๊น…, ๋ณด์•ˆ, ํŠธ๋žœ์žญ์…˜, ๋กœ๊ทธ ์ถ”์  ๋กœ์ง ๋“ฑ

Ex. orderService๋ผ๋Š” ํ•˜๋‚˜์˜ ๊ฐ์ฒด์— ํ•ต์‹ฌ๊ธฐ๋Šฅ์ธ ์ฃผ๋ฌธ ๋กœ์ง๊ณผ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ธ ๋กœ๊ทธ ์ถ”์  ๋กœ์ง์ด ํ•ฉ์ณ์ ธ์„œ ํ•˜๋‚˜์˜ ๋กœ์ง์„ ์™„์„ฑํ•˜๊ฒŒ ๋จ


โœ๏ธ ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ (Object Oriented Programming)

  • OOP์˜ ํ•ต์‹ฌ์€ ๊ณตํ†ต๋œ ๋ชฉ์ ์„ ๋ˆ ๋ฐ์ดํ„ฐ์™€ ๋™์ž‘์„ ๋ฌถ์–ด ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ์ •์˜ํ•˜๋Š” ๊ฒƒ

  • ๊ธฐ๋Šฅ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ํฐ ์žฅ์ 

  • ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ (Separation of Concerns, SoC)์˜ ๋””์ž์ธ ์›์น™์„ ์ค€์ˆ˜ํ•ด์•ผํ•จ

  • OOP ๋ชจ๋“ˆํ™”์˜ ํ•ต์‹ฌ ๋‹จ์œ„ โžœ ํด๋ž˜์Šค

โœ”๏ธ OOP์˜ ๋ฌธ์ œ์ 

๋น„์ฆˆ๋‹ˆ์Šค ํด๋ž˜์Šค์— ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ์™€ ํ•ต์‹ฌ ๊ด€์‹ฌ์‚ฌ๊ฐ€ ๊ณต์กดํ•˜๊ฒŒ ๋˜๋Š”๋ฐ,
์ด ๋•Œ ๋ฉ”์†Œ๋“œ ๋ณต์žก๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ์ฝ”๋“œ ํŒŒ์•…์ด ์–ด๋ ต๊ณ ,
๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์˜ ๋ถˆํŠน์ • ๋‹ค์ˆ˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ๊ตฌํ˜„๋˜์–ด ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ์˜ ๋ชจ๋“ˆํ™”๊ฐ€ ์–ด๋ ค์›€

โžœ OOP์˜ ๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ์— ๋Œ€ํ•œ ํ•œ๊ณ„๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž AOP ๋“ฑ์žฅ


โœ๏ธ AOP (Aspect-Oriented Programming)

  • ๊ธฐ์กด๊ณผ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ ๊ตฌ์กฐ ์‚ฌ๊ณ  ๋ฐฉ์‹์„ ์ œ๊ณตํ•จ์œผ๋กœ์จ ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(OOP)์˜ ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์„ ๋ณด์™„ํ•˜๋Š” ๊ฒƒ

  • ์—ฌ๋Ÿฌ ์œ ํ˜•๊ณผ ๊ฐ์ฒด ๊ฐ„์— ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์˜ ๋ชจ๋“ˆํ™”๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ

  • AOP์˜ ๋ชจ๋“ˆํ™”์˜ ํ•ต์‹ฌ ๋‹จ์œ„ โžœ ๊ด€์  (Aspect)
    ( OOP์˜ ๋ชจ๋“ˆํ™”์˜ ํ•ต์‹ฌ ๋‹จ์œ„ โžœ ํด๋ž˜์Šค (class) )

โœ”๏ธ Aspect

  • ์—ฌ๋Ÿฌ ๊ฐ์ฒด์— ๊ณตํ†ต์œผ๋กœ ์ ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ ( ๊ณตํ†ต ๊ธฐ๋Šฅ = ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ )
  • Aspect = ์–ด๋“œ๋ฐ”์ด์Šค(Advice) + ํฌ์ธํŠธ์ปท(PointCut)
    ( ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•œ ์ฝ”๋“œ์ธ ์–ด๋“œ๋ฐ”์ด์Šค(Advice)์™€ ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ์–ด๋””์— ์ ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ํฌ์ธํŠธ์ปท(PointCut)์„ ํ•ฉ์นœ ๊ฐœ๋… )

โœ๏ธ Spring AOP์˜ ๊ตฌํ˜„ ๋ฐฉ์‹

1. XML ๊ธฐ๋ฐ˜์˜ POJO ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•œ AOP ๊ตฌํ˜„

  • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” Advice ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑ

  • XML ์„ค์ • ํŒŒ์ผ์— <aop:config>๋ฅผ ์ด์šฉํ•ด์„œ ์• ์ŠคํŽ™ํŠธ๋ฅผ ์„ค์ •
    (์ฆ‰, ์–ด๋“œ๋ฐ”์ด์Šค์™€ ํฌ์ธํŠธ์ปท์„ ์„ค์ •ํ•จ)

2. @Aspect ์• ๋„ˆํ…Œ์ด์…˜์„ ์ด์šฉํ•œ AOP ๊ตฌํ˜„

  • @Aspect ์• ๋„ˆํ…Œ์ด์…˜์„ ์ด์šฉํ•ด์„œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” Aspect ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑ

  • ์ด ๋•Œ, Aspect ํด๋ž˜์Šค๋Š” ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฉ”์„œ๋“œ์™€ ํฌ์ธํŠธ์ปท์„ ํฌํ•จ

  • XML ์„ค์ • ํŒŒ์ผ์— <aop:aspectj-autoproxy />๋ฅผ ์„ค์ •


โœ๏ธ ์กฐ์ธ ํฌ์ธํŠธ (Join Point)

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹คํ–‰ ํ๋ฆ„์—์„œ์˜ AOP๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํŠน์ • ์ง€์ 

  • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์Šคํ”„๋ง ๋นˆ์—๋งŒ AOP ์ ์šฉ ๊ฐ€๋Šฅ

  • ํ•ญ์ƒ ๋ฉ”์„œ๋“œ ๋ ˆ๋ฒจ์—๋งŒ AOP ์ ์šฉ ๊ฐ€๋Šฅ
    ( ์Šคํ”„๋ง AOP๊ฐ€ ํ”„๋ก์‹œ ๋ฐฉ์‹ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ )
    ( ํ”„๋ก์‹œ ๋ฐฉ์‹ = ๋Ÿฐํƒ€์ž„ ์ค‘์ผ ๋•Œ, ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋นˆ์„ ์ƒ์„ฑํ•˜๋ฉด์„œ, ์š”์ฒญ์„ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๋Œ€์‹  ๊ฐ€๋กœ์ฑ„์„œ ์ฐ ๊ฐ์ฒด์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ )

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ƒˆ๋กœ์šด ๋™์ž‘์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด, ์ด ์กฐ์ธ ํฌ์ธํŠธ์— ๊ด€์‹ฌ์ฝ”๋“œ(Aspect Code) ์ถ”๊ฐ€ํ•จ

  • ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ๋Š” ์กฐ์ธํฌ์ธํŠธ ์ „/ํ›„์— AOP์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋จ

  • ์–ด๋“œ๋ฐ”์ด์Šค ์ ์šฉ์ด ํ•„์š”ํ•œ ๊ณณ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์— ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง

  • JointPoint ๋ฉ”์†Œ๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์–ด๋“œ๋ฐ”์ด์Šค ๋ฉ”์†Œ๋“œ์— ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์„ ์–ธ๋งŒ ํ•˜๋ฉด ๋จ

โœ” JointPoint ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

  • JoinPoint.getArgs()
    โžœ JoinPoint์— ์ „๋‹ฌ๋œ ์ธ์ž๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฐ˜ํ™˜

  • JoinPoint.getThis()
    โžœ AOP ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜

  • JoinPoint.getTarget()
    โžœ AOP๊ฐ€ ์ ์šฉ๋œ ๋Œ€์ƒ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
    ( ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋ฉ”์†Œ๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ )

  • JoinPoint.getSignature()
    โžœ ์กฐ์–ธ๋˜๋Š” ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์„ค๋ช…์„ ๋ฐ˜ํ™˜
    ( ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœํ•œ ๋ฉ”์†Œ๋“œ์˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜(๋ฆฌํ„ดํƒ€์ž…, ์ด๋ฆ„, ๋งค๊ฐœ๋ณ€์ˆ˜) ์ •๋ณด๊ฐ€ ์ €์žฅ๋œ Signature ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ )

    • Signature๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ
      ( ๊ฐ์ฒด๊ฐ€ ์„ ์–ธํ•˜๋Š” ๋ชจ๋“  ์—ฐ์‚ฐ์—์„œ, ์—ฐ์‚ฐ์˜ ์ด๋ฆ„, ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„๋“ค์ด๋Š” ๊ฐ์ฒด๋“ค์„ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ผ๊ณ  ํ•จ )

      • String getName()
        โžœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœํ•œ ๋ฉ”์†Œ๋“œ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜

      • String toLongString()
        โžœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœํ•œ ๋ฉ”์†Œ๋“œ์˜ ๋ฆฌํ„ดํƒ€์ž…, ์ด๋ฆ„, ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ๊นŒ์ง€ ํฌํ•จํ•ด์„œ ๋ฐ˜ํ™˜

      • String toShortString()
        โžœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ˜ธ์ถœํ•œ ๋ฉ”์†Œ๋“œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์ถ•์•ฝํ•œ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜

      • JoinPoint.toString()
        โžœ ์กฐ์–ธ๋˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์œ ์šฉํ•œ ์„ค๋ช…์„ ์ธ์‡„

โœ” ProceedingJoinPoint ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

  • proceed()
    โžœ ๋‹ค์Œ ์–ด๋“œ๋ฐ”์ด์Šค๋‚˜ ํƒ€์ผ“์„ ํ˜ธ์ถœ

โœ๏ธ ์–ด๋“œ๋ฐ”์ด์Šค (Advice)

  • ํŠน์ • ์กฐ์ธํฌ์ธํŠธ(Join Point)์—์„œ ์ˆ˜ํ–‰๋  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ •๋ฆฌํ•œ ์ฝ”๋“œ

  • ํ•ต์‹ฌ ์ฝ”๋“œ(Target)์— Aspect๋ฅผ ์–ธ์ œ ์ ์šฉํ• ์ง€ ์ •์˜

  • ์‹œ์Šคํ…œ ์ „์ฒด Aspect์— API ํ˜ธ์ถœ ์ œ๊ณต

  • ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ์ „ ์ƒ์„ธ ์ •๋ณด ๋ฐ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ธฐ ์œ„ํ•ด, ๋กœ๊ฑฐ(Logger)๋Š” ์กฐ์ธํฌ์ธํŠธ(๋ฉ”์†Œ๋“œ ์‹œ์ž‘ ์ „ ํฌ์ธํŠธ)๋ฅผ ์„ ํƒ

    โœ”๏ธ ๋กœ๊ฑฐ (Logger)
    ์ผ๋ จ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๊ทธ ํŒŒ์ผ๋กœ ๊ธฐ๋กํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด

  • ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ

    • ์ˆœ์„œ๋ฅผ ์ง€์ •ํ•˜๋ ค๋ฉด @Aspect ์ ์šฉ ๋‹จ์œ„๋กœ org.springframework.core.annotation.@Order ์• ๋„ˆํ…Œ์ด์…˜์„ ์ ์šฉํ•ด์•ผํ•จ

    • ์–ด๋“œ๋ฐ”์ด์Šค(Advice) ๋‹จ์œ„๊ฐ€ ์•„๋‹Œ ํด๋ž˜์Šค ๋‹จ์œ„๋กœ ์ ์šฉ ๊ฐ€๋Šฅ

    • ํ•˜๋‚˜์˜ ์• ์ŠคํŒฉํŠธ(Aspect)์— ์—ฌ๋Ÿฌ ์–ด๋“œ๋ฐ”์ด์Šค(Advice)๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ์ˆœ์„œ ๋ณด์žฅ X
      โžœ ์• ์ŠคํŒฉํŠธ(Aspect)๋ฅผ ํด๋ž˜์Šค๋ณ„๋กœ ๋ถ„๋ฆฌํ•ด์•ผํ•จ

โœ” Advice ์ข…๋ฅ˜

โœ”๏ธ Before

  • ์กฐ์ธ ํฌ์ธํŠธ ์ด์ „์— ์‹คํ–‰

  • ํƒ€๊ฒŸ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— ์ฒ˜๋ฆฌํ•ด์•ผํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ํ˜ธ์ถœ ์ „์— ๊ณตํ†ต๊ธฐ๋Šฅ ์‹คํ–‰

  • Before Advice ๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฆฌํ„ดํƒ€์ž…์ด void
    ( But, ๋ฆฌํ„ด ๊ฐ’์„ ๊ฐ–๋”๋ผ๋„ ์‹ค์ œ Advice ์ ์šฉ ๊ณผ์ •์—๋Š” ์˜ํ–ฅ X )

  • ์ž‘์—… ํ๋ฆ„ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€

  • ๋ฉ”์„œ๋“œ ์ข…๋ฃŒ ์‹œ, ์ž๋™์œผ๋กœ ๋‹ค์Œ ํƒ€๊ฒŸ ํ˜ธ์ถœ
    ( ์ฃผ์˜์  : ๋ฉ”์„œ๋“œ์—์„œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ๊ฒฝ์šฐ, ๋Œ€์ƒ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์•ˆ๋จ )

โœ”๏ธ After returning

  • ์กฐ์ธํฌ์ธํŠธ๊ฐ€ ์ •์ƒ ์™„๋ฃŒ๋œ ํ›„ ์‹คํ–‰
    ( ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์˜ˆ์™ธ์—†์ด ์ •์ƒ์ ์œผ๋กœ ๋ฐ˜ํ™˜๋œ ์ดํ›„ ๊ณตํ†ต ๊ธฐ๋Šฅ์„ ์‹คํ–‰ )

  • < returning ์†์„ฑ์— ์‚ฌ์šฉ๋œ ์ด๋ฆ„ = ์–ด๋“œ๋ฐ”์ด์Šค ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„ > ์ด์–ด์•ผํ•จ

  • returning ์ ˆ์— ์ง€์ •๋œ ํƒ€์ž…์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋งŒ ๋Œ€์ƒ์„ ์‹คํ–‰

โœ”๏ธ After throwing

  • ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ ์ข…๋ฃŒ๋  ๊ฒฝ์šฐ์— ์‹คํ–‰
    ( ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋„์ค‘ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ ๊ณตํ†ต ๊ธฐ๋Šฅ์„ ์‹คํ–‰ )

  • < throwing ์†์„ฑ์— ์‚ฌ์šฉ๋œ ์ด๋ฆ„ = ์–ด๋“œ๋ฐ”์ด์Šค ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„ > ์ด์–ด์•ผํ•จ

  • throwing ์ ˆ์— ์ง€์ •๋œ ํƒ€์ž…๊ณผ ๋งž์€ ์˜ˆ์™ธ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์‹คํ–‰

โœ”๏ธ After (finally)

  • ์กฐ์ธ ํฌ์ธํŠธ์˜ ๋™์ž‘๊ณผ๋Š” ์ƒ๊ด€์—†์ด ๋ฉ”์„œ๋“œ ์‹คํ–‰ ํ›„ ๊ณตํ†ต ๊ธฐ๋Šฅ ์‹คํ–‰
    ( ์‹คํ–‰ ๊ฒฐ๊ณผ๊ฐ€ ์ •์ƒ, ์˜ˆ์™ธ์™€๋Š” ์ƒ๊ด€ X )
    ( ์˜ˆ์™ธ ๋™์ž‘์˜ finally๋ฅผ ์ƒ๊ฐํ•˜๋ฉด ๋จ )

  • ๋ณดํ†ต ๋ฆฌ์†Œ์Šค ํ•ด์ œํ•˜๋Š”๋ฐ ์‚ฌ์šฉ

โœ”๏ธ Around

  • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ „/ํ›„, ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ์ ์— ๊ณตํ†ต ๊ธฐ๋Šฅ ์‹คํ–‰
    Ex. ์กฐ์ธ ํฌ์ธํŠธ ์‹คํ–‰ ์—ฌ๋ถ€ ์„ ํƒ, ๋ฐ˜ํ™˜ ๊ฐ’ ๋ณ€ํ™˜, ์˜ˆ์™ธ ๋ณ€ํ™˜ ๋“ฑ

  • ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ์–ด๋“œ๋ฐ”์ด์Šค

    • ์กฐ์ธ ํฌ์ธํŠธ ์‹คํ–‰ ์—ฌ๋ถ€ ์„ ํƒ - joinPoint.proceed()
    • ์ „๋‹ฌ ๊ฐ’ ๋ณ€ํ™˜ - joinPoint.proceed(args[])
    • ๋ฐ˜ํ™˜ ๊ฐ’ ๋ณ€ํ™˜
    • ์˜ˆ์™ธ ๋ณ€ํ™˜
    • try ~ catch ~ finally ๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ๊ตฌ๋ฌธ ์ฒ˜๋ฆฌ ( ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ๊ตฌ๋ฌธ )
  • ์–ด๋“œ๋ฐ”์ด์Šค์˜ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ProceedingJoinPoint ์‚ฌ์šฉํ•ด์•ผํ•จ

  • proceed()๋ฅผ ํ†ตํ•ด ๋Œ€์ƒ ์‹คํ–‰ํ•˜๊ณ  ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰ ๊ฐ€๋Šฅ

    ๐Ÿ’ก ์‚ฌ์‹ค @Around๋งŒ ์žˆ์–ด๋„ ๋ชจ๋“  ๊ธฐ๋Šฅ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ
    But, ๊ณ ๋ คํ•ด์•ผํ•  ์‚ฌํ•ญ์ด ์žˆ์„ ๋•Œ ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™์ด ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์žˆ์Œ
    โ €
    @Before / @After ๊ณผ ๊ฐ™์€ ์–ด๋“œ๋ฐ”์ด์Šค๋Š” ๊ธฐ๋Šฅ์€ ์ ์ง€๋งŒ ์ฝ”๋“œ๋„ ๋‹จ์ˆœํ•˜๊ณ , ์›ํ•˜๋Š” ๋Œ€๋กœ ์ž‘๋™ํ•˜๋ฉฐ ๊ฐ๊ฐ์ด ์• ๋„ˆํ…Œ์ด์…˜๋งŒ ๋ด๋„ ์–ด๋–ค ์ผ์„ ํ•˜๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ํŒŒ์•… ๊ฐ€๋Šฅ
    โ €
    โžœ ์ข‹์€ ์„ค๊ณ„๋Š” @Around๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ œ์•ฝ์„ ๊ฐ€์ง€๋”๋ผ๋„ ๋ฌธ์ œ ์ž์ฒด๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  ์—ญํ• ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜์—ฌ ์‹ค์ˆ˜๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ž„


โœ๏ธ ํฌ์ธํŠธ์ปท (Pointcut)

  • ์กฐ์ธํฌ์ธํŠธ(Join Point)์—์„œ ์–ด๋“œ๋ฐ”์ด์Šค(Advice)๊ฐ€ ์ ์šฉ๋  ์œ„์น˜๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ
    ( ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ํ๋ฆ„์—์„œ AOP๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํฌ์ธํŠธ์—์„œ ์ˆ˜ํ–‰๋  ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ฝ”๋“œ์˜ ์œ„์น˜๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ )

  • ๊ด€์‹ฌ ์กฐ์ธํฌ์ธํŠธ๋ฅผ ๊ฒฐ์ • โžœ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์‹คํ–‰๋˜๋Š” ์‹œ๊ธฐ ์ œ์–ด ๊ฐ€๋Šฅ

  • ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ง€์ ๋งŒ ํฌ์ธํŠธ์ปท์œผ๋กœ ์„ ๋ณ„ ๊ฐ€๋Šฅ

  • AspectJ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•ด์„œ ์ง€์ •
    ( &&, ||, ! ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐํ•ฉ ๊ฐ€๋Šฅ )

    ๐Ÿ’ก build.gradle ํŒŒ์ผ์˜ dependencies์— implementation 'org.springframework.boot:spring-boot-starter-aop' ์ถ”๊ฐ€ํ•ด์•ผ AspectJ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅ !

ํฌ์ธํŠธ์ปท ์ง€์‹œ์ž ์ข…๋ฅ˜

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

  • within
    โžœ ํŠน์ • ํƒ€์ž… ๋‚ด์˜ ์กฐ์ธ ํฌ์ธํŠธ ๋งค์นญ

  • args
    โžœ ์ธ์ž๊ฐ€ ์ฃผ์–ด์ง„ ํƒ€์ž…์˜ ์ธ์Šคํ„ด์Šค์ธ ์กฐ์ธ ํฌ์ธํŠธ

  • this
    โžœ ์Šคํ”„๋ง ๋นˆ ๊ฐ์ฒด(์Šคํ”„๋ง AOP ํ”„๋ก์‹œ)๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ์กฐ์ธ ํฌ์ธํŠธ

  • target
    โžœ Target ๊ฐ์ฒด(์Šคํ”„๋ง AOP ํ”„๋ก์‹œ๊ฐ€ ๊ฐ€๋ฅดํ‚ค๋Š” ์‹ค์ œ ๋Œ€์ƒ)๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ์กฐ์ธ ํฌ์ธํŠธ

  • @target
    โžœ ์‹คํ–‰ ๊ฐ์ฒด์˜ ํด๋ž˜์Šค์— ์ฃผ์–ด์ง„ ํƒ€์ž…์˜ ์• ๋„ˆํ…Œ์ด์…˜์ด ์žˆ๋Š” ์กฐ์ธ ํฌ์ธํŠธ

  • @within
    โžœ ์ฃผ์–ด์ง„ ์• ๋„ˆํ…Œ์ด์…˜์ด ์žˆ๋Š” ํƒ€์ž… ๋‚ด ์กฐ์ธ ํฌ์ธํŠธ

  • @annotation
    โžœ ๋ฉ”์„œ๋“œ๊ฐ€ ์ฃผ์–ด๋‹ˆ ์• ๋„ˆํ…Œ์ด์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์กฐ์ธ ํฌ์ธํŠธ๋ฅผ ๋งค์นญ

  • @args
    โžœ ์ „๋‹ฌ๋œ ์‹ค์ œ ์ธ์ˆ˜์˜ ๋Ÿฐํƒ€์ž„ ํƒ€์ž…์ด ์ฃผ์–ด์ง„ ํƒ€์ž…์˜ ์• ๋„ˆํ…Œ์ด์…˜์„ ๊ฐ–๋Š” ์กฐ์ธ ํฌ์ธํŠธ

  • bean
    โžœ ์Šคํ”„๋ง ์ „์šฉ ํฌ์ธํŠธ์ปท ์ง€์‹œ์ž
    โžœ ๋นˆ์˜ ์ด๋ฆ„์œผ๋กœ ํฌ์ธํŠธ์ปท์„ ์ง€์ •
    โ €
    โ— execution์„ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ์ž์ฃผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ !

โœ” ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹

(..) โžœ ๋ชจ๋“  ๋งค๊ฐœ๋ณ€์ˆ˜
*(..) โžœ ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์„ ํƒ
(*) โžœ ๋ฐ˜๋“œ์‹œ 1๊ฐœ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง€๋Š” ๋ฉ”์†Œ๋“œ๋งŒ ์„ ํƒ

  • ๋ชจ๋“  ๊ณต๊ฐœ ๋ฉ”์„œ๋“œ ์‹คํ–‰
    โžœ execution(public * *(..))
    โ €
  • set ๋‹ค์Œ ์ด๋ฆ„์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์‹คํ–‰
    ( ๋ฉ”์„œ๋“œ๋ช…์ด set~~์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ )
    โžœ execution(* set*(..))
    โ €
  • AccountService ์ธํ„ฐํŽ˜์ด์Šค์— ์˜ํ•ด ์ •์˜๋œ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ์˜ ์‹คํ–‰
    โžœ execution(* com.xyz.service.AccountService.*(..))
    ( ํŒŒ์ผ ์œ„์น˜๊ฐ€ com.xyz.service ํŒจํ‚ค์ง€์˜ AccountService ์ธํ„ฐํŽ˜์ด์Šค์ธ ๊ฒƒ )
    โ €
  • service ํŒจํ‚ค์ง€์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ ์‹คํ–‰
    โžœ execution(* com.xyz.service.*.*(..))
    ( ํŒŒ์ผ ์œ„์น˜๊ฐ€ com.xyz.service ํŒจํ‚ค์ง€ )
    โ €
  • service ํŒจํ‚ค์ง€ ๋˜๋Š” ํ•ด๋‹น ํ•˜์œ„ ํŒจํ‚ค์ง€ ์ค‘ ํ•˜๋‚˜์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ ์‹คํ–‰
    โžœ execution(* com.xyz.service..*.*(..))
    โ €
  • service ํŒจํ‚ค์ง€ ๋‚ด์˜ ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ within(com.xyz.service.*)
    โ €
  • service ํŒจํ‚ค์ง€ ๋˜๋Š” ํ•˜์œ„ ํŒจํ‚ค์ง€ ์ค‘ ํ•˜๋‚˜ ๋‚ด์˜ ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ within(com.xyz.service..*)
    โ €
  • AccountService ํ”„๋ก์‹œ๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ this(com.xyz.service.AccountService)
    โ €
  • AccountService ๋Œ€์ƒ ๊ฐ์ฒด๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ target(com.xyz.service.AccountService)
    โ €
  • ๋‹จ์ผ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋Ÿฐํƒ€์ž„์— ์ „๋‹ฌ๋œ ์ธ์ˆ˜๊ฐ€ Serializable๊ณผ ๊ฐ™์€ ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์†Œ๋“œ ์‹คํ–‰)
    โžœ args(java.io.Serializable)
    โ €
  • ๋Œ€์ƒ ๊ฐ์ฒด์— @Transactional ์• ๋„ˆํ…Œ์ด์…˜์ด ์žˆ๋Š” ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ @target(org.springframework.transaction.annotation.Transactional)
    โ €
  • ์‹คํ–‰ ๋ฉ”์„œ๋“œ์— @Transactional ์• ๋„ˆํ…Œ์ด์…˜์ด ์žˆ๋Š” ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ @annotation(org.springframework.transaction.annotation.Transactional)
    โ €
  • ๋‹จ์ผ ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ „๋‹ฌ๋œ ์ธ์ˆ˜์˜ ๋Ÿฐํƒ€์ž„ ์œ ํ˜•์ด @Classified ์• ๋„ˆํ…Œ์ด์…˜์„ ๊ฐ–๋Š” ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ @args(com.xyz.security.Classified)
    โ €
  • memberService ๋ผ๋Š” ์ด๋ฆ„์˜ ์Šคํ”„๋ง ๋นˆ์˜ ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    (Spring AOP์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰)
    โžœ bean(memberService)
    โ €
  • ์™€์ผ๋“œ ํ‘œํ˜„์‹ *Service ๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ์Šคํ”„๋ง ๋นˆ์˜ ๋ชจ๋“  ์กฐ์ธ ํฌ์ธํŠธ
    ( ~~Service๋กœ ๋๋‚˜๋Š” ์ด๋ฆ„์˜ ๋นˆ๋“ค )
    โžœ bean(*Service)

โœ๏ธ ์–ด๋“œ๋ฐ”์ด์ € (Advisor)

  • ํ•˜๋‚˜์˜ ์–ด๋“œ๋ฐ”์ด์Šค(Advice) + ํ•˜๋‚˜์˜ ํฌ์ธํŠธ์ปท(Pointcut) ์œผ๋กœ ๊ตฌ์„ฑ
    ( ์ˆ˜ํ–‰ ์ฝ”๋“œ(๋ถ€๊ฐ€๊ธฐ๋Šฅ) + ๊ทธ ์ฝ”๋“œ์˜ ์œ„์น˜ ์„ ๋ณ„ ๊ธฐ๋Šฅ )

  • ์Šคํ”„๋ง AOP์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ํŠน๋ณ„ํ•œ ์šฉ์–ด


โœ๏ธ ํƒ€๊ฒŸ (Target)

  • ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๋‹ด๊ณ ์žˆ๋Š” ๋ชจ๋“ˆ
    ( ๋ถ€๊ฐ€๊ธฐ๋Šฅ(Advice)์ด ์ ์šฉ๋  ๊ฐ์ฒด )

  • ํฌ์ธํŠธ์ปท(Pointcut)์œผ๋กœ ์–ด๋Š ํƒ€๊ฒŸ์— ๋“ค์–ด๊ฐˆ์ง€ ๊ฒฐ์ •๋จ
    ( ์œ„์น˜ ์„ ๋ณ„ ๊ธฐ๋Šฅ์œผ๋กœ ๊ฒฐ์ • )


โœ๏ธ ์œ„๋น™ (Weaving)

  • ํฌ์ธํŠธ์ปท(Pointcut)์œผ๋กœ ๊ฒฐ์ •ํ•œ ํƒ€๊ฒŸ(Target)์˜ ์กฐ์ธํฌ์ธํŠธ(Join Point)์— ์–ด๋“œ๋ฐ”์ด์Šค(Advice)๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ
    ( ์œ„์น˜ ์„ ๋ณ„ ๊ธฐ๋Šฅ์œผ๋กœ ๊ฒฐ์ •ํ•œ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๋ชจ๋“ˆ์˜ AOP ์ ์šฉ ๊ฐ€๋Šฅ ํฌ์ธํŠธ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ฝ”๋“œ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ )

โžœ ํ”„๋ก์‹œ ๋ฐฉ์‹์„ ํ†ตํ•ด์„œ AOP๊ฐ€ ๊ตฌํ˜„๋˜๋Š” ๊ณผ์ •

  • ์ปดํŒŒ์ผ ํƒ€์ž„
    โžœ .java ํŒŒ์ผ -> .class ํŒŒ์ผ๋กœ ์ปดํŒŒ์ผ๋˜๋Š” ์‹œ์ ์— Aspect๊ฐ€ ์‚ฝ์ž…๋œ
    โžœ ์Šคํ”„๋ง AOP์—์„œ๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€ / AspectJ ์‚ฌ์šฉํ•  ๋•Œ ์‚ฌ์šฉ
    โ €
  • ํด๋ž˜์Šค ๋กœ๋“œ ํƒ€์ž„
    โžœ ์ปดํŒŒ์ผ ์ดํ›„์— ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐ€๋Š” ์‹œ์ ์— AOP๊ฐ€ ์ ์šฉ
    โžœ ์Šคํ”„๋ง AOP์—์„œ๋Š” ์‚ฌ์šฉ ๋ถˆ๊ฐ€ / AspectJ ์‚ฌ์šฉํ•  ๋•Œ ์‚ฌ์šฉ
    โ €
  • ๋Ÿฐํƒ€์ž„(์‹คํ–‰์ค‘์ผ ๋•Œ) == ํ”„๋ก์‹œ ๋ฐฉ์‹
    โžœ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์›๋ณธ ๊ฐ์ฒด ๋Œ€์‹  ํ”„๋ก์‹œ ๋นˆ์œผ๋กœ ๋“ฑ๋ก
    (IoC๋ฅผ ํ†ตํ•ด์„œ)
    ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋นˆ์„ ์ƒ์„ฑํ•  ๋•Œ ์ด ์š”์ฒญ์„ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๊ฐ€๋กœ์ฑ„์„œ @Aspect๊ฐ€ ๋˜์–ด์žˆ๋Š” ๊ฐ์ฒด๋“ค์„ ์‹น ๋ชจ์•„์„œ ์ฐ ๊ฐ์ฒด์— ๋„ฃ์–ด์คŒ

โœ๏ธ AOP ํ”„๋ก์‹œ (proxy)

  • AOP ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ์œ„ํ•ด ๋งŒ๋“  ํ”„๋ก์‹œ ๊ฐ์ฒด

    โœ”๏ธ ํ”„๋ก์‹œ (Proxy)

    • '๋Œ€๋ฆฌ' ๋ผ๋Š” ์˜๋ฏธ
    • ๋‹ค๋ฅธ ๋ฌด์–ธ๊ฐ€์™€ ์ด์–ด์ง€๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์—ญํ• ์„ ํ•˜๋Š” ํด๋ž˜์Šค
    • ํƒ€๊ฒŸ์„ ๊ฐ์‹ธ์„œ ์š”์ฒญ์„ ๋Œ€์‹  ๋ฐ›์•„์ฃผ๋Š” ๋žฉํ•‘ ํด๋ž˜์Šค
      ( Spring์—์„œ๋Š” Proxy๋ฅผ ์ด์šฉํ•ด OOP์˜ 5๋Œ€ ์›์น™ ์ค‘ ํ•˜๋‚˜์ธ OCP ์ ์šฉ )
    • ํ”„๋ก์‹œ ๊ฐ์ฒด = ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์™ธ์— ๊ณตํ†ต์ ์ธ ๊ธฐ๋Šฅ์„ ๋‹ด์€ ๊ฐ์ฒด
      ( ์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ํƒ€๊ฒŸ ๊ฐ์ฒด์— ์ ์šฉํ•˜๋ฉด์„œ ์ƒ์„ฑ๋˜๋Š” ๊ฐ์ฒด )
      โžœ ์›๋ž˜ ๊ฐ์ฒด๋ฅผ ๊ฐ์‹ธ์„œ client์˜ ์š”์ฒญ์„ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•จ

Ex. ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ์‹ค์ œ ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ฐ์ฒด์™€ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๋Œ€์‹ ํ•˜์—ฌ ์‹ค์ œ ์š”์ฒญ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐ€์ง

  • ์Šคํ”„๋ง์—์„œ ์ƒ์„ฑํ•˜๋Š” AOP ํ”„๋ก์‹œ๋Š” JDK ๋™์  ํ”„๋ก์‹œ or CGLIB ํ”„๋ก์‹œ์ž„

โœ” IoC ์ปจํ…Œ์ด๋„ˆ์™€ AOP Proxy์˜ ๊ด€๊ณ„

Spring AOP๋Š” Proxy์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ AOP Proxy๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Œ !

์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ Spring AOP๋Š” ์‚ฌ์šฉ์ž์˜ ํŠน์ • ํ˜ธ์ถœ ์‹œ์ ์— IoC ์ปจํ…Œ์ด๋„ˆ์— ์˜ํ•ด AOP๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” Proxy Bean์„ ์ƒ์„ฑํ•ด์คŒ

๋™์ ์œผ๋กœ ์ƒ์„ฑ๋œ Proxy Bean์€ ํ•ต์‹ฌ๊ธฐ๋Šฅ ๋ชจ๋“ˆ(Target)์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ์‹œ์ ์—, ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋ฉ”์†Œ๋“œ๋ฅผ ์ž์ฒด์ ์œผ๋กœ ํŒ๋‹จํ•˜๊ณ  ๊ฐ€๋กœ์ฑ„์–ด ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ฃผ์ž…ํ•จ

โžœ ํ˜ธ์ถœ ์‹œ์ ์— ๋™์ ์œผ๋กœ ์œ„๋น™์„ ํ•œ๋‹ค๊ณ  ํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์œ„๋น™(Runtime Weaving)์ด๋ผ๊ณ  ํ•จ

๋”ฐ๋ผ์„œ Spring AOP๋Š” ๋Ÿฐํƒ€์ž„ ์œ„๋น™์˜ ๋ฐฉ์‹์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ๊ณ ,

Spring์—์„  ๋Ÿฐํƒ€์ž„ ์œ„๋น™์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ƒํ™ฉ์— ๋”ฐ๋ผ JDK Dynamic Proxy ๋ฐฉ์‹์™€ CGLIB ๋ฐฉ์‹์„ ํ†ตํ•ด Proxy Bean์„ ์ƒ์„ฑ์„ ํ•ด์คŒ

โœ” ๋‘ AOP Proxy๋Š” ์–ด๋–ค ์ƒํ™ฉ์—์„œ ์ƒ์„ฑ๋ ๊นŒ?


Spring์€ AOP Proxy๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ ์ž์ฒด ๊ฒ€์ฆ ๋กœ์ง์„ ํ†ตํ•ด ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค ์œ ๋ฌด๋ฅผ ํŒ๋‹จํ•จ

์ด ๋•Œ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์œ ๋ฌด์— ๋”ฐ๋ผ,

ํƒ€๊ฒŸ(Target)์ด ํ•˜๋‚˜ ์ด์ƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ํด๋ž˜์Šค๋ผ๋ฉด โžœ JDK Dynamic Proxy ๋ฐฉ์‹์œผ๋กœ,
( But, ๊ธฐ๋ณธ์ ์œผ๋กœ Spring์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœ )

์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์€ ํด๋ž˜์Šค๋ผ๋ฉด โžœ CGLIB์˜ ๋ฐฉ์‹์œผ๋กœ AOP ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑ

[์ฐธ๊ณ ] https://gmoon92.github.io/spring/aop/2019/04/20/jdk-dynamic-proxy-and-cglib.html


โ— ๐Ÿ’ฌ ์ •๋ฆฌ !!

ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•  ๋•Œ, ๋” ์ข‹์€ ๊ฐ์ฒด์ง€ํ–ฅ์„ ํ•˜๊ณ  ๋ฐ˜๋ณต์ ์ธ ์ž‘์—…์„ ํ†ต์ผํ•ด ํ•œ๋ฒˆ์— ํ•˜์—ฌ ํšจ์œจ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด AOP (Aspect Oriented Programming) ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

์ด AOP๋Š” ํ”„๋กœ์ ํŠธ ๋‚ด์˜ ์—ฌ๋Ÿฌ ๊ฐ์ฒด์— ๊ณตํ†ต์œผ๋กœ ์ ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ(Aspect)๋“ค๋งŒ ๊ธฐ๋Šฅ๋ณ„๋กœ ๋”ฐ๋กœ ๋ฌถ์–ด, ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค.

์ด ๋•Œ ๋”ฐ๋กœ ๋ฌถ์€ Aspect๋“ค์€ ๊ทธ ๊ธฐ๋Šฅ์„ ์–ด๋””์—(์–ด๋Š ํƒ€๊ฒŸ์—) ์ ์šฉํ•  ์ง€ ์จ์ค˜์•ผํ•˜๋Š”๋ฐ
( ํƒ€๊ฒŸ - ๋”ฐ๋กœ ๋ฌถ์€ ๊ณตํ†ต ๊ธฐ๋Šฅ๋“ค์„ ๋„ฃ์–ด์ค„ ํ•ต์‹ฌ ๊ธฐ๋Šฅ )

์ด๋Š” ํฌ์ธํŠธ์ปท์„ ํ™œ์šฉํ•˜์—ฌ ๊ทธ ๊ธฐ๋Šฅ์„ ์–ด๋Š ์œ„์น˜์— ๋„ฃ์„ ๊ฑด์ง€ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๊ณ ,

์–ด๋“œ๋ฐ”์ด์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์–ธ์ œ ๋„ฃ์„ ๊ฑด์ง€ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.
( Ex. ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ „, ํ•„๋“œ๊ฐ’ ๋ณ€๊ฒฝ ํ›„ ๋“ฑ )

์—ฌ๊ธฐ์„œ, AOP๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ง€์ ์„ ์กฐ์ธํฌ์ธํŠธ๋ผ๊ณ  ํ•˜๊ณ ,
๊ทธ๋Ÿฌ๋ฏ€๋กœ AOP๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์ง€์  == ํฌ์ธํŠธ์ปท์„ ๋„ฃ์„ ์ง€์ ์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์—

์ฆ‰, ํฌ์ธํŠธ์ปท์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์ง€์ ์„ ์กฐ์ธํฌ์ธํŠธ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

[์ฐธ๊ณ ] https://pangtrue.tistory.com/m/80


๐Ÿ’ฌ ๊ถ๊ธˆํ–ˆ๋˜ ์  !

Q1 ) AOP ๊ฐœ๋…์„ ์ ์šฉํ•  ๋•Œ, Proxy ๊ฐ์ฒด๊ฐ€ ์š”์ฒญ์„ ๋Œ€์‹  ๊ฐ€๋กœ์ฑ„์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ์™œ ๋ฐ”๋กœ ์›๋ž˜ ๊ฐ์ฒด์— ๋„ฃ์ง€ ์•Š๊ณ  Proxy ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ๋„ฃ์–ด์ฃผ๋Š” ๊ฑธ๊นŒ?
โ €
Answer1 )
๋จผ์ € Proxy ๊ฐ์ฒด๋Š” ์›๋ž˜ ๊ฐ์ฒด๋ฅผ ๊ฐ์‹ธ๋Š” ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š”๋ฐ, ์ด Proxy ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ์ ‘๊ทผ์„ ์ œ์–ดํ•˜๊ณ  ์‹ถ๊ฑฐ๋‚˜, ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉ๋จ.
์ฆ‰, Proxy ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ณตํ†ต ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๋ถ„๋ฆฌ๋˜์–ด ์—ญํ•  ๋ถ„๋ฆฌ๊ฐ€ ๋˜๊ณ  ์ฝ”๋“œ๋„ ๊น”๋”ํ•ด์ง€๊ธฐ ๋•Œ๋ฌธ์— Proxy ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ ์ธ ๊ฒƒ.
โ €
์˜ˆ๋ฅผ ๋“ค์–ด, Service๋ผ๋Š” ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ๊ณ , ์ด๋ฅผ ๊ตฌํ˜„ํ•œ MemberSevice๋ผ๋Š” ํ•˜์œ„ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ,
์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” main()์ด client๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๊ณ , MemberService๊ฐ€ ์›๋ž˜ ๊ฐ์ฒด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
์ด ๋•Œ ์›๋ž˜ ๊ฐ์ฒด์ธ MemberService์˜ ๋ฉ”์„œ๋“œ๋“ค์— ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋„ฃ๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ, ๊ฐ๊ฐ์˜ ๋ฉ”์„œ๋“œ์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์€ ํšจ์œจ์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.
๋”ฐ๋ผ์„œ ์ด ๊ฒฝ์šฐ Proxy ํŒจํ„ด์„ ์‚ฌ์šฉํ•œ๋‹ค.
Proxy ๊ฐ์ฒด๋Š” ์›๋ž˜ ๊ฐ์ฒด MemberService์™€ ๊ฐ™์€ ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค Service๋ฅผ ๊ตฌํ˜„ํ•ด์ค˜์•ผ ํ•œ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ์ด Proxy ๊ฐ์ฒด ์•ˆ์— ์›๋ž˜ ๊ฐ์ฒด MemberService๋ฅผ ์ฃผ์ž…๋ฐ›์•„์„œ ๊ทธ ์•ˆ์˜ ๋ฉ”์„œ๋“œ๋“ค์„ ์œ„์ž„๋ฐ›์•„ ์‚ฌ์šฉํ•˜๊ณ , ์›ํ•˜๋Š” ์ถ”๊ฐ€ ์ฝ”๋“œ๋ฅผ Proxy ๊ฐ์ฒด์— ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.
โ €

[์ฐธ๊ณ ] https://velog.io/@max9106/Spring-%ED%94%84%EB%A1%9D%EC%8B%9C-AOP-xwk5zy57ee

Q2 ) JoinPoint๋Š” AOP๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํŠน์ • ์ง€์ ์ด๊ณ , Pointcut์€ ์กฐ์ธํฌ์ธํŠธ์—์„œ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋  ์œ„์น˜๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ์ธ๋ฐ,
๊ทธ๋ ‡๋‹ค๋ฉด Pointcut์€ ๊ฒฐ๊ตญ ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋  ์œ„์น˜ ์ฆ‰, AOP๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์—ฌ
JoinPoint ์™€ Pointcut์˜ ์ฐจ์ด์ ์ด ๋„ˆ๋ฌด ํ–ˆ๊ฐˆ๋ ธ๋‹ค.
โ €
Answer2 )
JoinPoint๊ฐ€ Advice๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์œ„์น˜๋“ค(AOP ์ ์šฉ ์ง€์ ์— ๋Œ€ํ•œ ๋ฉ”ํƒ€์ ์ธ ์ •๋ณด)์ด๊ณ ,
Pointcut์€ ์ข€ ๋” ๊ตฌ์ฒด์ ์ธ ์ ์šฉ ์ง€์ ์œผ๋กœ ํŠน์ • ๋ฉ”์†Œ๋“œ์— ์–ด๋””์— ์–ด๋“œ๋ฐ”์ด์Šค๊ฐ€ ์ ์šฉ๋  ์ˆ˜ ์žˆ๋Š” ์ง€์— ๋Œ€ํ•œ ์ •๋ณด์ด๋‹ค.
โ €
์˜ˆ๋ฅผ ๋“ค์–ด์„œ MemberService์˜ hello()๋ผ๋Š” ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „,ํ›„์— hello์™€ bye๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์ผ์„ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•  ๋•Œ,
MemberService ๋นˆ = Target
"hello() ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „,ํ›„" = ํฌ์ธํŠธ์ปท
"๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „,ํ›„" = ์กฐ์ธํฌ์ธํŠธ
"hello๋ž‘ bye๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์ผ" = Advice
์ธ ๊ฒƒ์ด๋‹ค.

Q3 ) AnnotationConfigApplicationContext์™€ ApplicationContext์˜ ์ฐจ์ด์™€ /
๋ฐ‘์˜ ์ฝ”๋“œ์—์„œ AnnotationConfigApplicationContext๋กœ๋งŒ ์ •์˜ํ•ด๋„ ๋˜๋Š”๋ฐ ApplicationContext๋กœ ๋งŒ๋“œ๋Š” ์ด์œ ๊ฐ€ ๊ถ๊ธˆํ–ˆ๋‹ค.

AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(AppConfig.class);
โ €
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

Answer3 )

์ด ์œ„ ํ‘œ๋ฅผ ๋ณด๊ณ  ์ƒ์œ„ ํ•˜์œ„ ๊ด€๊ณ„๋ผ๋Š” ๊ฑด ์•Œ์•˜๋Š”๋ฐ ์ด ์ดํ›„๋กœ๋Š” ์ดํ•ด๊ฐ€ ๊ฐ€์ง€ ์•Š์•„ ์ฐพ์•„๋ณด์•˜๋Š”๋ฐ, ๋‚˜์™€ ๊ฐ™์€ ๊ถ๊ธˆ์ฆ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ์ด ์žˆ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด ํ•˜์œ„ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๊ทธ ๊ตฌํ˜„์ฒด๊ฐ€ ์ˆ˜์ •๋˜์–ด ๋‹ค๋ฅธ ํ•˜์œ„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ฐ”๋€Œ์–ด๋„ ๊ฒฐ๊ตญ์—” ๊ทธ๊ฒƒ๋„ ์ € ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—,
์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ธฐ๋Šฅ(๋ฉ”์„œ๋“œ)์ด ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์ด์ƒ ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ธฐ๋Šฅ๋“ค์€ ๋ณ€๊ฒฝํ•  ์ผ์ด ์—†์–ด์ ธ์„œ
๋‚˜์ค‘์— ๊ตฌํ˜„์ฒด๋ฅผ ์ˆ˜์ •ํ•˜๋”๋ผ๋„ ๊ทธ ๊ตฌํ˜„์ฒด๋งŒ์˜ ๋ฉ”์„œ๋“œ๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ข€ ๋” ์ˆ˜์ •ํ•  ์ผ์ด ์ ์–ด์ง„๋‹ค !
โ €
๋˜ ์ถ”๊ฐ€์ ์œผ๋กœ, ์ƒ์œ„ ์ธํ„ฐํŽ˜์ด์Šค์ธ ApplicationContext๋ฅผ ํ†ตํ•ด ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ํ˜•์‹์„ ์ €์žฅํ•˜๋ฉด getBeanDefinition() ๋ฉ”์„œ๋“œ๊ฐ€ ์‚ฌ์šฉ์ด ์•ˆ๋˜๋‹ˆ ์ฃผ์˜ํ•˜์ž !


๐ŸŒˆ ๋Š๋‚€์ 

์˜ค๋Š˜ ์‹ค์Šต์„ ํ•˜๋ฉด์„œ ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์น˜๊ณ  ์–ด๋–ค ์ƒํ™ฉ์—์„œ ์“ฐ์ด๋Š”์ง€ ์ข€ ๋” ์ž˜ ์ดํ•ด๊ฐ€ ๋๋‹ค!!
์—ญ์‹œ ์ง์ ‘ ๋งŽ์ด ์ณ๋ด์•ผํ•˜๋‚˜๋ณด๋‹ค
์‹ค์Šต ๋‚ด์šฉ์€ Project ํด๋” ๋‚ด์˜ aop !!

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