Board - Interceptor, AOP, Transaction
Interceptor 적용
인터셉터(Intterceptor)란?
인터셉터는 컨트롤러의 URI에 접근하는 과정에서 무언가를 제어할 필요가 있을 때 사용된다. 예를 들어, 회원제로 이루어지는 시스템이 있다고 가정했을 때, 로그인이나 계정의 권한과 관련된 처리 등을 인터셉터를 이용해서 더욱 효율적으로 처리가 가능하다.
1. LoggerInterceptor 클래스 생성
@Slf4j
public class LoggerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.debug("");
log.debug("==================== BEGIN ====================");
log.debug("Request URI ===> " + request.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("==================== END ======================");
log.debug("");
}
}
ㆍ interceptor 패키지에 LoggerInterceptor 클래스를 추가하고, 위 코드를 작성한다.
ㆍ 스프링에서 인터셉터는 HandlerInterceptor 인터페이스를 구현하는 방식으로 사용할 수 있다.
ㆍ 해당 인터페이스는 preHandle(), postHandle(), afterCompletion(), afterConcurrentHandlingStarted() 총 네 개의 메서드를 포함하고 있으며, 이번 프로젝트에서는 preHandel과 postHandle 메서드만 사용된다.
ㆍ preHandle() 메서드는 컨트롤러의 메서드에 매핑된 특정 URI를 호출했을 때 컨트롤러에 접근하기 전에 실행되는 메서드이며, postHandle() 메서드는 컨트롤러를 경유한 후 화면으로 결과를 전달하기 전에 실행되는 메서드이다.
2. 빈(Bean)으로 등록
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor())
.excludePathPatterns("/css/**", "/fonts/**", "/plugin/**", "/scripts/**");
}
}
ㆍ LoggerInterceptor 클래스를 빈으로 등록하기 위해 configuration 패키지에 MvcConfigutaion 클래스를 생성한다.
ㆍ 해당 클래스는 WebMvcConfigurer 인터페이스를 상속받고, addInterceptors() 메서드를 오버라이딩 함으로써 인터셉터 관련 클래스를 빈으로 등록할 수 있다.
3. 인터셉터 실행
ㆍ 게시글 리스트 페이지로 이동한 후 콘솔 창을 확인해보면, LoggerInterceptor 클래스의 preHandle(), postHandle() 메서드에 작성한 로그가 출력되고 있음을 확인할 수 있다.
ㆍ 이번 프로젝트에서는 로그인 처리를 구현하지 않았기 때문에, 특정 URI를 호출했을 때 로그를 출력하는 용도로 사용된다.
AOP 설정
AOP란?
AOP는 관점 지향 프로그래밍이며, 자바와 같은 객체 지향 프로그래밍을 효율적으로 사용할 수 있도록 도와주는 역할을 한다.
여러 개의 핵심 비즈니스 로직 외에 공통으로 처리되어야 하는 로그 출력, 보안 처리, 예외 처리와 같은 코드를 별도로 분리해서 하나의 단위로 묶는 모듈화의 개념으로 생각할 수 있다.
AOP에서 관점은 핵심적인 관점과 부가적인 관점으로 나눌 수 있으며, 핵심적인 관점은 핵심 비즈니스 로직을 의미하고, 부가적인 관점은 공통으로 처리되어야 하는 코드를 의미한다.
위 사진에서 각각의 화살표는 하나의 기능을 구현하기 위한 작업을 의미한다.
필수적으로 처리되어야 하는 로깅, 트랜잭션과 같은 부가적인 기능들이 각각의 기능들에 하나씩 추가된다고 하면 코드가 길어지는 것뿐만 아니라, 후에 유지보수 또한 힘들게 될 것이다. AOP는 이러한 문제를 관점이라는 개념을 통해 해결할 수 있다.
AOP에서는 부가적인 관점이 핵심 비즈니스 로직의 바깥에 포함되어 있다. AOP를 적용하면 로깅, 트랜잭션과 같은 부가적인 기능들을 핵심 비즈니스 로직에 일일이 추가하지 않아도 된다.
1. LoggerAspect 클래스 생성
@Component
@Aspect
public class LoggerAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Around("execution(* com.board..controller.*Controller.*(..)) or execution(* com.board..service.*Impl.*(..)) or execution(* com.board..mapper.*Mapper.*(..))")
public Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
String type = "";
String name = joinPoint.getSignature().getDeclaringTypeName();
if (name.contains("Controller") == true) {
type = "Controller ===> ";
} else if (name.contains("Service") == true) {
type = "ServiceImpl ===> ";
} else if (name.contains("Mapper") == true) {
type = "Mapper ===> ";
}
logger.debug(type + name + "." + joinPoint.getSignature().getName() + "()");
return joinPoint.proceed();
}
}
ㆍ aop 패키지 내에 LoggerAspect 클래스를 추가하고, 위 코드를 작성한다.
ㆍ "@Around" 애너테이션은 타겟 메서드의 호출 이전과 이후에 모두 적용이 가능하도록 설정하는 애너테이션이다.
ㆍ excution 구문을 통해서 AOP를 적용할 범위를 설정한다.
ㆍ 위 코드는 특정 클래스의 어떤 메서드가 호출되는지 로그에 출력해 주는 부가적인 역할을 하는 클래스이다.
2. AOP 실행
ㆍ 게시글 리스트 페이지로 이동한 후 콘솔 창을 확인해보면, LoggerAspect 클래스에서 정의한 로직에 알맞은 로그가 출력되고 있음을 알 수 있다.
Transaction 설정
트랜잭션(Transaction)이란?
트랜잭션은 쉽게 이야기해서 하나의 작업에 여러 개의 작업이 같이 묶여 있는 것으로 생각할 수 있다.
하나의 작업으로 이루어진 여러 개의 작업 중 어느 하나를 실패한다면, 하나의 작업 안에 포함되어 있는 모든 작업들이 취소된다. 이렇게 함으로써 데이터의 무결성을 보장할 수 있다.
트랜잭션의 기본 원칙
특성 | 설명 |
원자성(Atomicity) | 하나의 트랜잭션 내에서 실행한 작업들은 하나로 간주한다. |
일관성(Consistency) | 트랜잭션은 일관성있는 데이터베이스 상태를 유지한다. |
고립성(Isolation) | 트랜잭션은 독립적으로 처리되어야 하며, 처리되는 중간에 외부의 간섭이 없어야 한다, |
지속성(Durability) | 트랜잭션을 성공적으로 마치면 그 결과는 지속적으로 유지되어야 한다. |
1. 트랜잭션 매니저 등록
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
ㆍ configuration 패키지의 DBConfiguration 클래스에 위 코드를 추가한다.
ㆍ 추가된 메서드는 스프링에서 제공해주는 트랜잭션 매니저는 빈(Bean)으로 등록하기 위한 메서드이다.
2. TransactionAspect 클래스 생성
@Configuration
public class TransactionAspect {
@Autowired
private PlatformTransactionManager transactionManager;
private static final String EXPRESSION = "execution(* com.board..service.*Impl.*(..))";
@Bean
public TransactionInterceptor transactionAdvice() {
List<RollbackRuleAttribute> rollbackRules = Collections.singletonList(new RollbackRuleAttribute(Exception.class));
RuleBasedTransactionAttribute transactionAttribute = new RuleBasedTransactionAttribute();
transactionAttribute.setRollbackRules(rollbackRules);
transactionAttribute.setName("*");
MatchAlwaysTransactionAttributeSource attributeSource = new MatchAlwaysTransactionAttributeSource();
attributeSource.setTransactionAttribute(transactionAttribute);
return new TransactionInterceptor(transactionManager, attributeSource);
}
@Bean
public Advisor transactionAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, transactionAdvice());
}
}
ㆍ 스프링에서 트랜잭션을 설정하는 방법은 XML 설정, 애너테이션 설정, AOP 설정으로 나눌 수 있으며, 이번 프로젝트에서는 AOP 설정을 사용한다.
ㆍ aop 패키지에 TransactionAspect 클래스를 추가하고, 위 코드를 작성한다. transactionManager는 DBConfiguration 클래스에서 빈(Bean)으로 등록한 PlatformTransactionManager 객체이다.
ㆍ EXPRESSION을 통해 AOP를 적용할 범위를 설정해준다. 위 코드 같은 경우 비즈니스 로직을 수행하는 ServiceImpl 클래스의 모든 메서드를 지정해주었다.
ㆍ RollbackRuleAttribute 생성자의 인자로 Exception 클래스를 지정함으로써, 어떠한 예외가 발생하면 무조건 롤백이 수행되도록 설정해주었다.
GitHub - qlsdud0604/board: 스프링 부트를 이용한 게시판의 제작
:clipboard: 스프링 부트를 이용한 게시판의 제작. Contribute to qlsdud0604/board development by creating an account on GitHub.
github.com