Spring AOP, Cache - low-hill/Knowledge GitHub Wiki

AOP

AOP(Cross-Cutting)

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

Aspect ๊ตฌ์„ฑ

Aspect: ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ์ฒด

Advise: ์‹ค์งˆ์ ์ธ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹ด์€ ๊ตฌํ˜„์ฒด, Aspect๋Š” '๋ฌด์—‡'์„ '์–ธ์ œ' ํ• ์ง€๋ฅผ ์ •์˜

@Around, @Before, @After, @AfterReturning, @AfterThrowing PointCut: ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์ด ์ ์šฉ๋˜์–ด์•ผ ํ•  ๋Œ€์ƒ(๋ฉ”์†Œ๋“œ)์„ ์„ ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•

execution(), @annotion, ๋“ฑ๋“ฑ execution(๋ฆฌํ„ด ํƒ€์ž… ํƒ€๊ฒŸ์ด ๋˜๋Š” ๋ฉ”์†Œ๋“œ argument-๋งค๊ฐœ๋ณ€์ˆ˜) ex) execution(* com.dashboard.service.DashboardService(..)) JointPoint: Advise๊ฐ€ ์ ์šฉ๋  ์œ„์น˜, controller์—์„œ ์ •์˜๋œ method๋“ค์˜ args(๋งค๊ฐœ๋ณ€์ˆ˜)๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ฐ์ฒด

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


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

    @Autowired
    private UserAuthService userAuthService;


    @Pointcut("execution(* com.demo.dashboard.service.DashboardService.getBoard(..))")
    public void dashBoardAccountAuth() {}

    @Pointcut("execution(* com.demo.billing.service.BillingService.*(..))")
    public void billingAccountAuth() {}

    @Before("dashBoardAccountAuth() || billingAccountAuth()")
    public void before(JoinPoint joinPoint) {
        Object[] obj = joinPoint.getArgs();
        CommonReqModel model  = (CommonReqModel) obj[0];
        boolean hasDefaultAuth = userAuthService.getAuthInfo(model);
        model.setHasDefaultAuth(hasDefaultAuth);
    }
}

Cache

Cache ์ถ”์ƒํ™” ์ดํ•ดํ•˜๊ธฐ

ํ•ต์‹ฌ๋ถ€๋ถ„์—์„œ ์ถ”์ƒํ™”๋Š” Java method์— ์บ์‹ฑ์„ ์ ์šฉํ•จ์œผ๋กœ์จ ์บ์‹œ์— ๋ณด๊ด€๋œ ์ •๋ณด๋กœ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ์ค€๋‹ค. ์ฆ‰ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋ ๋•Œ๋งˆ๋‹ค ์ถ”์ƒํ™”๊ฐ€ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ๊ฐ™์€ ์ธ์ž๋กœ ์ด๋ฏธ ์‹คํ–‰๋˜์—ˆ๋Š” ํ™•์ธํ•˜๋Š” ์บ์‹ฑ ๋™์ž‘์„ ์ ์šฉํ•œ๋‹ค. ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•œ ๋’ค์— ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฐ˜ํ™˜ํ•ด์„œ ๋‹ค์Œ๋ฒˆ ํ˜ธ์ถœ์‹œ์— ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

Spring cache๋Š” cache ์ถ”์ƒํ™”๋ฅผ ์ง€์›ํ•˜๋Š”๋ฐ EhCache, Redis, Couchbase ๋“ฑ ์บ์‹œ ์ €์žฅ์†Œ์™€ ๋น ๋ฅด๊ฒŒ ์—ฐ๋™ํ•˜์—ฌ bean์œผ๋กœ ์„ค์ • ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.


์บ์‹ฑ ์ „๋žต์˜ ์ข…๋ฅ˜:

1. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ ์บ์‹œ:

@Cacheable ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ๋ฐ˜๋ณต์ ์ธ ์š”์ฒญ์— ๋Œ€ํ•ด ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ํ˜ธ์ถœ์„ ์ค„์ž…๋‹ˆ๋‹ค.

@Cacheable(value = "userCache", key = "#userId")
public User getUserById(Long userId) {
    return userRepository.findById(userId);
}

2. Redis ์บ์‹œ:

Redis๋Š” ๋น ๋ฅธ ์ฝ๊ธฐ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” In-memory ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ, ์ž์ฃผ ์กฐํšŒ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. Spring Boot์—์„œ๋Š” Spring Data Redis๋ฅผ ํ†ตํ•ด Redis์™€์˜ ์—ฐ๋™์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. Redis๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ž์ฃผ ์กฐํšŒ๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜์–ด ๋น ๋ฅด๊ฒŒ ์‘๋‹ต์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์บ์‹œ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„(1์‹œ๊ฐ„)์„ ์„ค์ •ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค๋ž˜๋œ ๊ฒฝ์šฐ์—๋Š” ์ž๋™์œผ๋กœ ๊ฐฑ์‹ ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.


์„ ์–ธ์ ์ธ ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์บ์‹ฑ

@Cacheable 

๋ฉ”์†Œ๋“œ์— ์ง€์ • ๊ฐ€๋Šฅํ•˜๊ณ  ์ง€์ •๋œ ๋ฉ”์„œ๋“œ์˜ ์บ์‹œ ์„ค์ •์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•œ๋ฒˆ ์ƒ์„ฑ๋˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹ฑ๋˜๋ฉฐ, ๋‹ค์Œ ํ˜ธ์ถœ ์‹œ์— ์บ์‹œ์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฆฌํ„ด๋œ๋‹ค.
@Cacheable ์„ค์ • ์˜ต์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
  • value
    • ์บ์‹ฑ ๊ณต๊ฐ„์˜ ๋Œ€ํ‘œ ๋ช…์นญ
  • key
    • Spring Expression Language(SpEl)์œผ๋กœ key์ƒ์„ฑ์„ ์ง€์ • 
    • ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์กฐํ•ฉํ•œ ํ•ด์‹œ์ฝ”๋“œ ๊ฐ’์„ ํ‚ค๋กœ ์ƒ์„ฑ
  • condition
    • ์กฐ๊ฑด๋ถ€ ์บ์‹ฑ. SpEL๋กœ ์ง€์ •ํ•˜๊ณ  ํ‘œํ˜„์‹์ด true๋ฉด ๋ฉ”์„œ๋“œ๋ฅผ ์บ์‹œ
์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ SpEL evaluation context
์ด๋ฆ„ ์œ„์น˜ ์„ค๋ช… ์˜ˆ์‹œ
methodName root object ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„ #root.methodName
method root object ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ #root.method.name
target root object ํ˜ธ์ถœ๋˜๋Š” ๋Œ€์ƒ ๊ฐ์ฒด #root.target
targetClass root object ํ˜ธ์ถœ๋˜๋Š” ๋Œ€์ƒ ํด๋ž˜์Šค #root.targetClass
args root object ๋Œ€์ƒ์„ ํ˜ธ์ถœํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•œ ์ธ์ž(๋ฐฐ์—ด) #root.args[0]
caches root object ํ˜„์žฌ ์‹คํ–‰๋œ ๋ฉ”์„œ๋“œ ์บ์‹œ์˜ ์ปฌ๋ ‰์…˜ #root.caches[0].name
argument name evaluation context ๋ฉ”์„œ๋“œ ์ธ์ž์˜ ์ด๋ฆ„. ์–ด๋–ค ์ด์œ ๋กœ๋“  ์ด๋ฆ„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋ฉด(์˜ˆ: ๋””๋ฒ„๊น… ์ •๋ณด๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ) a<#arg>์—์„œ ์ธ์ž ์ด๋ฆ„์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ  #arg์€ (0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š”) ์ธ์ž์˜ ์ธ๋ฑ์Šค๋ฅผ ์˜๋ฏธํ•œ๋‹ค. iban๋‚˜ a0 (p0๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๋ณ„์นญ์œผ๋กœ p<#arg> ํ˜•์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค)


์œ ์˜ํ•ด์•ผ ํ•  ์ 


@Cacheable์€ Spring AOP๋ฅผ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ ํด๋ž˜์Šค ๋‚ด @Cacheable์ด ์„ค์ •๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ ์บ์‹ฑ๋œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๊ณ  ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์‹คํ–‰ ํ•œ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์€ internal call์ด ์•„๋‹Œ Proxy Bean๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ์˜ค๋Š” ๊ฒƒ์ด๋‹ค.


์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต:

@CacheEvict ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•˜๋Š” API์—์„œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์˜ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•˜์—ฌ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜์˜ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์บ์‹œ ๊ฐฑ์‹  ์ „๋žต:

@CachePut ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์บ์‹œ๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์บ์‹œ๋„ ํ•จ๊ป˜ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

์บ์‹ฑ ์ „๋žต ์ ์šฉ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญ:

  • ์บ์‹œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„: ๋ฐ์ดํ„ฐ๋ฅผ ์–ธ์ œ ์บ์‹œ์—์„œ ์ œ๊ฑฐํ• ์ง€, ์ฆ‰ ์บ์‹œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ• ์ง€ ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ฃผ๊ธฐ๋‚˜ ์ค‘์š”๋„์— ๋”ฐ๋ผ ์„ค์ •์„ ๋‹ค๋ฅด๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์บ์‹œ ์ผ๊ด€์„ฑ: ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ ์บ์‹œ๊ฐ€ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ๊ฐฑ์‹ ๋˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด @CacheEvict์™€ @CachePut์„ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜์—ฌ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•˜๊ณ  ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
[์ฐธ๊ณ ]   https://ifuwanna.tistory.com/202















โš ๏ธ **GitHub.com Fallback** โš ๏ธ