이전에도 포스팅을 짧게 했었습니다.
https://chobi-meow.tistory.com/12
위에서는 너무 간단하게 방식정도와 간단한 어노테이션 개념정도만 정리해서 좀 더 정리가 필요할듯 하여
한번 더 포스팅하려고 합니다.
로그를 보통 logback.xml 로 서버로그에 담기도록 하는게 보통이지만
DB에 저장하는 방식을 정리해보려고 합니다.
@Entity
@Getter
@Builder
@DynamicInsert
@DynamicUpdate
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Table(name = "user_action_log")
@AttributeOverride(name = "id", column = @Column(name = "id"))
public class UserActionLog extends BaseEntity {
@JoinColumn(name = "user_id", foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
@Comment("아이디")
private Long id;
@Column(name = "user_nm")
@Comment("사용자 이름")
private String user_nm;
@Column(name = "user_ip")
@Comment("로그인한 사용자 아이피")
private String adminIp;
@Column(name = "request_param", length = 5000)
@Comment("요청파라미터")
private String requestParam;
@Column(name = "request_url")
@Comment("요청 URL")
private String requestUrl;
@Column(name = "response_data", columnDefinition = "TEXT")
@Comment("리턴 데이터")
private String responseData;
}
먼저 엔티티를 작성해줍니다.
나는 JPA와 Sping boot를 사용하다보니 위와 같은 형식으로 엔티티를 생성해줍니다.
JpaRepository도 생성해주고
save 명령어만 쓸예정이라 쿼리문은 따로 써주지 않았습니다.
@Aspect
@Component
public class LoggingAspect {
private final UserActionLogRepository userActionLogRepository;
private final UserRepository userRepository;
public LoggingAspect(UserRepository userRepository, UserActionLogRepository userActionLogRepository) {
this.userRepository = userRepository;
this.userActionLogRepository = userActionLogRepository;
}
@Pointcut("execution(* com.sample.test.*.controller.*.*(..))")
public void controller() {
}
LoggingAspect 라는 클래스를 만들어주고
@Aspect 어노테이션을 붙여 포인트컷(Pointcut)과 어드바이스(Advice)로 구성된 어드바이저(Advisor)의 생성을 편리하게 해주는 기능을 가진 어노테이션입니다. 실제 실무에서는 직접 어드바이저를 생성하는 것이 아닌 @Aspect 어노테이션을 이용하여 AOP를 적용하게 됩니다.
이후 사용할 Reposiory의 생성자를 만들어주었습니다.
그리고 @Pointcut이라는 표현식을 사용하여 프로젝트 내에 있는 모든 컨트롤러 경로를 지정해줍니다.
@AfterReturning(pointcut = "controller()", returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) throws JsonProcessingException {
//url 정보
StringBuffer url = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getRequestURL();
//String 내에서 20621 이후의 문자열을 추출
String urlStr = url.substring(url.indexOf("20621") + 5);
JLog.logd(String.format("url = %s", urlStr));
//IP 정보
String ip = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getRemoteAddr();
JLog.logd(String.format("ip = %s", ip));
//파라미터 정보
String params = Arrays.toString(joinPoint.getArgs());
JLog.logd(String.format("params = %s", params));
Long id = null;
String userNm = null;
ObjectMapper objectMapper = new ObjectMapper();
String jsonResult = null;
//로그인한 사용자 정보
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated() && authentication.getPrincipal() instanceof UserDetails userDetails) {
JLog.logd(String.format("userDetails = %s", userDetails.getUsername()));
User user = userRepository.findExistUser(userDetails.getUsername()).orElse(null);
id = user == null ? 0 : user.getId();
JLog.logd(String.format("id = %s", id));
userNm = user == null ? "" : user.getUserNm();
JLog.logd(String.format("userNm = %s", userNm));
jsonResult = objectMapper.writeValueAsString(result);
} else {
JLog.logd("사용자 정보를 가져올 수 없습니다.");
// 사용자 정보가 없을 때 수행할 작업을 여기에 추가
jsonResult = objectMapper.writeValueAsString(result);
}
JLog.logd(String.format("response = %s", jsonResult));
//Log 저장
UserActionLog userActionLog = new UserActionLog(id, userNm, ip, params, urlStr, jsonResult);
userActionLogRepository.save(userActionLog);
}
}
@AfterReturning 이 실행되는 시점은 대상 메서드가 정상적으로 종료한 후입니다.
상황에 따라 @Before @AfterThrowing @Around 등 사용할 수 있는데 정상적으로 메서드가 동작 한 후 데이터를 다루기 위한 부분이다보니 AfterReturning을 사용하였습니다.
returning = "result" 를 사용하게되면 리턴되는 값을 가져올수 있습니다.
처음에 returning 부분을 사용하지 않았더니.. 아무리 디버깅해봐도 response_data 부분이 안나오더군요..ㅜㅜ
이후 불러와진 값을 저장합니다.
'IT 개발 > Spring' 카테고리의 다른 글
@Scheduled Open API사용하여 공휴일 DB 적재하기 (1) | 2023.12.05 |
---|---|
AOP를 활용하여 로그 기록하기 (0) | 2023.12.01 |
Spring boot 에서 XSS Filter 적용하기 (2) | 2023.12.01 |
WebConfig에 설정된 경로 중 일부 API 예외처리 (2) | 2023.12.01 |