Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바의 정석 기초편 ch3
- 자바의 정석 기초편 ch13
- @Aspect
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch6
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch11
- 스프링 db1 - 스프링과 문제 해결
- 스프링 mvc1 - 스프링 mvc
- 스프링 mvc2 - 로그인 처리
- 스프링 mvc2 - 타임리프
- 스프링 입문(무료)
- jpa - 객체지향 쿼리 언어
- 코드로 시작하는 자바 첫걸음
- 자바의 정석 기초편 ch1
- 자바의 정석 기초편 ch14
- 자바의 정석 기초편 ch5
- 2024 정보처리기사 수제비 실기
- 자바의 정석 기초편 ch8
- 자바의 정석 기초편 ch4
- 스프링 고급 - 스프링 aop
- 게시글 목록 api
- 자바의 정석 기초편 ch7
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch2
- 타임리프 - 기본기능
- 스프링 db2 - 데이터 접근 기술
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch9
Archives
- Today
- Total
나구리의 개발공부기록
스프링 AOP - 실전예제, 예제 만들기, 로그 출력 AOP, 재시도 AOP 본문
인프런 - 스프링 완전정복 코스 로드맵/스프링 핵심원리 - 고급편
스프링 AOP - 실전예제, 예제 만들기, 로그 출력 AOP, 재시도 AOP
소소한나구리 2024. 11. 15. 09:54출처 : 인프런 - 스프링 핵심 원리 - 고급편 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 예제만들기
1) 예제
- @Trace 애노테이션으로 로그 출력
- @Retry 애노테이션으로 예외 발생시 재시도
(1) ExamRepository
- exam패키지를 생성하여 작성
- save메서드가 5번 호출되면 1번은 예외가 발생하도록 적용한 저장소
package hello.aop.exam;
@Repository
public class ExamRepository {
private static int seq = 0;
/**
* 5번중 1번이 실패하는 로직을 가상으로 적용
*/
public String save(String itemId) {
seq++;
if (seq % 5 == 0) {
throw new IllegalStateException("예외 발생");
}
return "ok";
}
}
(2) ExamService
- 비즈니스 로직이 있는 서비스
package hello.aop.exam;
@Service
@RequiredArgsConstructor
public class ExamService {
private final ExamRepository examRepository;
public void request(String itemId) {
examRepository.save(itemId);
}
}
(3) ExamTest
- test 하위에 exam패키지 생성 후 작성
- 5번째 루프가 실행될 때 리포지토리에서 예외가 발생함
package hello.aop.exam;
@Slf4j
@SpringBootTest
class ExamTest {
@Autowired
ExamService examService;
@Test
void test() {
for (int i = 0; i < 5; i++) {
log.info("client request i={}", i);
examService.request("data" + i);
}
}
}
2. 로그 출력 AOP
1) 로그 출력용 AOP
- @Trace가 메서드에 붙어있으면 호출 정보가 출력되는 기능
(1) @Trace
- exam하위에 annotation 패키지 생성 후 작성
- @Target: METHOD
- @Retention: RUNTIME
package hello.aop.exam.annotation;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Trace {
}
(2) TraceAspect
- exam하위에 aop패키지를 생성 후 작성
- @Before와 @annotation 지시자를 사용하여 포인트컷을 적용하여 @Trace가 붙은 메서드에 어드바이스를 적용함
- getArgs()로 인수 정보를 조회하고 시그니처와 함께 로그를 출력
package hello.aop.exam.aop;
@Slf4j
@Aspect
public class TraceAspect {
@Before("@annotation(hello.aop.exam.annotation.Trace)")
public void before(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); // 인수 정보 조회
log.info("[trace] {} args={}", joinPoint.getSignature(), args);
}
}
(3) @Trace 적용
- ExamService의 request메서드와 ExamRepository의 save 메서드에 @Trace애노테이션을 적용
(4) ExamTest - 애스펙트 스프링빈 적용
- @Import로 로그를 출력하는 Aspect를 스프링빈으로 등록하면, 4번까지 로그를 정상 출력하는 AOP가 적용되었음
- 현재는 5번째는 예외가 발생하기때문에 테스트에 오류가 나는것이 정상임
package hello.aop.exam;
@Slf4j
@Import(TraceAspect.class)
@SpringBootTest
class ExamTest {
// 기존 로직 동일
}
3. 재시도 AOP
1) 예외 발생 시 재시도하는 AOP
- @Retry 애노테이션이 있으면 예외가 발생했을 때 다시 시도해서 문제를 복구
(1) @Retry
- 애노테이션에 int타입 속성의 기본값을 3으로 설정
- @Target과 @Retention은 @Trace와 동일
package hello.aop.exam.annotation;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retry {
int value() default 3;
}
(2) RetryAspect
- @annotation(retry), Retury retry를 사용하여 어드바이스에 애노테이션을 파라미터로 전달하고 retry.value()를 통해서 애노테이션에 지정한 값을 가져올 수 있음
- 예외가 발생하여 결과가 정상 반환되지 않으면 retry.value()만큼 재시도하고 retry.value()까지 넘어갔음에도 정상 반환되지 않는다면 그때는 exceptionHolder에 저장된 예외를 던지도록 작성
** 참고
- 참고로 Throwable은 Exception 예외보다 더 상위의 예외이기 때문에 프로그램에서 처리할 수 없는 오류가 포함되어있을 수 있으므로 try-catch로 잡지않고 throws로 던졌음
- 일반적으로 개발할 때 잡는 예외는 Exception까지만 잡음
package hello.aop.exam.aop;
import hello.aop.exam.annotation.Retry;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Slf4j
@Aspect
public class RetryAspect {
@Around("@annotation(retry)")
public Object doRetry(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
log.info("[retry] {}, retry={}", joinPoint.toShortString(), retry);
int maxRetry = retry.value();
Exception exceptionHolder = null;
for (int retryCount = 1; retryCount <= maxRetry; retryCount++) {
try {
log.info("[retry] try count={}/{}", retryCount, maxRetry);
return joinPoint.proceed();
} catch (Exception e) {
exceptionHolder = e;
}
}
throw exceptionHolder;
}
}
(3) @Retry 적용
- ExamRepository의 save메서드에 @Retry(value = 4)를 적용하여 재시도 횟수를 기본값3이 아닌 4로 적용(value 생략가능)
- @Retry같은 재시도 로직을 적용할 때 최대 횟수를 지정하는것이 매우 중요함, 실무에서 이런 조건을 빼먹고 적용하면 셀프 DDos 공격과 같이 정상 처리가 될때까지 요청을 계속 보낼 수 있기 때문에 꼭 최대 횟수를 지정해주어야 함
(4) ExamTest - 애스펙트 적용 및 실행
- 적용한 애스펙트를 둘다 적용하고 실행하여 로그를 확인해보면 try count=1/4에서 에러가 발생했으나 2번째 재시도에서 정상적으로 테스트가 통과된 것을 확인할 수 있음
@Slf4j
//@Import(TraceAspect.class)
@Import({TraceAspect.class, RetryAspect.class})
@SpringBootTest
class ExamTest {
// 테스트 로직 동일
}
** 참고
- 스프링이 제공하는 @Transactional은 가장 대표적인 AOP임