๐Ÿš€ Spring AOP๋กœ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๊ธฐ: ๊ฐœ๋ฐœ์ž์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ! ๐Ÿ•’

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Spring AOP๋กœ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๊ธฐ: ๊ฐœ๋ฐœ์ž์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ! ๐Ÿ•’

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ๊ฐœ๋ฐœ์ž ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ๊ฟ€์žผ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด์š”. ๋ฐ”๋กœ Spring AOP๋ฅผ ํ™œ์šฉํ•œ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด๊ฑฐ ์ง„์งœ ๊ฐœ๋ฐœ์ž๋“ค์˜ ํ•„์ˆ˜ ์Šคํ‚ฌ์ด๋ผ๊ณ  ํ•ด๋„ ๊ณผ์–ธ์ด ์•„๋‹ˆ์ฃ ! ๐Ÿ˜Ž

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

๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฐ ๊ฐœ๋ฐœ ์Šคํ‚ฌ์„ ์ตํžˆ๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์˜ ์žฌ๋Šฅ์„ ๋”์šฑ ๋น›๋‚˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์š”. ํ˜น์‹œ ์•„์„ธ์š”? ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)๊ฐ™์€ ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์—์„œ ์ด๋Ÿฐ ์‹ค๋ ฅ์„ ๋ฝ๋‚ด๋ฉด ์ข‹์€ ๊ธฐํšŒ๊ฐ€ ์˜ฌ์ง€๋„ ๋ชจ๋ฅด๋‹ˆ๊นŒ์š”! ๐Ÿ˜‰

์ž, ๊ทธ๋Ÿผ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ์š”? ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿง Spring AOP๊ฐ€ ๋ญ๊ธธ๋ž˜? ์ดˆ๋ณด์ž๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์„ค๋ช…!

์šฐ์„ , Spring AOP๊ฐ€ ๋ญ”์ง€๋ถ€ํ„ฐ ์•Œ์•„๋ณผ๊นŒ์š”? AOP๋Š” Aspect-Oriented Programming์˜ ์•ฝ์ž๋กœ, ํ•œ๊ตญ์–ด๋กœ๋Š” '๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ'์ด๋ผ๊ณ  ํ•ด์š”. ์–ด๋จธ, ๋ญ”๊ฐ€ ์–ด๋ ค์›Œ ๋ณด์ด์ฃ ? ใ…‹ใ…‹ใ…‹ ๊ฑฑ์ • ๋งˆ์„ธ์š”! ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด๋“œ๋ฆด๊ฒŒ์š”.

AOP๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์„ ๋ถ„๋ฆฌํ•ด์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋ฒ•์ด์—์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋กœ๊น…์ด๋‚˜ ๋ณด์•ˆ, ํŠธ๋žœ์žญ์…˜ ๊ด€๋ฆฌ ๊ฐ™์€ ๊ฒƒ๋“ค์ด์ฃ . ์ด๋Ÿฐ ๊ธฐ๋Šฅ๋“ค์„ 'ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ(Cross-cutting concerns)'๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๋ฐ, ์ด๊ฒŒ ๋ฐ”๋กœ AOP์˜ ํ•ต์‹ฌ์ด์—์š”!

๐ŸŽฏ AOP์˜ ํ•ต์‹ฌ ํฌ์ธํŠธ:

  • ์ฝ”๋“œ์˜ ์ค‘๋ณต์„ ์ค„์ผ ์ˆ˜ ์žˆ์–ด์š”.
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜์š”.
  • ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์‰ฌ์›Œ์ ธ์š”.
  • ์ฝ”๋“œ๋ฅผ ๋” ๊น”๋”ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค˜์š”.

์ž, ์ด์ œ AOP๊ฐ€ ๋ญ”์ง€ ๋Œ€์ถฉ ๊ฐ์ด ์˜ค์‹œ๋‚˜์š”? ๊ทธ๋Ÿผ ์ด๊ฑธ Spring์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”?

Spring AOP๋Š” Spring ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” AOP ๊ตฌํ˜„์ฒด์˜ˆ์š”. Spring์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค์ด ์‰ฝ๊ฒŒ AOP๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ฑฐ์ฃ . ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์‰ฝ๊ฒŒ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”! ๐Ÿ˜„

Spring AOP๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๊ฒŒ ๋ฐ”๋กœ Spring AOP์˜ ๋งˆ๋ฒ•์ด์ฃ ! โœจ

Spring AOP ๊ฐœ๋…๋„ Core Concern Logging Security Transaction Performance

์œ„์˜ ๊ทธ๋ฆผ์„ ๋ณด์„ธ์š”. ๊ฐ€์šด๋ฐ ์žˆ๋Š” 'Core Concern'์ด ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด์—์š”. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ฃผ๋ณ€์„ ๋‘˜๋Ÿฌ์‹ธ๊ณ  ์žˆ๋Š” ๊ฒƒ๋“ค์ด ๋ฐ”๋กœ AOP๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” 'ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ'๋“ค์ด์ฃ . ์ด๋ ‡๊ฒŒ AOP๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ต์‹ฌ ๋กœ์ง๊ณผ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”!

์ด์ œ Spring AOP๊ฐ€ ๋ญ”์ง€ ์ข€ ๊ฐ์ด ์˜ค์‹œ๋‚˜์š”? ใ…‹ใ…‹ใ…‹ ์–ด๋ ต์ง€ ์•Š์ฃ ? ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ€๊ธฐ ์ „์— ์ž ๊น ์‰ฌ์–ด๊ฐ€๋Š” ํƒ€์ž„! ๐Ÿต

โ˜• ์ž ๊น! ๊ฐœ๋ฐœ์ž ์œ ๋จธ ํƒ€์ž„!

Q: ๊ฐœ๋ฐœ์ž๊ฐ€ ์ปคํ”ผ๋ฅผ ๋งˆ์‹œ๋Š” ์ด์œ ๋Š”?
A: ์ž๋ฐ”(Java)๋ฅผ ๋” ์ž˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ์š”! ใ…‹ใ…‹ใ…‹

์ž, ์ด์ œ Spring AOP์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ์•Œ์•˜์œผ๋‹ˆ, ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ๊ฒŒ์š”. ํŠนํžˆ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋Š” ๋ฐ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ž์„ธํžˆ ์‚ดํŽด๋ณผ ๊ฑฐ์˜ˆ์š”. ๊ธฐ๋Œ€๋˜์ง€ ์•Š๋‚˜์š”? ๐Ÿ˜†

๐Ÿ› ๏ธ Spring AOP๋กœ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •ํ•˜๊ธฐ: ์‹ค์ „ ๊ฐ€์ด๋“œ!

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ Spring AOP๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ฒŒ์š”. ์ด๊ฑด ์ •๋ง ์œ ์šฉํ•œ ๊ธฐ์ˆ ์ด์—์š”! ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์–ด๋–ค ๋ฉ”์„œ๋“œ๊ฐ€ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ผ์œผํ‚ค๋Š”์ง€, ์–ด๋–ค ๋ถ€๋ถ„์„ ์ตœ์ ํ™”ํ•ด์•ผ ํ•˜๋Š”์ง€ ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๊ฑฐ๋“ ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ž์‹ ์˜ ์žฌ๋Šฅ์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ•ด์•ผ ํ• ์ง€ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ์š”! ๐Ÿ˜‰

๊ทธ๋Ÿผ ์ด์ œ ๋‹จ๊ณ„๋ณ„๋กœ ์•Œ์•„๋ณผ๊นŒ์š”? Let's go! ๐Ÿš€

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

๋จผ์ € Spring AOP๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ด์š”. Maven์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด pom.xml ํŒŒ์ผ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Gradle์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด build.gradle ํŒŒ์ผ์— ์ด๋ ‡๊ฒŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ผ์š”:

implementation 'org.springframework.boot:spring-boot-starter-aop'

์˜์กด์„ฑ ์ถ”๊ฐ€, ์ฐธ ์‰ฝ์ฃ ? ใ…‹ใ…‹ใ…‹ ์ด์ œ Spring AOP๋ฅผ ์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋์–ด์š”!

2. Aspect ํด๋ž˜์Šค ๋งŒ๋“ค๊ธฐ

๋‹ค์Œ์œผ๋กœ, ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•  Aspect ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”. ์ด ํด๋ž˜์Šค๊ฐ€ ๋ฐ”๋กœ ์šฐ๋ฆฌ์˜ AOP ๋งˆ๋ฒ•์„ ๋ถ€๋ฆฌ๋Š” ์ฃผ์ธ๊ณต์ด์—์š”! ๐Ÿ˜Ž

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

@Aspect
@Component
public class ExecutionTimeAspect {

    @Around("execution(* com.example..*(..))")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object proceed = joinPoint.proceed();
        
        long executionTime = System.currentTimeMillis() - start;
        
        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        
        return proceed;
    }
}

์šฐ์™€, ์ฝ”๋“œ๊ฐ€ ์ข€ ์žˆ์ฃ ? ใ…‹ใ…‹ใ…‹ ํ•˜๋‚˜์”ฉ ์„ค๋ช…ํ•ด๋“œ๋ฆด๊ฒŒ์š”!

๐Ÿ” ์ฝ”๋“œ ํ•ด์„ค:

  • @Aspect: ์ด ํด๋ž˜์Šค๊ฐ€ Aspect์ž„์„ ๋‚˜ํƒ€๋‚ด์š”.
  • @Component: Spring์ด ์ด ํด๋ž˜์Šค๋ฅผ ๋นˆ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•ด์š”.
  • @Around: ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ „ํ›„์— ๋กœ์ง์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์–ด๋“œ๋ฐ”์ด์Šค์˜ˆ์š”.
  • execution(* com.example..*(..)): ์ด๊ฑด ํฌ์ธํŠธ์ปท ํ‘œํ˜„์‹์ด์—์š”. com.example ํŒจํ‚ค์ง€์™€ ๊ทธ ํ•˜์œ„ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•œ๋‹ค๋Š” ๋œป์ด์ฃ .
  • ProceedingJoinPoint: ์›๋ž˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฐ์ฒด์˜ˆ์š”.
  • System.currentTimeMillis(): ํ˜„์žฌ ์‹œ๊ฐ„์„ ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„๋กœ ๊ฐ€์ ธ์™€์š”.
  • joinPoint.proceed(): ์›๋ž˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด์š”.

์ด ์ฝ”๋“œ๋Š” ์ง€์ •๋œ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ณ  ์ฝ˜์†”์— ์ถœ๋ ฅํ•ด์š”. ์ง„์งœ ํŽธํ•˜์ฃ ? ๐Ÿ˜„

3. ์„ค์ • ํ™œ์„ฑํ™”ํ•˜๊ธฐ

๋งˆ์ง€๋ง‰์œผ๋กœ, Spring์—๊ฒŒ AOP๋ฅผ ์‚ฌ์šฉํ•  ๊ฑฐ๋ผ๊ณ  ์•Œ๋ ค์ค˜์•ผ ํ•ด์š”. ๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํด๋ž˜์Šค์— @EnableAspectJAutoProxy ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

์งœ์ž”! ์ด์ œ ๋ชจ๋“  ์ค€๋น„๊ฐ€ ๋๋‚ฌ์–ด์š”. ๐Ÿ‘

4. ์‹คํ–‰ ๊ฒฐ๊ณผ ํ™•์ธํ•˜๊ธฐ

์ด์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด, com.example ํŒจํ‚ค์ง€์™€ ๊ทธ ํ•˜์œ„ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์ฝ˜์†”์— ์ถœ๋ ฅ๋  ๊ฑฐ์˜ˆ์š”. ์˜ˆ๋ฅผ ๋“ค๋ฉด ์ด๋Ÿฐ ์‹์œผ๋กœ์š”:

com.example.service.UserService.findUser executed in 15ms
com.example.controller.UserController.getUser executed in 20ms

์–ด๋•Œ์š”? ์ •๋ง ์‰ฝ๊ณ  ํŽธํ•˜์ฃ ? ใ…‹ใ…‹ใ…‹ ์ด๋ ‡๊ฒŒ Spring AOP๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ ํ•œ ์ค„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ๊ฐœ๋ฐœ์ž์˜ ๊ฟˆ์ด์ฃ ! ๐Ÿ˜†

๐Ÿ’ก Pro Tip: ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” System.out.println() ๋Œ€์‹  ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ(์˜ˆ: SLF4J + Logback)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„์š”. ์„ฑ๋Šฅ๋„ ๋” ์ข‹๊ณ , ๋กœ๊ทธ ๋ ˆ๋ฒจ ์กฐ์ •๋„ ๊ฐ€๋Šฅํ•˜๊ฑฐ๋“ ์š”!

์ž, ์ด์ œ Spring AOP๋กœ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋์–ด์š”. ์ด ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์„ ์‰ฝ๊ฒŒ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์ž์‹ ์˜ ์žฌ๋Šฅ์„ ๊ณ„์† ๋ฐœ์ „์‹œํ‚ค๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์ฃ ! ๐Ÿ˜‰

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด ๊ธฐ์ˆ ์„ ๋” ๋ฐœ์ „์‹œ์ผœ ๋ณผ๊ฒŒ์š”. ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•˜๊ณ , ๋” ์œ ์šฉํ•œ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ๊ธฐ๋Œ€๋˜์ง€ ์•Š๋‚˜์š”? ๐Ÿคฉ

๐Ÿš€ Spring AOP ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •: ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค!

์•ˆ๋…•ํ•˜์„ธ์š”, ๊ฐœ๋ฐœ ๊ณ ์ˆ˜๋‹˜๋“ค! ใ…‹ใ…‹ใ…‹ ์ด์ œ Spring AOP๋กœ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•์€ ์•Œ์•˜์ฃ ? ๊ทธ๋Ÿผ ์ด์ œ ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ์ˆ ๋“ค์„ ์•Œ์•„๋ณผ ์ฐจ๋ก€์˜ˆ์š”. ์ด ๊ธฐ์ˆ ๋“ค์„ ๋งˆ์Šคํ„ฐํ•˜๋ฉด ์—ฌ๋Ÿฌ๋ถ„์€ ์ง„์ •ํ•œ AOP ๋งˆ์Šคํ„ฐ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ˜Ž

1. ์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜ ๋งŒ๋“ค๊ธฐ

์ง€๊ธˆ๊นŒ์ง€๋Š” ํŠน์ • ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ–ˆ์–ด์š”. ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ํŠน์ • ๋ฉ”์„œ๋“œ๋งŒ ์ธก์ •ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ๊ฒ ์ฃ ? ์ด๋Ÿด ๋•Œ ์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋ฉด ์•„์ฃผ ํŽธ๋ฆฌํ•ด์š”!

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}

์ด๋ ‡๊ฒŒ ์ปค์Šคํ…€ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค๊ณ , Aspect ํด๋ž˜์Šค๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ฃผ์„ธ์š”:

@Around("@annotation(LogExecutionTime)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    // ๊ธฐ์กด ์ฝ”๋“œ์™€ ๋™์ผ
}

์ด์ œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ณ  ์‹ถ์€ ๋ฉ”์„œ๋“œ์—๋งŒ @LogExecutionTime ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์ด๋ฉด ๋ผ์š”. ์™„์ „ ์‰ฝ์ฃ ? ใ…‹ใ…‹ใ…‹

2. ์ž„๊ณ„๊ฐ’ ์„ค์ •ํ•˜๊ธฐ

๋ชจ๋“  ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์„ ๋กœ๊น…ํ•˜๋ฉด ๋กœ๊ทธ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„์งˆ ์ˆ˜ ์žˆ์–ด์š”. ๊ทธ๋ž˜์„œ ํŠน์ • ์‹œ๊ฐ„ ์ด์ƒ ๊ฑธ๋ฆฌ๋Š” ๋ฉ”์„œ๋“œ๋งŒ ๋กœ๊น…ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์ฃ . ์ด๋Ÿด ๋•Œ๋Š” ์ž„๊ณ„๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ๋ผ์š”!

@Around("@annotation(LogExecutionTime)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object proceed = joinPoint.proceed();
    long executionTime = System.currentTimeMillis() - start;
    
    if (executionTime > 500) { // 500ms ์ด์ƒ ๊ฑธ๋ฆฌ๋Š” ๋ฉ”์„œ๋“œ๋งŒ ๋กœ๊น…
        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
    }
    
    return proceed;
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‹คํ–‰ ์‹œ๊ฐ„์ด 500ms๋ฅผ ๋„˜๋Š” ๋ฉ”์„œ๋“œ๋งŒ ๋กœ๊น…๋ผ์š”. ์ง„์งœ ํšจ์œจ์ ์ด์ฃ ? ๐Ÿ‘

3. ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ ๋กœ๊น…ํ•˜๊ธฐ

์‹คํ–‰ ์‹œ๊ฐ„๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฉ”์„œ๋“œ์— ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์ „๋‹ฌ๋๋Š”์ง€๋„ ์•Œ๋ฉด ๋” ์œ ์šฉํ•˜๊ฒ ์ฃ ? ์ด๊ฒƒ๋„ AOP๋กœ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”!

@Around("@annotation(LogExecutionTime)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    
    // ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ ๋กœ๊น…
    Object[] args = joinPoint.getArgs();
    for (Object arg : args) {
        System.out.println("Argument: " + arg);
    }
    
    Object proceed = joinPoint.proceed();
    long executionTime = System.currentTimeMillis() - start;
    
    System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
    
    return proceed;
}

์ด์ œ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ „๋‹ฌ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ๊นŒ์ง€ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋””๋ฒ„๊น…ํ•  ๋•Œ ์ •๋ง ์œ ์šฉํ•˜๊ฒ ์ฃ ? ใ…‹ใ…‹ใ…‹

4. ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ ์ฒ˜๋ฆฌํ•˜๊ธฐ

Spring์˜ @Async ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? ์ด๊ฒƒ๋„ AOP๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”!

@Around("@annotation(org.springframework.scheduling.annotation.Async)")
public Object measureAsyncExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object proceed = joinPoint.proceed();
    
    if (proceed instanceof Future) {
        Future future = (Future) proceed;
        try {
            future.get(); // ๋น„๋™๊ธฐ ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ
        } catch (ExecutionException e) {
            // ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
        }
    }
    
    long executionTime = System.currentTimeMillis() - start;
    System.out.println(joinPoint.getSignature() + " async executed in " + executionTime + "ms");
    
    return proceed;
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„๋„ ์ •ํ™•ํ•˜๊ฒŒ ์ธก์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ํ”„๋กœ ๊ฐœ๋ฐœ์ž ๋Š๋‚Œ ๋‚˜์ฃ ? ๐Ÿ˜Ž

๐Ÿšจ ์ฃผ์˜์‚ฌํ•ญ: ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ธก์ •ํ•  ๋•Œ๋Š” ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์–ด์š”. ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด์š”!

5. ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ์— ๋”ฐ๋ฅธ ๋กœ๊น…

๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋กœ๊น…ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ๊ฒ ์ฃ ? ์˜ˆ๋ฅผ ๋“ค์–ด, ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋” ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ๋กœ๊น…ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”:

@Around("@annotation(LogExecutionTime)")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long start = System.currentTimeMillis();
    Object proceed = null;
    try {
        proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " executed successfully in " + executionTime + "ms");
    } catch (Exception e) {
        long executionTime = System.currentTimeMillis() - start;
        System.err.println(joinPoint.getSignature() + " failed after " + executionTime + "ms");
        System.err.println("Exception: " + e.getMessage());
        throw e;
    }
    return proceed;
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์„ฑ๊ณตํ–ˆ์„ ๋•Œ์™€ ์‹คํŒจํ–ˆ์„ ๋•Œ๋ฅผ ๊ตฌ๋ถ„ํ•ด์„œ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์—๋Ÿฌ ์ถ”์ ์ด ํ›จ์”ฌ ์‰ฌ์›Œ์ง€๊ฒ ์ฃ ? ๐Ÿ‘

6. ๋กœ๊ทธ ๋ ˆ๋ฒจ ์กฐ์ •ํ•˜๊ธฐ

System.out.println() ๋Œ€์‹  ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. SLF4J์™€ Logback์„ ์‚ฌ์šฉํ•œ ์˜ˆ์‹œ๋ฅผ ๋ณผ๊นŒ์š”?

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Aspect
@Component
public class ExecutionTimeAspect {
    private static final Logger logger = LoggerFactory.getLogger(ExecutionTimeAspect.class);

    @Around("@annotation(LogExecutionTime)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;
        
        logger.info("{} executed in {}ms", joinPoint.getSignature(), executionTime);
        
        return proceed;
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ INFO, DEBUG, ERROR ๋“ฑ์œผ๋กœ ์‰ฝ๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์šด์˜ ํ™˜๊ฒฝ๊ณผ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋‹ค๋ฅธ ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ? ์™„์ „ ํŽธ๋ฆฌํ•ด์š”! ใ…‹ใ…‹ใ…‹

7. ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ํšŸ์ˆ˜ ์นด์šดํŒ…ํ•˜๊ธฐ

์‹คํ–‰ ์‹œ๊ฐ„๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ช‡ ๋ฒˆ ํ˜ธ์ถœ๋๋Š”์ง€๋„ ์•Œ๋ฉด ์œ ์šฉํ•˜๊ฒ ์ฃ ? ์ด๊ฒƒ๋„ AOP๋กœ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”!

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Aspect
@Component
public class ExecutionTimeAspect {
    private final ConcurrentHashMap<string atomiclong> methodInvocationCounts = new ConcurrentHashMap<>();

    @Around("@annotation(LogExecutionTime)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        methodInvocationCounts.computeIfAbsent(methodName, k -> new AtomicLong()).incrementAndGet();

        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        long count = methodInvocationCounts.get(methodName).get();
        System.out.println(methodName + " executed " + count + " times. Last execution time: " + executionTime + "ms");

        return proceed;
    }
}
</string>

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ๋ฉ”์„œ๋“œ์˜ ํ˜ธ์ถœ ํšŸ์ˆ˜์™€ ๋งˆ์ง€๋ง‰ ์‹คํ–‰ ์‹œ๊ฐ„์„ ํ•จ๊ป˜ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ ํŒจํ„ด์„ ๋ถ„์„ํ•˜๋Š” ๋ฐ ์ •๋ง ์œ ์šฉํ•˜๊ฒ  ์ฃ ? ๐Ÿ˜‰

๐Ÿ’ก Pro Tip: ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ํšŸ์ˆ˜์™€ ์‹คํ–‰ ์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ซ์ŠคํŒŸ(์ž์ฃผ ํ˜ธ์ถœ๋˜๊ฑฐ๋‚˜ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฉ”์„œ๋“œ)์„ ์‰ฝ๊ฒŒ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ์„ฑ๋Šฅ ์ตœ์ ํ™”์— ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค!

8. ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ํ†ต๊ณ„ ๋‚ด๊ธฐ

๊ฐ ๋ฉ”์„œ๋“œ์˜ ํ‰๊ท  ์‹คํ–‰ ์‹œ๊ฐ„, ์ตœ์†Œ ์‹คํ–‰ ์‹œ๊ฐ„, ์ตœ๋Œ€ ์‹คํ–‰ ์‹œ๊ฐ„ ๋“ฑ์˜ ํ†ต๊ณ„๋ฅผ ๋‚ด๊ณ  ์‹ถ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”? ์ด๊ฒƒ๋„ AOP๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด์š”!

import java.util.concurrent.ConcurrentHashMap;

@Aspect
@Component
public class ExecutionTimeAspect {
    private final ConcurrentHashMap<string methodstats> methodStats = new ConcurrentHashMap<>();

    @Around("@annotation(LogExecutionTime)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().toShortString();
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        methodStats.computeIfAbsent(methodName, k -> new MethodStats()).addExecutionTime(executionTime);

        MethodStats stats = methodStats.get(methodName);
        System.out.println(methodName + " stats: " + stats);

        return proceed;
    }

    private static class MethodStats {
        private long count = 0;
        private long totalTime = 0;
        private long minTime = Long.MAX_VALUE;
        private long maxTime = 0;

        public synchronized void addExecutionTime(long time) {
            count++;
            totalTime += time;
            minTime = Math.min(minTime, time);
            maxTime = Math.max(maxTime, time);
        }

        @Override
        public String toString() {
            return String.format("count=%d, avg=%d ms, min=%d ms, max=%d ms",
                    count, totalTime / count, minTime, maxTime);
        }
    }
}
</string>

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ํšŸ์ˆ˜, ํ‰๊ท  ์‹คํ–‰ ์‹œ๊ฐ„, ์ตœ์†Œ ์‹คํ–‰ ์‹œ๊ฐ„, ์ตœ๋Œ€ ์‹คํ–‰ ์‹œ๊ฐ„์„ ํ•œ ๋ฒˆ์— ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ํ”„๋กœํŽ˜์…”๋„ํ•˜์ฃ ? ๐Ÿ˜Ž

9. ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ๋กœ๊น…ํ•˜๊ธฐ

๋•Œ๋กœ๋Š” ํŠน์ • ์กฐ๊ฑด์—์„œ๋งŒ ๋กœ๊น…์„ ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋กœ๊น…์„ ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์— ๋Œ€ํ•ด์„œ๋งŒ ๋กœ๊น…์„ ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์ฃ . ์ด๋Ÿด ๋•Œ๋Š” Spring์˜ ํ”„๋กœํ•„ ๊ธฐ๋Šฅ๊ณผ AOP๋ฅผ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ์š”!

import org.springframework.context.annotation.Profile;

@Aspect
@Component
@Profile("dev") // ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์ด Aspect๊ฐ€ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.
public class ExecutionTimeAspect {
    // ... ๊ธฐ์กด ์ฝ”๋“œ ...
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์šด์˜ ํ™˜๊ฒฝ์˜ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ๋„ ๊ฐœ๋ฐœ ์ค‘์—๋Š” ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์ฃ . ์™„์ „ ์Šค๋งˆํŠธํ•˜์ง€ ์•Š๋‚˜์š”? ใ…‹ใ…‹ใ…‹

10. ๋กœ๊ทธ ํฌ๋งท ์ปค์Šคํ„ฐ๋งˆ์ด์ง•

๋งˆ์ง€๋ง‰์œผ๋กœ, ๋กœ๊ทธ ํฌ๋งท์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•ด์„œ ๋” ์ฝ๊ธฐ ์‰ฝ๊ณ  ๋ถ„์„ํ•˜๊ธฐ ์ข‹์€ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด๋ณผ๊นŒ์š”? JSON ํ˜•์‹์œผ๋กœ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•˜๋ฉด ๋‚˜์ค‘์— ๋กœ๊ทธ ๋ถ„์„ ๋„๊ตฌ๋กœ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.

import com.fasterxml.jackson.databind.ObjectMapper;

@Aspect
@Component
public class ExecutionTimeAspect {
    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Around("@annotation(LogExecutionTime)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;

        LogData logData = new LogData(
            joinPoint.getSignature().toShortString(),
            executionTime,
            Arrays.toString(joinPoint.getArgs())
        );

        System.out.println(objectMapper.writeValueAsString(logData));

        return proceed;
    }

    private static class LogData {
        public String method;
        public long executionTime;
        public String args;

        public LogData(String method, long executionTime, String args) {
            this.method = method;
            this.executionTime = executionTime;
            this.args = args;
        }
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋กœ๊ทธ๊ฐ€ JSON ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ๋ผ์š”. ๋กœ๊ทธ ๋ถ„์„์ด ํ›จ์”ฌ ์‰ฌ์›Œ์ง€๊ฒ ์ฃ ? ๐Ÿ‘

๐ŸŽ‰ ์ถ•ํ•˜ํ•ด์š”! ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ Spring AOP๋ฅผ ์‚ฌ์šฉํ•œ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ๊ฐ„ ์ธก์ •์˜ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค์„ ๋ชจ๋‘ ๋งˆ์Šคํ„ฐํ–ˆ์–ด์š”. ์ด ๊ธฐ์ˆ ๋“ค์„ ํ™œ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ๋”์šฑ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ ์ž์‹ ์˜ ์žฌ๋Šฅ์„ ๊ณ„์†ํ•ด์„œ ๋ฐœ์ „์‹œํ‚ค๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์ฃ ! ๐Ÿ˜‰

์ž, ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ ์ง„์ •ํ•œ Spring AOP ๋งˆ์Šคํ„ฐ๊ฐ€ ๋์–ด์š”! ์ด ๊ธฐ์ˆ ๋“ค์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด์„ธ์š”. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์ด ๋ˆˆ์— ๋„๊ฒŒ ๊ฐœ์„ ๋˜๋Š” ๊ฑธ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๊ทธ๋ฆฌ๊ณ  ์žŠ์ง€ ๋งˆ์„ธ์š”, ๊ฐœ๋ฐœ์€ ๋Š์ž„์—†๋Š” ํ•™์Šต์˜ ๊ณผ์ •์ด์—์š”. ์žฌ๋Šฅ๋„ท์—์„œ ์ƒˆ๋กœ์šด ์žฌ๋Šฅ์„ ๋ฐฐ์šฐ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ํ•ญ์ƒ ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ์ตํžˆ๊ณ  ๋ฐœ์ „์‹œ์ผœ ๋‚˜๊ฐ€์„ธ์š”! ๐Ÿš€

๋‹ค์Œ์— ๋˜ ์žฌ๋ฏธ์žˆ๊ณ  ์œ ์šฉํ•œ ๊ฐœ๋ฐœ ํŒ์œผ๋กœ ์ฐพ์•„์˜ฌ๊ฒŒ์š”. ๊ทธ๋•Œ๊นŒ์ง€ ํ•ดํ”ผ ์ฝ”๋”ฉํ•˜์„ธ์š”! ๐Ÿ˜„๐Ÿ‘จโ€๐Ÿ’ป๐Ÿ‘ฉโ€๐Ÿ’ป