๐Ÿ”ฎSpring

[AOP] AOP Aspect ๋ฅผ ์ด์šฉํ•œ ๋กœ๊ทธ ์ฒ˜๋ฆฌ ํ•˜๊ธฐ

harry.93 2022. 4. 18. 11:36
๋ฐ˜์‘ํ˜•

AOP ๋ฐ ๊ตฌ์„ฑ์š”์†Œ ๊ฐ„๋‹จ ์„ค๋ช…

์Šคํ”„๋ง ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ ์ค‘ ํ•˜๋‚˜์ธ AOP ๋Š” ๋กœ์ง์„ ๊ด€์‹ฌ์‚ฌ(Aspect)๋ผ๋Š” ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
OOP์—์„œ ๋ชจ๋“ˆํ™”์˜ ํ•ต์‹ฌ ๋‹จ์œ„๋Š” ํด๋ž˜์Šค์ธ ๋ฐ˜๋ฉด, AOP์˜ ๋ชจ๋“ˆํ™” ๋‹จ์œ„๋Š” Aspect ์ž…๋‹ˆ๋‹ค.

์ข…์†์„ฑ ์ฃผ์ž…์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ์ฒด๋ฅผ ์„œ๋กœ ๋ถ„๋ฆฌํ•˜๋Š”๋ฐ ๋„์›€์ด ๋˜๊ณ , AOP๋Š” ๊ฐœ์ฒด์™€ ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

[์šฉ์–ด ์„ค๋ช…]

Aspect
- ํšก๋‹จ ๊ด€์‹ฌ์‚ฌ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ชจ๋“ˆ, ์˜ˆ๋ฅผ ๋“ค์–ด ์ง€๊ธˆ ์ž‘์„ฑํ•˜๋ คํ•˜๋Š” ๋กœ๊น…์„ ์œ„ํ•œ ๋ชจ๋“ˆ์„ ๋กœ๊น…์„ ์œ„ํ•œ Aspect ๋ผ๊ณ  ํ•œ๋‹ค.

Join Point
- AOP ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…ํ•  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์œ„์น˜

Advice
- ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „์ด๋‚˜ ํ›„์— ์ทจํ•ด์•ผ ํ•  ์‹ค์ œ ์กฐ์น˜

Point Cut
- Advice๊ฐ€ ์‹คํ–‰๋˜๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ Join Point ์œ„์น˜, ํ‘œํ˜„์‹์ด๋‚˜ ํŒจํ„ด์„ ์ด์šฉํ•ด ์ง€์ • ๊ฐ€๋Šฅ.

Target Object
- ํ•˜๋‚˜ ์ด์ƒ์˜ Aspect ์—์„œ Advice ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฐœ์ฒด. ํ•ญ์ƒ ํ”„๋ก์‹œ ๊ฐ์ฒด์ด๋‹ค.

Advice Type

before - ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „
after - ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ํ›„(์—๋Ÿฌ ๊ด€๊ณ„์—†์ด)
after-returning - ๋ฉ”์†Œ๋“œ "์ •์ƒ" ์‹คํ–‰ ํ›„
after-throwing - ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์‹œ, ์—๋Ÿฌ throwingํ•œ ๊ฒฝ์šฐ
around - ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „/ํ›„ 


From : https://www.tutorialspoint.com/springaop/springaop_implementations.htm

 

Spring AOP - Implementations

Spring AOP - Implementations Spring supports the @AspectJ annotation style approach and the schema-based approach to implement custom aspects. XML Schema Based Aspects are implemented using regular classes along with XML based configuration. To use the AOP

www.tutorialspoint.com

 

Log Aspect ์ž‘์„ฑ

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

๋‹จ์ˆœํžˆ, ์‹คํ–‰ ์ „/ํ›„๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ํŠน์ • ์ปจํŠธ๋กค๋Ÿฌ Path ๋กœ๋งŒ Aspect ๋ฅผ ์žก๊ฒŒ ๋˜๋ฉด ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ ๋‚ด์— ์žˆ๋Š” ์ œ์™ธ๋˜์–ด์•ผ ํ•˜๋Š” ํ˜น์€ ์ œ์™ธํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ตฌ๋ถ„ํ•ด๋‚ด๊ธฐ๋Š” ์‰ฝ์ง€ ์•Š๋‹ค.

๋”ฐ๋ผ์„œ, ์•„๋ฌด ์ž‘์—…์„ ํ•˜์ง€ ์•Š๋Š” ๋นˆ Annotation์„ ๋งŒ๋“ค์–ด, ํ•ด๋‹น Annotation์„ Aspect์—์„œ ์ œ์™ธ ์‹œํ‚ค๋ฉด, ์ƒ๊ธฐ ๋ชฉ์ ์„ ์ด๋ฃฐ ์ˆ˜ ์žˆ๋‹ค.

์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ PointCut ํ‘œํ˜„์‹์œผ๋กœ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•˜๋‹ค.NoLogging.java 

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NoLogging {

}

* ์‚ฌ์šฉ

@NoLogging
@RestController
public void index() {
	...
}

 

๊ทธ๋ฆฌ๊ณ  PoingCut์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ‘œํ˜„์‹์„ ์ž‘์„ฑํ•œ๋‹ค

@Pointcut("execution(* com.example.controller..*.*(..)) && !@annotation(com.example.aspect.NoLogging)")
private void cut() {
	// ์ปจํŠธ๋กค๋Ÿฌ ๊ฒฝ๋กœ ๋‚ด๋ถ€ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ + NoLogging ์–ด๋…ธํ…Œ์ด์…˜ ๋ถ™์–ด์žˆ๋Š” ๋ฉ”์†Œ๋“œ ์ œ์™ธ
}

 

์œ„์™€ ๊ฐ™์€ PointCut ์„ ๋งŒ๋“ค๊ณ , Advice์— ์ ์šฉํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์•„๋ž˜๋Š” ์ž‘์„ฑํ•œ ์ฝ”๋“œ์ด๋‹ค.

LogAspect.java

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

    // ๋กœ๊ทธ ์ €์žฅ์šฉ ์„œ๋น„์Šค
    @Autowired
    LogSvc svc;

    /**
     * Log Aspect๋ฅผ ์ ์šฉํ•  ํŒจํ‚ค์ง€/ํƒ€์ž…/๊ฒฝ๋กœ
     * 
     * ex) controller ๊ฒฝ๋กœ / service ๊ฒฝ๋กœ / ๋ฉ”์†Œ๋“œ ๋“ฑ
     * ํŠน์ • ํด๋”(ํŒจํ‚ค์ง€) : com.example.controller.menuMngt..*.*(..)
     * ํŠน์ • ๋ฉ”์†Œ๋“œ(ํŒŒ๋ผ๋ฏธํ„ฐํฌํ•จ) : com.example.service.findId(HashMap<String, Object>)
     * 
     * [๊ตฌ์กฐ]
     * 1. * : ๋ฆฌํ„ด ํƒ€์ž… ์ง€์ • (*:anything | public string | ...)
     * 2. com.acaas.admin.controller..*.* : ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ ๋ฐ ๋ฉ”์†Œ๋“œ ๋ช… ๋“ฑ
     * 3. (..) : ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž…
     * 
     * .. ๋” ๋งŽ์€ ์ •๋ณด : https://www.baeldung.com/spring-aop-pointcut-tutorial
     * 
     * '!@annotation(com.acaas.admin.aspect.NoLogging)'
     * NoLogging ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์†Œ๋“œ๋Š” ์ œ์™ธํ•œ๋‹ค.
     */
    @Pointcut("execution(* com.acaas.admin.controller..*.*(..)) && !@annotation(com.acaas.admin.aspect.NoLogging)")
    private void cut() {
    }

    /**
     * ๋ฉ”์†Œ๋“œ ์ „ ๊ตฌ์—ญ
     * 
     * @param jp
     */
    @Around("cut()")
    public void Around(JoinPoint jp) {
        logInsert(jp);
    }

    /**
     * ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์ „
     * 
     * @param jp
     */
    @Before("cut()")
    public void Before(JoinPoint jp) {
        String logType = "Before";
        logInsert(jp, logType, "-", "");
    }

    /**
     * ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ํ›„ <๊ธฐ๋ณธ>
     * !! AfterReturning ๊ณผ Throwing ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ๊ธฐ๋ณธ After๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€ ๊ฐ€๋Šฅํ•จ. !!
     * 
     * @param jp
     */
    /*
     * @After("cut()")
     * public void After(JoinPoint jp) {
     * String logType = "After";
     * logInsert(jp, logType);
     * }
     */

    /**
     * ๋ฉ”์†Œ๋“œ ์ •์ƒ ์ข…๋ฃŒ ํ›„
     * 
     * @param jp
     */
    @AfterReturning("cut()")
    public void AfterReturning(JoinPoint jp) {
        String logType = "AfterReturning";
        logInsert(jp, logType, "200", "");
    }

    /**
     * ๋ฉ”์†Œ๋“œ ์—๋Ÿฌ ( Exception ) ๋ฆฌํ„ด ๊ฒฝ์šฐ
     * 
     * @param jp
     */
    @AfterThrowing(value = "cut()", throwing = "e")
    public void AfterThrowing(JoinPoint jp, Exception e) {
        String logType = "AfterThrowing";
        logInsert(jp, logType, "500", e.getMessage());
    }

    /**
     * ์‹คํ–‰๋œ ๋ฉ”์†Œ๋“œ ๋ช… ๊ฐ€์ ธ์˜ค๊ธฐ
     * 
     * @param joinPoint
     * @return
     */
    private Method getMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getMethod();
    }

    /**
     * ๋กœ๊ทธ ์ž‘์—… ๊ตฌ๊ฐ„
     * ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ตฌ๊ฐ„
     * 
     * @param jp
     * @param logType
     * @param status
     * @param errorLog
     * @return
     */
    private int logInsert(JoinPoint jp, String logType, String status, String errorLog) {
        // ๋ฉ”์†Œ๋“œ ๋ช… : ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‚ฌ์šฉ
        Method method = getMethod(jp);
        // ๋ฉ”์†Œ๋“œ ์‹คํ–‰ ์‹œ ์ž…๋ ฅ๋œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
        Object[] args = jp.getArgs();
        // ํŒŒ๋ผ๋ฏธํ„ฐ ๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ
        HashMap<String, Object> paramMap = new HashMap<String, Object>();
        for (Object arg : args) {
            String type = arg.getClass().getSimpleName();
            // ํŠน์ • ํƒ€์ž…๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
            if (type.equals("HashMap<String, Object>")) {
                paramMap.putAll((HashMap<String, Object>) arg);
            }
        }
        // url check
        String chkUrl = paramMap.getStr("mappingUrl");
        boolean pass = this.chkUrl(chkUrl, paramMap);

        // url pass ?
        if (pass) {
            // ๋กœ๊ทธ ํ…Œ์ด๋ธ”์— ์ €์žฅ
            HashMap<String, Object> logMap = new HashMap<String, Object>();

            String userId = "";
            String userNm = "";
            if (JavaUtil.NVL(paramMap.get("session"), "").equals("")) {
                userId = "No Session :: userId";
                userNm = "No Session :: userNm";
            } else {
                userId = paramMap.get("session").getStr("id");
                userNm = paramMap.get("session").getStr("nm");
            }
            String url = paramMap.getStr("mappingUrl");
            String description = this.getDescription(url, paramMap);
            String urlParameter = paramMap.toString();

            logMap.put("logType", logType);
            logMap.put("userId", userId);
            logMap.put("userNm", userNm);
            logMap.put("url", url);
            logMap.put("description", description);
            logMap.put("urlParameter", urlParameter);
            logMap.put("status", status);
            logMap.put("errorLog", errorLog);
            logger.info("[Aspect] " + logType + " logMap : " + logMap);
            return svc.logInsert(logMap);
        } else {
            // none
            return -500;
        }
    }

    /**
     * URL check <DB>
     * 
     * @param url
     * @param paramMap
     * @return
     */
    public boolean chkUrl(String url, HashMap<String, Object> paramMap) {
        String splitUrl = url.split("/admin")[1];
        logger.info("[LOG] chkUrl : " + splitUrl);
        // 1. ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์œผ๋กœ ์ œ์™ธ URL ์ฒดํฌ
        if (svc.chkTask(splitUrl, "black") > 0) {
            return false;
        } else {
            // 2. ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ฐฉ์‹์œผ๋กœ DB์— ๋“ฑ๋ก๋œ task์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ์ฒดํฌ
            if (svc.chkTask(splitUrl, "white") > 0) {
                return true;
            } else {
                return false;
            }
        }
    }

    /**
     * ํ•ด๋‹น ์ž‘์—… ๋งคํ•‘๋œ ์ž‘์—…๋‚ด์šฉ ๊ฐ€์ ธ์˜ค๊ธฐ <DB>
     * 
     * @param url
     * @param paramMap
     * @return
     */
    public String getDescription(String url, HashMap<String, Object> paramMap) {
        String splitUrl = url;
        return svc.getDescription(splitUrl);
    }

}

 

 

728x90
๋ฐ˜์‘ํ˜•