Lv4 과제는 나에게 있어 단순한 기능 구현 과제가 아니라 문제도 이해하기 어려웠다.
현재 과제의 모든 코드를 다 이해하고나서 새로운 기능을 추가하는 것인데 기존 코드가 어떻게 돌아가는지부터 확인하는 것이 너무 어려웠다.
여러가지 부분에서 의문점이 많았다. 일단 내가 애매하게 모르는 단어들 모두를 명확하게 정리하기! 가
나의 제일 먼저 해야할 목표였다.
그 이후에는 찾아보며 코드를 구현하기로 했다.
내가 가장 결정한 것은 위에서처럼 모든 단어들이나 모르는 로직을 검색하기였다.
찾아보고 메모하고 싶은 것은 블로그에 입력하고!
https://velog.io/@todok0317/세션Session과-JWT의-차이점
https://velog.io/@todok0317/AOP-Around-어노테이션
https://velog.io/@todok0317/Filter-필터-vs-Interceptor-인터셉터
하나씩 정리도 하고, 모르는것을 물어보기도 하고, 다는 이해못했지만 대략적인 느낌은 알아갔다.
일단 나는 JWT는 알고있었지만 Session을 대게 사용했었다.
그렇다보니 확실하게 이해하고 넘어가야겠다고 생각했기에 검색하고 확인하고 튜터님께 물어보았다.
userId를 가져오는 방식도 다르기에 비교를 하기도 하였다.
그 다음은 @Around가 뭐지? 였기에 검색을하고 어떻게 흐름이 되는지 확인하는 과정을 가졌다.
어려운 개념은 아니기에 잘 이해는 했으나 내가 잘 사용할지는 아직은 모르겠다.
그 다음은 필터와 인터셉터의 차이를 명확하게 알았다. 구글링을 통해서 한 웹사이트에 너무 잘 정리해주신 글을 보고 따라 적으면서 이해했다. 아직은 한번에 머리속에 이해는 못했지만 그래도 어느정도 감은 익혔달까?ㅎㅎ
이렇게 하나씩 정리하였다. 아직 더 적어야할게 많지만 일단 이정도는 정리해놓았다.
이렇게 어떻게 적어야할지 어느정도 파악한 후에 구글링과 나의 GPT와 이야기를 하면서 코드를 구현해보았다.
일단 이 코드
@Aspect // AOP에서 "공통 기능"을 가진 클래스라는 의미
@Component // 스프링이 자동으로 이 클래스를 Bean으로 등록해줌
public class AdminApiLoggingAspect {
private static final Logger logger = LoggerFactory.getLogger(AdminApiLoggingAspect.class);
// @Around : 해당 메서드 실행 전후에 끼어들어 실행되는 AOP 어드바이스
// exection(..) : AOP가 적용될 메서드 명시 (여기서는 deleteComment(), changeUserRole())
// 즉, 이 두 메서드에 들어오는 요청과 나가는 응답을 로깅하겠다는 뜻
@Around("execution(* org.example.expert.domain.comment.controller.CommentAdminController.deleteComment(..)) || " +
"execution(* org.example.expert.domain.user.controller.UserAdminController.changeUserRole(..))")
public Object logAdminApi(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
Long userId = (Long) request.getAttribute("userId");
String email = (String) request.getAttribute("email");
UserRole userRole = UserRole.of((String) request.getAttribute("userRole"));
String url = request.getRequestURI();
String method = request.getMethod();
LocalDateTime requestTime = LocalDateTime.now();
// joinPoint는 지금 AOP가 적용된 대상 메서드를 나타내는 객체
// .getArgs()는 그 메서드에 들어온 매개변수들을 배열로 가져와
// instanceof 연산자 : 객체가 어떤 클래스인지 어떤 클래스를 상속받았는지 확인하는데 사용하는 연산자.
Object[] args = joinPoint.getArgs();
String requestBody = Arrays.stream(args)
// HttpServletRequest, HttpServletResponse 타입은 직렬화(문자열로 바꾸기) 가 안 되기 때문에, 이런 타입은 제외하고 필터링 해줌
// 즉, 우리가 요청에서 진짜 사용자 정보(DTO 등)만 골라서 직렬화 하는 것임.
.filter(arg -> !(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse))
// 각 파라미터 arg를 문자열(JSON) 으로 바꾸기 위해 ObjectMapper를 사용함.
// 예 : UserDto → {"id":1, "name":"홍길동"} 이런 식으로 바뀜.
.map(arg -> {
try {
return new ObjectMapper().writeValueAsString(arg);
} catch (JsonProcessingException e) {
return "직렬화 실패";
}
})
// JSON으로 바뀐 파라미터들을 콤마(,)로 이어 붙여서 문자열로 만드는 과정
// 예: {"id":1}, {"name":"홍길동"}
.collect(Collectors.joining(", "));
logger.info("=== [Admin API 요청] ===");
logger.info("요청자 ID: {}", userId);
logger.info("요청 URL: {} {}", method, url);
logger.info("요청 시각: {}", requestTime);
logger.info("요청 본문: {}", requestBody);
Object result = joinPoint.proceed();
String responseBody = new ObjectMapper().writeValueAsString(result);
logger.info("응답 본문: {}", responseBody);
logger.info("========================");
return result;
}
}
참조 : https://qkrqkrrlrl.tistory.com/175 이것과 GPT와 함께 코드를 구현했다.
내 스스로는 못구현해요..ㅜㅜ 이건 너무 어려워요ㅜㅜㅜㅜ
나의 몫은 이걸 보고 완벽하게 이해하는 것이 나의 해결과정이였다.
한 줄씩 천천히 뜯어보며 주석을 달고, 직접 출력도 해보면서
이 코드가 어떤 요청 데이터를 로그로 남기려는 AOP용 코드라는 걸 알게 되었다.
사실 Lv4는 해결해서 과제한 것이 아니라 그냥 공부하는 파트가 되버렸다 ㅎㅎ
진짜 이건 했다고 보기 어렵기때문에 과제를 제출할까말까 하다가 그래도 제출하고 양심껏
여기에도 적고 제출함에 적기로도 했다.
나는 직접 손으로 적어가면서 이해해야지 잘 이해되기에 그래도 다는 못하더라도 조금은 이해하고 어느정도 감은 왔다정도?ㅋㅋㅋㅋ
다음에는 스스로 구현해바야지!!!!