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 |
Tags
- mysql
- error
- 티스토리챌린지
- github
- pandas
- mssql
- 책
- oracle
- DBMS
- java
- Linux
- 자바
- PostgreSQL
- springboot
- 인터페이스
- 명령어
- 후기
- 리눅스
- docker
- SQL
- 인덱스
- 독서
- IntelliJ
- 네트워크
- Javascript
- spring
- git
- Python
- MariaDB
- 오블완
Archives
- Today
- Total
hanker
[SPRING] 웹 서비스에서 발생하는 이벤트를 DB에 기록하기 (AOP) 본문
반응형
Spring AOP를 사용하여 웹 서비스에서 이벤트가 발생할 때 DB에 기록하는 로직을 만들어보자.
1. 설정
우선 AOP를 사용하기 위해 의존성을 추가해줘야 한다.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
application.properties
spring.aop.auto=true
spring.aop.proxy-target-class=true
spring.aop.auto
- true: @EnableAspectJAutoProxy를 자동으로 활성화
- false: AOP 자동 설정 비활성화, 수동으로 설정해야 함
- 기본값: true (Spring Boot에서는 보통 생략 가능)
spring.aop.proxy-target-class
Spring AOP가 객체의 프록시를 만드는 방식을 설정
- true : CGLIB 프록시
- false : JDK 동적 프록시
2. AOP Aspect 클래스 정의
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.HashMap;
import java.util.Map;
@Aspect
@Component
@RequiredArgsConstructor
public class EventAspect {
private final ObjectMapper objectMapper;
// @LogEvent 어노테이션이 있는 메서드에 대해 적용
@Around("@annotation(logEvent)")
public Object logAnnotatedMethods(ProceedingJoinPoint joinPoint, LogEvent logEvent) throws Throwable {
return executeWithLogging(joinPoint, logEvent.eventType());
}
private Object executeWithLogging(ProceedingJoinPoint joinPoint, String eventType) throws Throwable {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
long startTime = System.currentTimeMillis();
// 메서드 정보 추출
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getSimpleName();
// 이벤트 로그를 담을 Map 생성
// Entity 클래스에 담아서 전송해도 무관
Map<String, Object> map = new HashMap<>();
map.put("eventType", eventType);
map.put("methodName", methodName);
map.put("className", className);
try {
// 파라미터 정보 추출
Object[] args = joinPoint.getArgs();
if (args.length > 0) {
map.put("parameters", objectMapper.writeValueAsString(args));
}
// IP 주소 추출
map.put("ipAddress", request.getRemoteAddr());
// 메서드 실행
Object result = joinPoint.proceed();
// 실행 시간 계산
long executionTime = System.currentTimeMillis() - startTime;
map.put("executionTime", executionTime);
// 로그 저장() Repository에 맵을 담아서 전달해준다
// aopRepository.saveEventLog(map);
System.out.println("map = " + map);
System.out.println("저장되었습니다.");
return result;
} catch (Exception e) {
throw e;
}
}
}
3. Custom 어노테이션 정의
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogEvent {
String eventType() default "DEFAULT";
boolean includeParameters() default true;
boolean includeResponse() default false;
}
4. Sample RestController 정의
@RestController
@RequestMapping("/api/users")
public class UserController {
private List<User> users = new ArrayList<>(List.of(
new User(1, "hanker", "hanker@example.com"),
new User(2, "Hong gil dong", "gildong@example.com")
));
// GET: 특정 사용자 조회
@GetMapping("/{id}")
@LogEvent(eventType = "GET_USER_BY_ID", includeParameters = true, includeResponse = true)
public User getUserById(@PathVariable int id) {
return users.stream()
.filter(user -> user.getId() == id)
.findFirst()
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
실행
반응형
'SPRING' 카테고리의 다른 글
[Spring] Gradle .war 와 -plain.war의 차이 (2) | 2025.06.11 |
---|---|
Redis - Spring Boot + Redis를 사용하여 실시간 검색어 순위 만들기 (3) 화면에 데이터 넘겨주기 (0) | 2025.04.08 |
Spring boot - 스프링 부트 프로젝트 jar파일 생성 방법 (0) | 2025.04.07 |
Spring boot - thymeleaf layout 사용 시 설정 (layout 적용 안될 때) (0) | 2025.04.02 |
iBatis - isEmpty, isNotEmpty / isNull, isNotNull 뭘 써야 할까? (빈 값 / null 체크) (0) | 2025.03.18 |