[Spring] AOP ๊ฐœ๋… ๋ฐ ์ ์šฉํ•˜๊ธฐ

1. AOP๋ž€?

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

- ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๋ผ๋ณด๋Š” ๊ด€์ ์„ ๋ฐ”๊ฟ”๋ณด์ž๋Š” ์˜๋ฏธ

- ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ(Cross-Cutting Concern)์˜ ๋ถ„๋ฆฌ๋ฅผ ํ—ˆ์šฉํ•จ์œผ๋กœ์จ ๋ชจ๋“ˆ์„ฑ์„ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„

- ์—ฌ๋Ÿฌ ๊ฐ์ฒด์— ๊ณตํ†ต์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•ด์„œ ๊ฐœ๋ฐœ์ž๋Š” ๋ฐ˜๋ณต ์ž‘์—…์„ ์ค„์ด๊ณ  ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Œ

 

๊ฐ„๋‹จํ•˜๊ฒŒ ํ•œ์ค„๋กœ AOP๋ฅผ ์ •๋ฆฌํ•ด๋ณด์ž๋ฉด, AOP๋Š” ๊ณตํ†ต๋œ ๊ธฐ๋Šฅ์„ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค.

 

์™œ ์‚ฌ์šฉํ•ด์•ผํ• ๊นŒ??

 

 

์œ„ ์‚ฌ์ง„์€ ์šฐ์•„ํ•œ ํ…Œํฌ 10๋ถ„ ํ…Œํฌํ†ก ์œ ํŠœ๋ธŒ ์˜์ƒ์—์„œ ์บก์ณํ•œ ์‚ฌ์ง„์ด๋‹ค. 

๋งŒ์•ฝ 3๊ฐœ์˜ ๋ฉ”์†Œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด๋‹ฌ๋ผ๋Š” ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๋ฉ”์†Œ๋“œ ์‹œ์ž‘ ์ง€์ ๊ณผ ๋๋‚˜๋Š” ์ง€์ ์˜ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ด ์ด์˜ ์ฐจ์ด๋ฅผ ์ถœ๋ ฅํ•  ๊ฒƒ์ด๋‹ค. 

์ด๋Ÿฌ๋ฉด ๋ถ€๊ฐ€๊ธฐ๋Šฅ๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ํ•จ๊ป˜ ์žˆ๊ฒŒ ๋˜๊ณ , ์œ ์ง€๋ณด์ˆ˜ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐ€๋…์„ฑ๋„ ๋–จ์–ด์ง€๊ฒŒ๋œ๋‹ค. 

 

์œ„ ์‚ฌ์ง„์—์„œ  ๋ถ€๊ฐ€๊ธฐ๋Šฅ์€ ์‹คํ–‰์‹œ๊ฐ„ ์ธก์ •, ๋กœ๊น…์ด๊ณ  ์ด๋“ค์„ ๋ฌถ์€ ๊ฒƒ์ด ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ์ด๋‹ค. 

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ AOP๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์ด ์žˆ๋‹ค.

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

 

 

2. AOP์˜ ์ฃผ์š” ๊ฐœ๋…

 

Target

๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•  ๋Œ€์ƒ(ํด๋ž˜์Šค)์„ ์˜๋ฏธํ•œ๋‹ค. ๋ฐ”๋กœ ์•„๋ž˜์„œ ์„ค๋ช…ํ•˜๋Š” Aspect๊ฐ€ ์ ์šฉ๋˜๋Š” ๋Œ€์ƒ์„ ์˜๋ฏธํ•œ๋‹ค.

 

 

Aspect

๊ฐ์ฒด์ง€ํ–ฅ ๋ชจ๋“ˆ์„ ์˜ค๋ธŒ์ ํŠธ๋ผ ๋ถ€๋ฅด๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๊ฒŒ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ชจ๋“ˆ์„ Aspect๋ผ๊ณ  ๋ถ€๋ฅด๋ฉฐ, ํ•ต์‹ฌ ๊ธฐ๋Šฅ์— ๋ถ€๊ฐ€๋˜์–ด ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š” ํŠน๋ณ„ํ•œ ๋ชจ๋“ˆ ์ด๋‹ค.

Aspect = Advice + PointCut
Aspect ์•ˆ์—๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ •์˜ํ•˜๋Š” Advice,

Advice๋ฅผ ์–ด๋””์— ์ ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•˜๋Š” PointCut์ด ์žˆ๋‹ค.
์ฐธ๊ณ ๋กœ AOP๋ผ๋Š” ๋œป ์ž์ฒด๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ์ ์ธ ๊ธฐ๋Šฅ์—์„œ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•ด์„œ Aspect๋ผ๋Š” ๋ชจ๋“ˆ์„ ๋งŒ๋“ค์–ด ์„ค๊ณ„ํ•˜๊ณ  ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋œปํ•œ๋‹ค.

 

 

Advice

Aspect์—์„œ ์‹ค์งˆ์ ์œผ๋กœ ์–ด๋–ค ์ผ์„ ํ•ด์•ผํ• ์ง€์— ๋Œ€ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ๊ตฌํ˜„์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
Advice๋Š” Target Object์— ์ข…์†๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์ˆ˜ํ•˜๊ฒŒ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค. Advice๋Š” Aspect๊ฐ€ ‘๋ฌด์—‡’์„ ‘์–ธ์ œ’ํ• ์ง€๋ฅผ ์ •์˜ํ•˜๊ณ  ์žˆ๋‹ค.

 

 

JoinPoint

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

Spring ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ JoinPoint๋ผ๋ฉด Advice๊ฐ€ ์ ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

 

 

PoinCut

๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์ ์šฉ๋  ๋Œ€์ƒ(๋ฉ”์„œ๋“œ)๋ฅผ ์„ ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์˜๋ฏธํ•œ๋‹ค.
Advice๋ฅผ ์ ์šฉํ•  JoinPoint๋ฅผ ์„ ๋ณ„ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ •์˜ํ•œ ๋ชจ๋“ˆ์ด๋‹ค. JoinPoint์˜ ์ƒ์„ธํ•œ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

Proxy

Target์„ ๊ฐ์‹ธ์„œ Target์— ๋“ค์–ด์˜ค๋Š” ์š”์ฒญ์„ ๋Œ€์‹  ๋ฐ›์•„์ฃผ๋Š” Warpping ์˜ค๋ธŒ์ ํŠธ์ด๋‹ค.
Client์—์„œ Target์„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด Target์ด ์•„๋‹Œ Target์„ ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” Proxy๊ฐ€ ํ˜ธ์ถœ๋˜์–ด, Target ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „์— ์ „์ฒ˜๋ฆฌ, Target ๋ฉ”์„œ๋“œ ์‹คํ–‰ ํ›„ ํ›„์ฒ˜๋ฆฌ๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

 

 

 

3. ์‚ฌ์šฉ๋ฒ•

@Aspect
@Component
public class TestAop {
    @Around("execution(* com.gjgs.modules.users.services.interfaces.UserService.getUser(..))")
    public void adviceMethod(ProceedingJoinPoint joinPoint){    
        Object result = null;
            try {
                System.out.println("AOP ์‹œ์ž‘");
                result = proceedingJoinPoint.proceed();
                System.out.println("AOP ์ข…๋ฃŒ");
            } catch (){
                ...
            }
    }
}

 

@Around๋Š” Advice๋กœ์„œ Aspect๊ฐ€ ‘๋ฌด์—‡์„’, ์–ธ์ œ’ ํ• ์ง€๋ฅผ ์ •์˜ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ‘์–ธ์ œ’ ๋ผ๋Š” ์‹œ์ ์„ ์ •์˜ํ•œ ๊ฒƒ์ด๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด 5๊ฐ€์ง€ ํƒ€์ž…์ด ์กด์žฌํ•œ๋‹ค.

 

  • @Before
    • Advice ํƒ€๊ฒŸ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ „์— Advice ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰
  • @After
    • Target ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ์™€ ๊ด€๊ณ„์—†์ด(์„ฑ๊ณต,์˜ˆ์™ธ์™€ ๊ด€๊ณ„์—†์ด) Target ๋ฉ”์„œ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด Advice ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰
  • @AfterReturning(์ •์ƒ์  ๋ฐ˜ํ™˜ ์ดํ›„)
    • Target ๋ฉ”์„œ๋“œ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ ํ›„์— Advice ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰
  • @AfterThrowing (์˜ˆ์™ธ ๋ฐœ์ƒ ์ดํ›„)
    • Target ๋ฉ”์„œ๋“œ๊ฐ€ ์ˆ˜ํ–‰ ์ค‘ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ฒŒ ๋˜๋ฉด Advice ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰
  • @Around(๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „ํ›„)
    • Target ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ์‹ธ์„œ Target ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์ „๊ณผ ํ˜ธ์ถœ ํ›„์— Advice ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰
    • Around์˜ ๊ฒฝ์šฐ, ์ธ์ž๋กœ ProceedingJoinPoint๋ฅผ ๋ฐ›์•„์„œ proceed ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. proceed๋Š” ์‹ค์ œ Target ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋˜๊ณ  ์•ž ๋’ค์— ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „, ํ›„์— ์ง„ํ–‰ํ•  ์ž‘์—…์„ ์ฝ”๋”ฉ๋ฉด ๋œ๋‹ค.

 

 

execution(* com.gjgs.modules.users.services.impl.UserService.getUser(..))

 

Advice์˜ Value๋กœ ๋“ค์–ด๊ฐ„ ๋ฌธ์ž์—ด์„ PointCut ํ‘œํ˜„์‹ ์ด๋ผ๊ณ  ํ•œ๋‹ค.
PointCut์˜ ๊ตฌ์„ฑ์€ 2๊ฐ€์ง€๋กœ ๋ถ„๋ฅ˜๋˜๋Š”๋ฐ execution์„ ์ง€์ •์ž ๋ผ๊ณ  ๋ถ€๋ฅด๋ฉฐ ์ด์™ธ์˜ ๋’ค์ชฝ ๋ถ€๋ถ„์€ Target ๋ช…์„ธ ๋ผ๊ณ  ํ•œ๋‹ค.
์ง€์ •์ž๋Š” ์ด 9๊ฐ€์ง€ ํƒ€์ž…์ด ์žˆ์ง€๋งŒ ์ž์ฃผ ์“ฐ๋Š” execution๊ณผ @annotation ์ •๋„๋งŒ ์•Œ์•„๋ณด์ž.

 

  • execution()
    • ์ ‘๊ทผ์ œํ•œ์ž, ๋ฆฌํ„ดํƒ€์ž…, ์ธ์žํƒ€์ž…, ํด๋ž˜์Šค/์ธํ„ฐํŽ˜์ด์Šค, ๋ฉ”์†Œ๋“œ๋ช…, ํŒŒ๋ผ๋ฏธํ„ฐํƒ€์ž…, ์˜ˆ์™ธํƒ€์ž… ๋“ฑ์„ ์ „๋ถ€ ์กฐํ•ฉ๊ฐ€๋Šฅํ•œ ๊ฐ€์žฅ ์„ธ์‹ฌํ•œ ์ง€์ •์ž
    • ex) execution(* com.blogcode.service.AccountService.*(..)
      • AccountService ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ
  • @annotation()
    • Target ๋ฉ”์„œ๋“œ์— ํŠน์ • ์• ๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋œ ๊ฒฝ์šฐ
    • @annotation(org.springframework.transaction.annotation.Transactional)
      • Transactional ์• ๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋œ ๋ชจ๋“  ๋ฉ”์„œ๋“œ

 

execution ํ‘œํ˜„์‹์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ฉด

execution([์ˆ˜์‹์–ด] ๋ฆฌํ„ดํƒ€์ž… [ํด๋ž˜์Šค์ด๋ฆ„].๋ฉ”์„œ๋“œ์ด๋ฆ„(ํŒŒ๋ผ๋ฏธํ„ฐ))

 

  • ์ˆ˜์‹์–ด
    • public, private ๋“ฑ ์ˆ˜์‹์–ด๋ฅผ ๋ช…์‹œํ•˜์ง€๋งŒ ์Šคํ”„๋ง AOP์—์„œ๋Š” private๋งŒ ๊ฐ€๋Šฅ (์ƒ๋žต ๊ฐ€๋Šฅ)
  • ๋ฆฌํ„ดํƒ€์ž…
    • ๋ฆฌํ„ด ํƒ€์ž…์„ ๋ช…์‹œ
  • ํด๋ž˜์Šค์ด๋ฆ„ ๋ฐ ๋ฉ”์„œ๋“œ์ด๋ฆ„
    • ํด๋ž˜์Šค์ด๋ฆ„๊ณผ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์„ ๋ช…์‹œ (ํด๋ž˜์Šค ์ด๋ฆ„์€ ํŒจํ‚ค์ง€๋ช…๊นŒ์ง€ ํ•จ๊ป˜ ๋ช…์‹œ)
  • ํŒŒ๋ผ๋ฏธํ„ฐ
    • ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ช…์‹œ
  • ํ‘œํ˜„์‹
    • * : ๋ชจ๋“  ๊ฐ’์„ ํ‘œํ˜„
    • .. : 0๊ฐœ ์ด์ƒ์„ ์˜๋ฏธ
    • execution(void set*(..))
      • ๋ฆฌํ„ด ํƒ€์ž…์ด void์ด๊ณ  ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด set์œผ๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ, ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ 0๊ฐœ ์ด์ƒ์ธ ๋ฉ”์„œ๋“œ
    • execution(* sp.aop.service..())
      • sp.aop.service ํŒจํ‚ค์ง€์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ
    • execution(* sp.aop.service...(..))
      • sp.aop.service ํŒจํ‚ค์ง€ ๋ฐ ํ•˜์œ„ ํŒจํ‚ค์ง€์— ์žˆ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ 0๊ฐœ ์ด์ƒ์ธ ๋ชจ๋“  ๋ฉ”์„œ๋“œ
    • execution(* get())
      • get์œผ๋กœ ์‹œ์ž‘ํ•˜๊ณ  1๊ฐœ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ–๋Š” ๋ฉ”์„œ๋“œ
    • execution(Integer read*(Integer, ..))
      • ๋ฆฌ๋˜๊ฐ’์ด Integer์ด๊ณ  ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด read๋กœ ์‹œ์ž‘ํ•˜๋ฉฐ ์ฒซ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…์ด Integer์ด๊ณ , 1๊ฐœ ์ด์ƒ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ–๋Š” ๋ฉ”์„œ๋“œ

 

์ด์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด์ž.

 

๋‹น์žฅ์— ์ ์šฉํ•ด๋ณผ๋งŒํ•œ ๊ฒŒ ํŒŒ๋ผ๋ฏธํ„ฐ ๋กœ๊ทธ์ฐ๊ธฐ์™€ ์‹คํ–‰์‹œ๊ฐ„ ์ •๋„์˜€๋‹ค. 

๊ทธ ์ค‘ Service๋ ˆ์ด์–ด์˜ ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜์—ฌ ์ถœ๋ ฅํ•ด์ฃผ์—ˆ๋‹ค. ์ถ”ํ›„์— ๊ณตํ†ต๊ด€์‹ฌ์‚ฌ๊ฐ€ ์ƒ๊ธด๋‹ค๋ฉด ๊ทธ๋•Œ ์ถ”๊ฐ€๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

@Component
@RequiredArgsConstructor
@Aspect
@Slf4j
public class TimingAdvisor {

    private final SlackService slackService;

    @Pointcut("execution(public * jikgong.domain..*Service.*(..))")
    public void pointcut() {}

    @Around("pointcut()") // ์–ด๋“œ๋ฐ”์ด์Šค + ํฌ์ธํŠธ์ปท ์„ค์ •
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String methodName = proceedingJoinPoint.getSignature().getName();

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

        long end = System.currentTimeMillis();
        long runningTime = end - start;

        if (runningTime <= 1000) {
            log.info("[์ •์ƒ ์‹คํ–‰] method = {}, ์‹คํ–‰์‹œ๊ฐ„ = {} ms", methodName, runningTime);
        } else {
            String message = "[!๊ฒฝ๊ณ ] [๊ธฐ์ค€ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ดˆ๊ณผํ•˜์˜€์Šต๋‹ˆ๋‹ค] method = " + methodName + ", ์‹คํ–‰์‹œ๊ฐ„ = " + runningTime + "ms";
            log.error(message);
            // Slack ๋ฉ”์‹œ์ง€ ์ „์†ก
            HashMap<String, String> data = new HashMap<>();
            data.put("๊ฒฝ๊ณ  ๋‚ด์šฉ", message);
            slackService.sendMessage("๊ฒฝ๊ณ : ์‹คํ–‰ ์‹œ๊ฐ„ ์ดˆ๊ณผ", data);
        }
        return result;
    }
}

 

 

 

 

์–ด๋–ค ๋ฉ”์†Œ๋“œ์— ์‹คํ–‰ ์‹œ๊ฐ„์ด ์–ผ๋งˆ ๊ฑธ๋ ธ๋‹ค๋ฅผ ์ถœ๋ ฅํ•ด์ฃผ๋‹ค.

๊ธฐ์ค€ ์‹œ๊ฐ„ (1000ms) ๋ณด๋‹ค ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ ๊ฒฝ๊ณ  log ๋ฐ slack์œผ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด์ฃผ์—ˆ๊ณ , ๊ธฐ์ค€ ์‹œ๊ฐ„ ๋ณด๋‹ค ์ ๊ฒŒ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ๋Š” ์ •์ƒ ์‹คํ–‰ log๋ฅผ ์ฐ์–ด์ฃผ์–ด ๋น„์ •์ƒ์ ์œผ๋กœ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ธํŒ…ํ–ˆ๋‹ค.

 

 

 

(์ฐธ๊ณ )

slack ํ˜ธ์ถœ ์ฝ”๋“œ

@Service
@Slf4j
public class SlackService {
    @Value("${webhook.slack.url}")
    private String SLACK_WEBHOOK_URL;

    private final Slack slackClient = Slack.getInstance();

    /**
     * ์Šฌ๋ž™ ๋ฉ”์‹œ์ง€ ์ „์†ก
     **/
    public void sendMessage(String title, HashMap<String, String> data) {
        try {
            slackClient.send(SLACK_WEBHOOK_URL, payload(p -> p
                    .text(title) // ๋ฉ”์‹œ์ง€ ์ œ๋ชฉ
                    .attachments(List.of(
                            Attachment.builder().color(Color.GREEN.toString()) // ๋ฉ”์‹œ์ง€ ์ƒ‰์ƒ
                                    .fields( // ๋ฉ”์‹œ์ง€ ๋ณธ๋ฌธ ๋‚ด์šฉ
                                            data.keySet().stream().map(key -> generateSlackField(key, data.get(key))).collect(Collectors.toList())
                                    ).build())))
            );
            log.info("slack ์•Œ๋ฆผ ์ „์†ก");
        } catch (IOException e) {
            log.error("slack ์•Œ๋ฆผ ์ „์†ก ์‹คํŒจ");
            e.printStackTrace();
        }
    }

    /**
     * Slack Field ์ƒ์„ฑ
     **/
    private Field generateSlackField(String title, String value) {
        return Field.builder()
                .title(title)
                .value(value)
                .valueShortEnough(false)
                .build();
    }
}