AOP란?
AOP(Aspect Oriented Programming) 관점 중심 설계다.
예를 들어 '부가기능'과 '핵심기능' 과는 관점(Aspcet)이 다르다.
따라서 핵심기능과 부가기능을 분리해서 부가기능 중심으로 설계가 가능하게 하는 것이 AOP다
AOP를 사용해서 부가기능을 모듈화 할 수 있다.
모듈화 - 유지보수성을 높이기 위해 코드를 여러개의 모듈로 나누는 것
@Aspect
Spring Bean 클래스에만 적용 가능
클래스를 AOP화 하게 해주는 어노테이션
어드바이스 종류
- @Around: '핵심기능' 수행 전과 후 (@Begore + @After)
- @Before: '핵심기능' 호출 전
- @After: '핵심기능' 수행 성공/실패 여부와 상관없이 언제나 동작
- @AfterReturning: '핵심기능' 호출 성공 시 (함수의 Return 값 사용가능)
- @AfterThrowing: '핵심기능' 호출 실패 시. 즉 예외가 발생한 경우 동작
포인트 컷
어느 클래스에 적용할 지 정하는 것
포인트 컷 표현식은 머리에 안 들어와서... 구현할 때마다 검색해야겠음..
@Pointcut
포인트컷을 재사용 및 결합이 가능하게 해준다
메소드 위에 붙여서 사용
로깅코드
@Slf4j(topic = "로그")
@Aspect
@Component
public class LoggingAop {
@Pointcut("execution(* com.sparta.todoapp.domain.*.controller..*.*(..))")
private void allController() {
}
@Around("allController()")
public Object loggingTest(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
log.info("IP: {} | 요청 시각 = {}", getRemoteAddr(req), LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일(E) HH시 mm분 ss초 SSS (a)")));
try {
Object result = joinPoint.proceed();
return result;
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
log.info("Join log = {}", joinPoint.getSignature());
log.info("IP: {} | 응답 시각 = {}", getRemoteAddr(req), LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일(E) HH시 mm분 ss초 SSS (a)")));
log.info("걸린 시간 = {}ms", timeMs);
}
}
public static String getRemoteAddr(HttpServletRequest request) {
String ip = null;
ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-RealIP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("REMOTE_ADDR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
@Around("allController()")
public Object loggingTest(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
log.info("IP: {} | 요청 시각 = {}", getRemoteAddr(req), LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일(E) HH시 mm분 ss초 SSS (a)")));
try {
Object result = joinPoint.proceed();
return result;
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
log.info("Join log = {}", joinPoint.getSignature());
log.info("IP: {} | 응답 시각 = {}", getRemoteAddr(req), LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일(E) HH시 mm분 ss초 SSS (a)")));
log.info("걸린 시간 = {}ms", timeMs);
}
}
System.currenTimeMillis() - 1970년도 부터 현재 시각까지 계산 한것을 ms로 반환하는 메소드
LocalDateTime.now - 현재 시각 표시
DateTimeFormatter.ofPattern - 내가 원하는 포맷 패턴대로 시간을 표시
joinPoint.getSignature() - 접근한 클래스 경로 및 메소드 이름 출력
public static String getRemoteAddr(HttpServletRequest request) {
String ip = null;
ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-RealIP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("REMOTE_ADDR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
requset Header에서 X-Forwarded-For 에 적혀 있는 IP를 ip 문자열에 저장
X-Forwarded-For에서 IP를 못뽑아왔으면 다른 곳에 담겨져 있는 ip를 찾아서 반환한다
위 부분은 구글링해서 찾은 것..ㅎㅎ IP 뽑는 거 어렵다 어려워
이때는 getSignature.toShortString으로 작성하고 실행해서 Join log 부분이 짧게 출력됐다!
'TIL' 카테고리의 다른 글
TIL 2023-12-15 QueryDSL 기본 사용법 (0) | 2023.12.15 |
---|---|
TIL 2023-12-14 정적쿼리 동적쿼리 그리고 QueryDSL 약간 (0) | 2023.12.14 |
TIL 2023-12-12 왜 Service를 인터페이스와 구현체로 나눌까? (0) | 2023.12.12 |
TIL 2023-12-11 명시적 삭제 컬럼 (0) | 2023.12.11 |
TIL 2023-12-08 getWriter() has already been called for this response (0) | 2023.12.08 |