비즈니스 로직을 개발할 때 핵심 기능과 부가 기능이 섞여 있으면 다음과 같은 문제가 발생합니다. 예를 들어, 모든 비즈니스 메소드의 실행 시간을 측정해야 한다고 가정해보겠습니다.
public class UserService {
public void createUser(User user) {
// 시간 측정 시작
long start = System.currentTimeMillis();
// 실제 비즈니스 로직
validateUser(user);
userRepository.save(user);
sendWelcomeEmail(user);
// 시간 측정 종료 및 로깅
long end = System.currentTimeMillis();
System.out.println("실행 시간: " + (end - start) + "ms");
}
}
위와 같은 코드는 다음과 같은 문제를 발생시킵니다:
AOP를 사용하면 이러한 문제를 다음과 같이 해결할 수 있습니다:
@Aspect
@Component
public class TimeTraceAspect {
@Around("execution(* com.example.service..*(..))")
public Object trackTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
try {
// 실제 비즈니스 메소드 실행
return joinPoint.proceed();
} finally {
long end = System.currentTimeMillis();
System.out.println(joinPoint.getSignature() + " 실행 시간: " + (end - start) + "ms");
}
}
}
이렇게 AOP를 적용하면 다음과 같은 장점을 얻을 수 있습니다:
실무에서 AOP는 다음과 같은 상황에서 주로 활용됩니다:
이처럼 AOP는 애플리케이션의 부가적인 관심사를 효과적으로 모듈화하여 관리할 수 있게 해주며, 이는 코드의 품질과 유지보수성을 크게 향상시킵니다. 특히 기업의 대규모 애플리케이션에서 공통 관심사를 처리할 때 그 진가를 발휘하며, Spring 프레임워크의 핵심적인 기능 중 하나로 자리 잡고 있습니다.
@Controller
public class UserController {
@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id, Model model) {
User user = userService.getUser(id);
model.addAttribute("user", user);
return "userDetail";// View 이름 반환
}
}
@RestController
public class UserApiController {
@GetMapping("/api/users/{id}")
public UserDto getUser(@PathVariable Long id) {
User user = userService.getUser(id);
return new UserDto(user);// 객체 직접 반환
}
}
@Controller
public class ViewController {
@GetMapping("/dashboard")
public String dashboard(Model model) {
model.addAttribute("stats", statsService.getStats());
return "dashboard";// dashboard.html 뷰 반환
}
}@RestController
public class ApiController {
@PostMapping("/api/users")
public ResponseEntity<UserDto> createUser(@RequestBody UserRequest request) {
UserDto user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
}