λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

SpringπŸƒ

Spring AOP

728x90

좜처 : 내일배움캠프

 

Aspect Oriented Programming : 관점지ν–₯ ν”„λ‘œκ·Έλž˜λ°

  1. AOP κ°œλ… 이해
  2. 'λ‚˜λ§Œμ˜ 셀렉샡' 에 μŠ€ν”„λ§ AOP 적용
    1. Top5 νšŒμ› μ°ΎκΈ°
    2. 쀑볡 폴더λͺ… μ €μž₯ μ‹œ μ—λŸ¬ 처리
    3. μŠ€ν”„λ§ μ˜ˆμ™Έ 처리

μ„œλ²„λ₯Ό μ‚¬μš©ν•œ μ‹œκ°„ ~ λͺ¨λ“  API에 μš”μ²­, μ‘λ‹΅ν•œ μ‹œκ°„μ˜ 합을 κΈ°μ€€μœΌλ‘œ μƒμœ„ 5인 μ„ λ°œ

슀크래치 νŒŒμΌμ„ μ΄μš©ν•œ μ„œλ²„ μ‚¬μš©μ‹œκ°„ μΈ‘μ •

+ 슀크래치 νŒŒμΌμ€ λ³„λ„λ‘œ μ €μž₯λ˜λŠ” μž„μ‹œνŒŒμΌ, ν”„λ‘œμ νŠΈμ™€ κ΄€λ ¨μ—†λŠ” μ½”λ“œλ₯Ό λ‹€λ£° λ•Œ μœ μš©ν•˜λ‹€

슀크래치 파일 경둜

class Scratch {
    public static void main(String[] args) {
        // μΈ‘μ • μ‹œμž‘ μ‹œκ°„
        long startTime = System.currentTimeMillis();

        // ν•¨μˆ˜ μˆ˜ν–‰
        long output = sumFromOneTo(1_000_000_000);

        // μΈ‘μ • μ’…λ£Œ μ‹œκ°„
        long endTime = System.currentTimeMillis();

        long runTime = endTime - startTime;
        System.out.println("μ†Œμš”μ‹œκ°„: " + runTime);
    }

    private static long sumFromOneTo(long input) {
        long output = 0;

        for (int i = 1; i < input; ++i) {
            output = output + i;
        }

        return output;
    }
}

output의 값을 μ΄ˆκΈ°κ°’ 1초둜 μ„€μ •ν•œ ν›„, μ‚¬μš©μ‹œκ°„λ§ŒνΌ 값을 μ¦κ°€μ‹œν‚€λŠ” λ°˜λ³΅λ¬Έμ„ 톡해 μ‹œκ°„μ˜ 합을 κ΅¬ν•œλ‹€

TOP 5 νšŒμ› μ°ΎκΈ° 섀계 및 κ΅¬ν˜„

컬럼λͺ… νƒ€μž… μ€‘λ³΅ν—ˆμš© μ„€λͺ…
id Long X ν…Œμ΄λΈ” ID(PK)
userId Long X νšŒμ› ID(FK)
totalTime Long O API 총 μ‚¬μš©μ‹œκ°„

ApiUseTime 생성

ApiUseTimeRepository 생성

ProductController μ‚¬μš©μ‹œκ°„ 기둝 μΆ”κ°€

(κ΄€λ¦¬μž) νšŒμ›λ³„ API 총 μ‚¬μš©μ‹œκ°„ 쑰회 API 섀계 및 κ΅¬ν˜„

κΈ°λŠ₯ λ©”μ„œλ“œ URL μ„€λͺ…
API 총 μ‚¬μš©μ‹œκ°„ 쑰회 GET /api/use/time νšŒμ›λ³„ API 총 μ‚¬μš©μ‹œκ°„ 쑰회
(κ΄€λ¦¬μžμš©)

ApiUseTimeController 생성

 

AOPλž€?

각 API 별 μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” μ£Όμš” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ œμ™Έν•œ λΆ€κ°€κΈ°λŠ₯을 λͺ¨λ“ˆν™”ν•˜μ—¬ λΆ€κ°€κΈ°λŠ₯ μ€‘μ‹¬μœΌλ‘œ 섀계 κ΅¬ν˜„ν•˜λŠ” 것

~ μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” AOP

AOPλ₯Ό μ‚¬μš©ν•΄ λͺ¨λ“  μ»¨νŠΈλ‘€λŸ¬μ— λΆ€κ°€κΈ°λŠ₯ μΆ”κ°€

package com.sparta.myselectshop.aop;

import com.sparta.myselectshop.entity.ApiUseTime;
import com.sparta.myselectshop.entity.User;
import com.sparta.myselectshop.repository.ApiUseTimeRepository;
import com.sparta.myselectshop.security.UserDetailsImpl;
import lombok.RequiredArgsConstructor;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

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

    private final ApiUseTimeRepository apiUseTimeRepository;

    @Around("execution(public * com.sparta.myselectshop.controller..*(..))")
    public synchronized Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        // μΈ‘μ • μ‹œμž‘ μ‹œκ°„
        long startTime = System.currentTimeMillis();

        try {
            // 핡심기λŠ₯ μˆ˜ν–‰
            Object output = joinPoint.proceed();
            return output;
        } finally {
            // μΈ‘μ • μ’…λ£Œ μ‹œκ°„
            long endTime = System.currentTimeMillis();
            // μˆ˜ν–‰μ‹œκ°„ = μ’…λ£Œ μ‹œκ°„ - μ‹œμž‘ μ‹œκ°„
            long runTime = endTime - startTime;

            // 둜그인 νšŒμ›μ΄ μ—†λŠ” 경우, μˆ˜ν–‰μ‹œκ°„ κΈ°λ‘ν•˜μ§€ μ•ŠμŒ
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            if (auth != null && auth.getPrincipal().getClass() == UserDetailsImpl.class) {
                // 둜그인 νšŒμ› 정보
                UserDetailsImpl userDetails = (UserDetailsImpl) auth.getPrincipal();
                User loginUser = userDetails.getUser();

                // API μ‚¬μš©μ‹œκ°„ 및 DB 에 기둝
                ApiUseTime apiUseTime = apiUseTimeRepository.findByUser(loginUser)
                        .orElse(null);
                if (apiUseTime == null) {
                    // 둜그인 νšŒμ›μ˜ 기둝이 μ—†μœΌλ©΄
                    apiUseTime = new ApiUseTime(loginUser, runTime);
                } else {
                    // 둜그인 νšŒμ›μ˜ 기둝이 이미 있으면
                    apiUseTime.addUseTime(runTime);
                }

                log.info("[API Use Time] Username: " + loginUser.getUsername() + ", Total Time: " + apiUseTime.getTotalTime() + " ms");
                apiUseTimeRepository.save(apiUseTime);

            }
        }
    }
}

AOP 적용 μ „

AOP 적용 ν›„

μŠ€ν”„λ§ AOP μ–΄λ…Έν…Œμ΄μ…˜μ˜ μ’…λ₯˜

1. @Aspect

μŠ€ν”„λ§ 빈 ν΄λž˜μŠ€μ— μ μš©κ°€λŠ₯

2. μ–΄λ“œλ°”μ΄μŠ€μ˜ μ’…λ₯˜ ~ AOP μ½”λ“œκ°€ μ‹€ν–‰λ˜λŠ” μ‹œμ μ„ 지정해쀀닀

- @Around : '핡심기λŠ₯' μˆ˜ν–‰ μ „κ³Ό ν›„(@Before + @After)

- @Before : '핡심기λŠ₯' μˆ˜ν–‰ μ „(ex : Client의 μž…λ ₯κ°’ Vaildation μˆ˜ν–‰)

- @After : '핡심기λŠ₯' μˆ˜ν–‰ 성곡/μ‹€νŒ¨ 여뢀와 관계없이 μ–Έμ œλ‚˜ λ™μž‘ (try, catch의 finally()와 같은 λ™μž‘ 방식)

- @AfterReturning : '핡심기λŠ₯' 호좜 μ„±κ³΅μ‹œ λ™μž‘ν•˜μ—¬ ν•¨μˆ˜μ˜ Returnκ°’ μ‚¬μš©κ°€λŠ₯

- @AfterThrowing : '핡심기λŠ₯' 호좜 μ‹€νŒ¨μ‹œ, μ˜ˆμ™Έκ°€ λ°œμƒν•œ κ²½μš°μ—λ§Œ λ™μž‘ (ex : μ˜ˆμ™Έλ°œμƒμ‹œ κ°œλ°œμžμ—κ²Œ 톡지기λŠ₯)

3. 포인트컷 ~ AOP μ½”λ“œκ°€ μ‹€ν–‰λ˜λŠ” μž₯μ†Œλ₯Ό μ •ν•΄μ€€λ‹€

예제 -> @μ–΄λ“œλ°”μ΄μŠ€(포인트컷)의 ν˜•νƒœ

execution(

modifiers-pattern? 

return-type-pattern declaring-type-pattern? 

method-name-pattern(param-pattern) throws-pattern?)​
@Around("execution(public * com.sparta.springcore.controller..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable { ... }

modifiers-pattern : pubilc, private, *

return-type-pattern : void, String, List<String>, *

declaring-type-pattern : 클래슀λͺ…(νŒ¨ν‚€μ§€λͺ… ν•„μš”)

~ com.sparta.springcore.controller.* : controller νŒ¨ν‚€μ§€μ˜ λͺ¨λ“  ν΄λž˜μŠ€μ— 적용

~ com.sparta.springcore.controller.. : controller νŒ¨ν‚€μ§€ 및 ν•˜μœ„ νŒ¨ν‚€μ§€μ˜ λͺ¨λ“  ν΄λž˜μŠ€κΉŒμ§€ 적용

method-name-pattern(param-pattern)

ν•¨μˆ˜λͺ…

addFolders : addFolders() ν•¨μˆ˜μ—λ§Œ 적용

add* : add둜 μ‹œμž‘ν•˜λŠ” λͺ¨λ“  ν•¨μˆ˜μ— 적용

νŒŒλΌλ―Έν„° νŒ¨ν„΄

(com.sparta.springcore.dto.FolderRequestDto) - FolderRequestDto μΈμˆ˜μ—λ§Œ 적용

() - 인수 μ—†μŒ

(*) - νƒ€μž… 상관없이 인수 1개

(..) - νƒ€μž… 상관없이 인수 1~N개

728x90

'SpringπŸƒ' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

Spring Transaction  (0) 2022.12.29
Spring Exception  (0) 2022.12.29
ν…ŒμŠ€νŠΈ μ½”λ“œ  (0) 2022.12.27
OAuth2  (0) 2022.12.27
μŠ€ν”„λ§ μˆ™λ ¨ : Project MySelectShop (3)  (0) 2022.12.15