๐ 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์ ๋ง๋ฒ์ด์ฃ ! โจ
์์ ๊ทธ๋ฆผ์ ๋ณด์ธ์. ๊ฐ์ด๋ฐ ์๋ '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 ๋ง์คํฐ๊ฐ ๋์ด์! ์ด ๊ธฐ์ ๋ค์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด์ธ์. ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ด ๋์ ๋๊ฒ ๊ฐ์ ๋๋ ๊ฑธ ๊ฒฝํํ ์ ์์ ๊ฑฐ์์. ๊ทธ๋ฆฌ๊ณ ์์ง ๋ง์ธ์, ๊ฐ๋ฐ์ ๋์์๋ ํ์ต์ ๊ณผ์ ์ด์์. ์ฌ๋ฅ๋ท์์ ์๋ก์ด ์ฌ๋ฅ์ ๋ฐฐ์ฐ๋ ๊ฒ์ฒ๋ผ, ํญ์ ์๋ก์ด ๊ธฐ์ ์ ์ตํ๊ณ ๋ฐ์ ์์ผ ๋๊ฐ์ธ์! ๐
๋ค์์ ๋ ์ฌ๋ฏธ์๊ณ ์ ์ฉํ ๊ฐ๋ฐ ํ์ผ๋ก ์ฐพ์์ฌ๊ฒ์. ๊ทธ๋๊น์ง ํดํผ ์ฝ๋ฉํ์ธ์! ๐๐จโ๐ป๐ฉโ๐ป
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ