Problem
솔루션 초기 설정 중에 중구난방으로 남기던 로그 체계를 통합하여 만들고자 하였다.
로그 템플릿을 만들어 놓고 그때 그때 메서드에 붙여 사용하는게 아니라
Request Method > URL 정보를 출력하고
프로세스의 흐름안에서 메서드의 정보와 파라미터를 로그에 출력하기로 하였다.
Scenario
클라이언트에서 request > [METHOD : POST | URL: /test] 형식의 log 출력
Controller > Service > Compnet 등 경로별 호출하는 메서드와 파라미터 출력 dto, entity 등은 toString 활용예정
log 라이브러리는 기본으로 제공하는 Slf4j 사용
Solution
필터를 상속 받는 AccessLogFilter 라는 커스텀 필터를 만든다.
필터는 요청을 중간에 가로채서 (스프링 시큐리티의 커스텀 필터와 같은 원리 >> 사실 필터니까 그게 그거지 뭐;;)
요청에 대한 로그를 찍는 로직을 만든다.
@Slf4j
public class AccessLogFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
String prefix = String.format("[METHOD: %s | URL: %s]", request.getMethod(), request.getRequestURI());
// 요청 정보 로그 남기기
log.info("{} API request", prefix);
filterChain.doFilter(request, servletResponse);
// 응답 정보 로그 남기기
log.info("[Response status] {}", ((HttpServletResponse) servletResponse).getStatus());
}
}
이렇게 작성한 필터를 원래 설정해놨던
jwt 필터 앞에 addFilterBefore 함수를 써서 배치를 해주면
원하는 형태의 로그가 뜬다.
Request 에 대한 로그는 적용해 놨으니 이제 메소드별로 로그를 쪼개 넣는 작업도 해야한다.
Spring 에서 실행 메서드의 이름을 중간에서 가져오는 방법은 Spring AOP 를 이용하는 방법이 보편적이다.
AOP 란
Aspect-Oriented Programming 이라는 프로그래밍 패러다임이고 핵심 비즈니스 논리와 그에 부수적인 기능을 분리하여 개발할 수 있도록 도와준다. 지금 현재 우리 상황에 딱 맞는 것이다.
주요 개념
- Aspect (관점):
- 비즈니스 로직과는 별개로 공통적으로 적용해야 할 기능을 정의하는 모듈입니다. 예를 들어 로깅, 성능 측정, 보안 점검 등을 관리.
- Join Point (조인 포인트):
- Aspect가 적용될 수 있는 지점. 예를 들어, 메서드 호출, 예외 발생, 객체 생성 등 다양한 지점에서 Aspect를 적용할 수 있다.
- Advice (어드바이스):
- Join Point에서 Aspect의 행동을 정의하는 방법. 다양한 종류의 어드바이스가 있으며, 예를 들어:
- Before: 메서드 실행 전에 실행되는 어드바이스
- After: 메서드 실행 후에 실행되는 어드바이스
- Around: 메서드 실행 전과 후에 모두 실행되는 어드바이스
- Join Point에서 Aspect의 행동을 정의하는 방법. 다양한 종류의 어드바이스가 있으며, 예를 들어:
- Pointcut (포인트컷):
- 어떤 Join Point에서 Advice를 적용할지를 규정하는 표현식입니다. 이를 통해 특정 메서드를 타겟팅할 수 있다.
- Weaving (위빙):
- Aspect와 비즈니스 로직의 결합을 의미한다. 이는 컴파일 타임, 로드 타임 또는 런타임에 발생할 수 있다.
AOP의 장점
- 모듈화:
- 비즈니스 로직과 공통 관심사를 분리하여 코드의 가독성과 유지 보수성을 높인다.
- 재사용성:
- 한 번 정의된 Aspect를 프로젝트의 여러 부분에서 재사용할 수 있으므로 중복 코드를 줄일 수 있다.
- 유연성:
- 애플리케이션의 요구 사항이 변경될 때, Aspect를 수정하여 모든 관련 기능에 적용할 수 있다.
- 시작점 통제:
- 특정 메서드나 클래스에서 공통적인 작업(예: 로깅, 보안 검사 등)을 일괄적으로 관리하고 통제하는 것이 가능하다.
각설하고 이제 프로젝트에 적용해보자.
implementation 'org.springframework.boot:spring-boot-starter-aop'
그래들에 spring boot starter aop 의존성 주입
@Aspect
Component
@Slf4j
public class MethodLogAspect {
/**
* 지정된 패키지 내의 메서드 호출을 가로채는 Around 어드바이스.
*/
@Around("execution(* com.logistics..*(..))") // 메서드 호출을 잡아내는 포인트컷.
public Object logMethodNameAndParam(ProceedingJoinPoint joinPoint) throws Throwable {
// 호출된 메서드 이름을 가져오기.
String methodName = joinPoint.getSignature().getName();
// 메서드에 전달된 인자 가져오기.
Object[] args = joinPoint.getArgs();
// 로그를 남기기 위해 메서드 파라미터 문자열 생성.
StringBuilder params = new StringBuilder();
for (Object arg : args) {
params.append(arg).append(","); // 각 인자를 추가.
}
log.info("Called Method: {} || Param: [{}]", methodName, params.toString());
// 메서드 실행하고 결과를 받아오기.
Object result = joinPoint.proceed();
// 메서드 실행 완료 로그를 남기기.
log.info("Method {} finished", methodName);
return result; // 실행된 메서드의 결과를 반환.
}
}
결과
'BE > Java' 카테고리의 다른 글
Monorepo 란? (0) | 2025.04.25 |
---|---|
Interceptor 인터셉터 / Filter 필터 / AOP 의 차이점 (0) | 2025.04.18 |
@All / No / Required ArgsConstructor 어노테이션 정리 (0) | 2025.03.20 |
yml ? properties? (0) | 2024.05.23 |
Spring Annotation - @RequiredArgsConstructor (0) | 2024.05.09 |