Interceptor와 AOP 개념 정리 및 API 로깅 예시 ✍️

2025. 2. 26. 14:00·TIL (Today I Learned)

이미지 출처: https://jake-seo-dev.tistory.com/83

 

📌 Interceptor

Spring의 `HandlerInterceptor`를 활용하여 API 요청 전에 권한을 검사하거나 로그를 남길 때 사용

`HandlerInterceptor`를 구현하여 요청(Request)과 응답(Response)을 가로채는 방식

  • 서버에 들어온 Request 객체를 컨트롤러의 핸들러로 도달하기 전에 낚아채서 부가적인 기능이 실행되게끔 만들어준다.

주로 인증/인가 체크 또는 요청 데이터를 사전 처리하는 용도로 활용

  • 로그인 체크, 권한 체크, 프로그램 실행시간 작업, 로그확인 등

 

특징

  • 요청과 응답을 직접 가로채고 로깅 가능
  • `WebMvcConfigurer`를 구현한 Config 클래스에 등록하여 사용
  • 특정 URL 패턴에 대해서만 로깅을 적용할 수 있음
  • Controller 진입 전과 후를 제어하기에 적합
  • Service 계층 내부의 동작은 감지할 수 없음

 

주요 메서드

  • `preHandle()`: 요청이 Controller에 도달하기 전 실행됨
  • `postHandle()`: 요청이 Controller에서 실행된 후, View 렌더링 전 실행됨
  • `afterCompletion()`: 요청 처리가 완료된 후 실행됨

 

Interceptor로 Logging 예시

@Component
public class LoggingInterceptor implements HandlerInterceptor {

    private static final Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        UserRole userRole = UserRole.valueOf((String) request.getAttribute("userRole"));

        if (!UserRole.ADMIN.equals(userRole)) {
            log.warn("{} 관리자 권한 없음 - Method: {}, URL: {}", LocalDateTime.now(), request.getMethod(), request.getRequestURI());
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "관리자 권한이 없습니다.");
            return false;
        }

        log.info("{} admin 접근 - Method: {}, URL: {}", LocalDateTime.now(), request.getMethod(), request.getRequestURI());
        return true;
    }
}

📌 AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)

`@Aspect`와 `@Around` 등을 활용하여 비지니스 로직과 별개로 공통 기능(로깅, 트랜잭션 관리 등)을 적용할 때 사용

API 실행 전후의 데이터를 수집하여 세부적인 로깅을 구현할 때 유용

메서드 단위의 공통 로직 로깅 시 편리

 

특징

  • 모든 계층에서 로깅 가능 (Service 계층에서도 로깅 가능)
  • 코드 변경 없이 로깅 적용 가능
  • Interceptor보다 요청과 응답의 직접적인 로깅에는 적합하지 않음

 

주요 어노테이션

  • `@Aspect`: AOP 기능을 가진 클래스임을 명시
  • `@Around("execution(패키지.클래스.메서드)")`: 특정 메서드 실행 전후로 코드 실행
  • `@Before`: 메서드 실행 전 동작
  • `@AfterReturning`: 메서드 실행이 성공적으로 종료된 후 동작

 

 AOP로 Logging 예시

@Aspect
@Component
public class ApiLoggingAspect {

    private static final Logger log = LoggerFactory.getLogger(ApiLoggingAspect.class);
    private final HttpServletRequest request;
    private final ObjectMapper objectMapper;

    public ApiLoggingAspect(HttpServletRequest request, ObjectMapper objectMapper) {
        this.request = request;
        this.objectMapper = objectMapper;
    }

    @Pointcut("execution(* org.example.expert.domain.comment.controller.CommentAdminController.deleteComment(..)) " +
            "|| execution(* org.example.expert.domain.user.controller.UserAdminController.changeUserRole(..))")
    private void cut(){}

    @Around("cut()")
    public Object logAdminApi(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Method method = getMethod(proceedingJoinPoint);
        String requestUrl = request.getRequestURI();
        String requestMethod = request.getMethod();
        Long userId = (Long) request.getAttribute("userId");

        log.info("======= [Admin API Request] =======");
        log.info("Request User ID: {}", userId);
        log.info("Request Time: {}", LocalDateTime.now());
        log.info("Request URL: {}", requestUrl);
        log.info("Request Method: {}", requestMethod);
        log.info("Method Name: {}", method.getName());

        Object[] args = proceedingJoinPoint.getArgs();
        if (args.length == 0) {
            log.info("Request Body: no parameter");
        } else {
            Arrays.stream(args).forEach(arg -> {
                log.info("Request Parameter Type: {}", arg.getClass().getSimpleName());
                log.info("Request Parameter: {}", convertToJson(arg));
            });
        }

        // proceed()를 호출하여 실제 메서드 실행
        Object returnObj = proceedingJoinPoint.proceed();

        log.info("======= [Admin API Response] =======");
        if (returnObj != null) {
            log.info("Response Type: {}", returnObj.getClass().getSimpleName());

            if (returnObj instanceof ResponseEntity) {
                ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnObj;
                log.info("Response Status: {}", responseEntity.getStatusCode());
                log.info("Response Body: {}", convertToJson(responseEntity.getBody()));
            } else {
                log.info("Response Value: {}", convertToJson(returnObj));
            }
        } else {
            log.info("Response Value: null (void method)");
        }

        return returnObj;
    }

    private Method getMethod(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        return signature.getMethod();
    }

    private String convertToJson(Object object) {
        if (object == null) return "null";
        try {
            return objectMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.warn("Json 형식으로 변환 실패: {}", e.getMessage());
            return "Json 형식으로 변환 실패";
        }
    }
}

참고: https://velog.io/@jjeongdong/Spring-AOP%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-Logging-%EA%B5%AC%ED%98%84

저작자표시 비영리 변경금지 (새창열림)

'TIL (Today I Learned)' 카테고리의 다른 글

SPRING ADVANCED 과제 | ‘내’가 정의한 문제와 해결 과정 기록 📜  (0) 2025.02.27
Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑  (0) 2025.02.27
Spring JWT 인증 과정 뜯어보기 🔑 | Spring Security 없이 구현하는 방법  (0) 2025.02.25
git clone 후 build.gradle 빨간 줄❓ 해결 방법‼️  (0) 2025.02.24
[Spring] 인증/인가와 Session 방식과 JWT 방식의 차이  (0) 2025.02.20
'TIL (Today I Learned)' 카테고리의 다른 글
  • SPRING ADVANCED 과제 | ‘내’가 정의한 문제와 해결 과정 기록 📜
  • Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑
  • Spring JWT 인증 과정 뜯어보기 🔑 | Spring Security 없이 구현하는 방법
  • git clone 후 build.gradle 빨간 줄❓ 해결 방법‼️
기만나🐸
기만나🐸
공부한 내용을 기록합시다 🔥🔥🔥
  • 기만나🐸
    기만나의 공부 기록 🤓
    기만나🐸
  • 전체
    오늘
    어제
    • ALL (147)
      • TIL (Today I Learned) (56)
      • Dev Projects (15)
      • Algorithm Solving (67)
        • Java (52)
        • SQL (15)
      • Certifications (8)
        • 정보처리기사 실기 (8)
  • 인기 글

  • 태그

    jpa
    백준
    BOJ
    javascript
    GROUP BY
    백트래킹
    jwt
    DFS
    자료구조
    완전탐색
    greedy
    mysql
    dp
    websocket
    Google Fonts
    프로그래머스
    jQuery
    그리디
    programmers
    BFS
    bootstrap
    CSS
    HTML
    join
    다이나믹프로그래밍
    sql
    Subquery
    Firebase
    java
    시뮬레이션
  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.3
기만나🐸
Interceptor와 AOP 개념 정리 및 API 로깅 예시 ✍️
상단으로

티스토리툴바