회사 업무 중 스프링 레거시 프로젝트를 스프링 부트로 변환하는 업무가 있었다. 업무 내용중 xml로 관리되는 빈들을 자바클래스로 변환하는 업무도 있었는데 이 때, aop:config 로 선언된 문구를 변환하면서 어려움을 겪었다. 빈은 클래스를 생성하고 @Bean 어노테이션을 통해 등록했는데 aop는 뭔지 하나도 아는게 없었기 때문이다. 그래서 이번에 aop에 관한 공부를 하고 스프링 부트에서는 어떻게 적용가능한지 포스팅해보려 한다.
GitHub - N1ghtsky0/playground: 개인적으로 흥미가 생긴 기술들을 구현하면서 생각을 정리한 놀이터입니
개인적으로 흥미가 생긴 기술들을 구현하면서 생각을 정리한 놀이터입니다. Contribute to N1ghtsky0/playground development by creating an account on GitHub.
github.com
- 스프링부트 3.2.1
- 자바 17
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'org.springframework.boot:spring-boot-starter-aop'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
프로젝트는 단순하게 구현할 예정이다.
컨트롤러가 호출될 때마다 어떤 path가 호출되었는지를 로깅하는 기능을 구현할 것이다.
특별한 기능을 만들지 않을거기 때문에 그냥 A서비스, B서비스와 같이 호출되는 서비들과 컨트롤러를 생성해주었다.
// controller
import com.jiwook.playground.service.AService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
@RequiredArgsConstructor
public class AController {
private final AService aService;
@GetMapping("/a")
public String a() {
aService.serviceMethod();
return "index";
}
}
// service
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class AService {
public void serviceMethod() {
log.info("A serviceMethod");
}
}
AOP를 이용한 로깅은 컨트롤러에만 적용할 것이기에 서비스 호출 로깅은 직접 매서드 내에 작성해주었다.
// aspect
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@Aspect
public class LoggingAspect {
private final HttpServletRequest request;
public LoggingAspect(HttpServletRequest request) {
this.request = request;
}
@Before("execution(* com.jiwook.playground.controller.*.*(..))")
public void beforeController() {
log.info("Before controller");
log.info(request.getRequestURI());
}
@After("within(com.jiwook.playground.controller.*)")
public void afterController() {
log.info("After controller");
}
}
<!-- index -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>메인 페이지</h2>
<a href="/a">A 서비스 호출하기</a><br />
<a href="/b">B 서비스 호출하기</a>
</body>
</html>
프로젝트 실행 후 http://localhost:8080/a 로 접속하면
와 같이 컨트롤러가 호출된 순간 Aspect가 동작하여 로그가 남은것을 확인할 수 있다.
사실 단순한 로그기록을 위해서라면 CommonController 클래스를 만든 뒤 모든 컨트롤러에 상속해서 컨트롤러가 호출 될때마다 로그를 남기는 방법도 있다. 하지만 이 경우 새로운 컨트롤러를 만들었을 때 개발자의 실수로 상속하는 것을 잊어버린다면 로그가 작성되지 않을 수 있다.
이번에는 예시 때문에 로깅 관련 기능을 구현했지만, 추적하는 대상을 어드민 기능 관련 컨트롤러로 축소하여 접속 기록을 DB에 저장하고 관리하는 등의 기능을 구현할 때 유용할 것 같다.
'~2025' 카테고리의 다른 글
@RequiredArgsConstructor 사용과 관련된 궁금증 (0) | 2024.08.07 |
---|---|
백준) 10844 - 쉬운 계단 수 [파이썬] (0) | 2024.07.05 |
git 디렉토리별 계정 설정하기 (0) | 2024.04.30 |
SpringBoot 3 + JWT(jjwt 0.12.5) + Spring security (0) | 2024.03.08 |
스프링부트 다중서버 세션관리 구현해보기(spring security + redis) (0) | 2024.03.04 |